File size: 9,928 Bytes
25e074b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#!/usr/bin/env python3
"""
Comprehensive test script for Interactive Image Mosaic Generator
Tests multiple parameter combinations and saves results with performance metrics
"""

import os
import json
import csv
from pathlib import Path
from itertools import product
import tempfile
from tqdm import tqdm
import pandas as pd

from simple_mosaic import SimpleMosaicImage
from tile_library import build_cifar10_tile_library, build_cifar100_tile_library
from performance import calculate_all_metrics
from PIL import Image

# Test parameters
TEST_PARAMS = {
    'min_size': [4, 16, 32],
    'start_size': [32, 64, 128],  # max size
    'threshold': [0.1, 5.0],      # subdivision threshold
    'color_quantization': [0, 16, 32],
    'tile_library': ['none', 'cifar-10', 'cifar-100']
}

# Input and output directories
SAMPLES_DIR = Path('./samples')
OUTPUT_DIR = Path('./output/test_result')
MOSAIC_DIR = OUTPUT_DIR / 'mosaic_images'
METRICS_DIR = OUTPUT_DIR / 'metrics'

# Tile cache to avoid reloading
tile_cache = {}

def setup_directories():
    """Create necessary output directories"""
    for dir_path in [OUTPUT_DIR, MOSAIC_DIR, METRICS_DIR]:
        dir_path.mkdir(parents=True, exist_ok=True)
    print(f"[INFO] Created output directories under {OUTPUT_DIR}")

def get_tile_library(tile_type, max_per_class=500):
    """Get cached tile library or create new one"""
    if tile_type == 'none':
        return None, None, None

    cache_key = f"{tile_type}_{max_per_class}"

    if cache_key not in tile_cache:
        print(f"[INFO] Loading {tile_type} tile library...")
        if tile_type == "cifar-10":
            tiles, means, labels = build_cifar10_tile_library(max_per_class=max_per_class)
        elif tile_type == "cifar-100":
            tiles, means, labels = build_cifar100_tile_library(max_per_class=max_per_class)
        else:
            return None, None, None

        tile_cache[cache_key] = (tiles, means, labels)
        print(f"[INFO] Cached {len(tiles)} tiles for {tile_type}")

    return tile_cache[cache_key]

def generate_filename(image_name, min_size, start_size, threshold, color_quantization, tile_library):
    """Generate standardized filename for results"""
    # Clean image name (remove extension)
    base_name = Path(image_name).stem

    # Format threshold to avoid decimal issues
    threshold_str = f"{threshold:g}".replace('.', 'p')

    # Create filename
    filename = f"{base_name}-min{min_size}-max{start_size}-sub{threshold_str}-quant{color_quantization}-{tile_library}"

    return filename

def process_single_combination(image_path, params, progress_bar=None):
    """Process a single parameter combination and return results"""
    image_name = image_path.name
    min_size, start_size, threshold, color_quantization, tile_library = params

    try:
        # Load original image
        original_image = Image.open(image_path).convert("RGB")

        # Create temporary file for processing
        with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp_file:
            original_image.save(tmp_file.name)

            # Load and process image
            loader = SimpleMosaicImage(tmp_file.name)

            # Apply resize if needed (use existing MAX_IMAGE_SIZE logic)
            MAX_IMAGE_SIZE = 4500
            if max(loader.width, loader.height) > MAX_IMAGE_SIZE:
                loader.resize(MAX_IMAGE_SIZE)

            # Apply color quantization if requested
            if color_quantization > 0:
                loader.quantize_colors(color_quantization)

            # Smart boundary handling
            loader.crop_to_grid(2)

            # Build adaptive cells
            cells = loader.build_adaptive_cells(
                start_size=start_size,
                min_size=min_size,
                threshold=threshold
            )

            # Generate mosaic based on tile type
            if tile_library == "none":
                result_img = loader.mosaic_average_color_adaptive(cells)
                processing_info = f"Average colors with {len(cells)} adaptive cells"
            else:
                tiles, tile_means, _ = get_tile_library(tile_library, max_per_class=500)
                if tiles is None:
                    raise ValueError(f"Failed to load {tile_library} tile library")

                result_img = loader.mosaic_with_tiles_adaptive(cells, tiles, tile_means)
                processing_info = f"{tile_library} with {len(cells)} cells using {len(tiles)} tiles"

            # Calculate metrics
            metrics = calculate_all_metrics(original_image, result_img)

            # Generate filenames
            base_filename = generate_filename(
                image_name, min_size, start_size, threshold, color_quantization, tile_library
            )

            # Save mosaic image
            mosaic_path = MOSAIC_DIR / f"{base_filename}.jpg"
            result_img.save(mosaic_path, quality=95)

            # Save metrics
            metrics_path = METRICS_DIR / f"{base_filename}_metrics.json"
            with open(metrics_path, 'w') as f:
                json.dump(metrics, f, indent=2)

            # Clean up temporary file
            os.unlink(tmp_file.name)

            # Update progress bar
            if progress_bar:
                progress_bar.set_postfix({
                    'image': image_name[:15],
                    'params': f"min{min_size}_max{start_size}_sub{threshold:g}_q{color_quantization}_{tile_library[:6]}"
                })
                progress_bar.update(1)

            return {
                'image_name': image_name,
                'min_size': min_size,
                'start_size': start_size,
                'threshold': threshold,
                'color_quantization': color_quantization,
                'tile_library': tile_library,
                'num_cells': len(cells),
                'processing_info': processing_info,
                'mosaic_path': str(mosaic_path),
                'metrics_path': str(metrics_path),
                'success': True,
                **metrics
            }

    except Exception as e:
        error_msg = f"Error processing {image_name} with params {params}: {str(e)}"
        print(f"[ERROR] {error_msg}")

        if progress_bar:
            progress_bar.update(1)

        return {
            'image_name': image_name,
            'min_size': min_size,
            'start_size': start_size,
            'threshold': threshold,
            'color_quantization': color_quantization,
            'tile_library': tile_library,
            'success': False,
            'error': str(e),
            'MSE': float('nan'),
            'SSIM': float('nan'),
            'Histogram_Correlation': float('nan'),
            'Edge_Similarity': float('nan'),
            'Overall_Quality': float('nan')
        }

def run_comprehensive_test():
    """Run comprehensive test across all parameter combinations"""
    setup_directories()

    # Find all image files in samples directory
    image_files = []
    for ext in ['*.jpg', '*.jpeg', '*.JPG', '*.JPEG', '*.png', '*.PNG']:
        image_files.extend(list(SAMPLES_DIR.glob(ext)))

    if not image_files:
        print(f"[ERROR] No image files found in {SAMPLES_DIR}")
        return

    print(f"[INFO] Found {len(image_files)} images to process")
    print(f"[INFO] Images: {[f.name for f in image_files]}")

    # Generate all parameter combinations
    param_combinations = list(product(
        TEST_PARAMS['min_size'],
        TEST_PARAMS['start_size'],
        TEST_PARAMS['threshold'],
        TEST_PARAMS['color_quantization'],
        TEST_PARAMS['tile_library']
    ))

    # Filter out invalid combinations (min_size >= start_size)
    valid_combinations = [
        combo for combo in param_combinations
        if combo[0] < combo[1]  # min_size < start_size
    ]

    total_tests = len(image_files) * len(valid_combinations)
    print(f"[INFO] Total test combinations: {total_tests}")
    print(f"[INFO] Parameter combinations per image: {len(valid_combinations)}")

    # Results storage
    all_results = []

    # Create progress bar
    with tqdm(total=total_tests, desc="Processing mosaics", unit="test") as pbar:
        for image_path in image_files:
            print(f"\n[INFO] Processing image: {image_path.name}")

            for params in valid_combinations:
                result = process_single_combination(image_path, params, pbar)
                all_results.append(result)

    # Save comprehensive results to CSV
    summary_path = OUTPUT_DIR / 'summary.csv'
    df = pd.DataFrame(all_results)
    df.to_csv(summary_path, index=False)

    # Print summary statistics
    successful_tests = df[df['success'] == True]
    failed_tests = df[df['success'] == False]

    print(f"\n[SUMMARY]")
    print(f"Total tests run: {len(all_results)}")
    print(f"Successful: {len(successful_tests)}")
    print(f"Failed: {len(failed_tests)}")

    if len(successful_tests) > 0:
        print(f"\nPerformance Statistics (successful tests):")
        print(f"Average MSE: {successful_tests['MSE'].mean():.2f}")
        print(f"Average SSIM: {successful_tests['SSIM'].mean():.4f}")
        print(f"Average Histogram Correlation: {successful_tests['Histogram_Correlation'].mean():.4f}")
        print(f"Average Edge Similarity: {successful_tests['Edge_Similarity'].mean():.4f}")
        print(f"Average Overall Quality: {successful_tests['Overall_Quality'].mean():.4f}")

    print(f"\nResults saved to:")
    print(f"- Summary CSV: {summary_path}")
    print(f"- Mosaic images: {MOSAIC_DIR}")
    print(f"- Individual metrics: {METRICS_DIR}")

    return all_results

if __name__ == "__main__":
    print("=" * 60)
    print("Interactive Image Mosaic Generator - Comprehensive Test")
    print("=" * 60)

    results = run_comprehensive_test()

    print("\n" + "=" * 60)
    print("Test completed successfully!")
    print("=" * 60)