File size: 5,071 Bytes
d2aa004
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
import numpy as np
import cv2
from skimage.metrics import structural_similarity as ssim
from PIL import Image

def preprocess_image(image, grid_size):
    """Resize image to ensure consistent processing."""
    if isinstance(image, str):  # If image is a file path
        image = cv2.imread(image)
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    
    height, width = image.shape[:2]
    max_dim = 512
    scale = max_dim / max(height, width)
    new_width = int(width * scale)
    new_height = int(height * scale)
    return cv2.resize(image, (new_width, new_height))

def quantize_colors(image, n_colors=8):
    """Quantize colors using optimized K-means clustering."""
    pixels = image.reshape(-1, 3).astype(np.float32)  # Flatten image for K-means
    
    # Define K-means criteria and apply clustering
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
    _, labels, centers = cv2.kmeans(pixels, n_colors, None, criteria, 10, cv2.KMEANS_PP_CENTERS)
    
    # Convert back to uint8 and reshape
    centers = np.uint8(centers)
    quantized = centers[labels.flatten()]
    return quantized.reshape(image.shape)

def create_grid(image, grid_size):
    """Divide image into grid cells."""
    height, width = image.shape[:2]
    cell_height = height // grid_size
    cell_width = width // grid_size
    
    grid = []
    for i in range(grid_size):
        row = []
        for j in range(grid_size):
            y_start = i * cell_height
            x_start = j * cell_width
            y_end = height if i == grid_size - 1 else (i + 1) * cell_height
            x_end = width if j == grid_size - 1 else (j + 1) * cell_width
            cell = image[y_start:y_end, x_start:x_end]
            row.append(cell)
        grid.append(row)
    return grid

def get_average_color(cell):
    """Calculate average color of a grid cell."""
    return np.mean(cell, axis=(0, 1)).astype(np.uint8)

def create_mosaic(image, grid_size, use_color_quantization, n_colors=8):
    """Create mosaic from input image using optional color quantization."""
    processed_image = preprocess_image(image, grid_size)
    
    if use_color_quantization:
        processed_image = quantize_colors(processed_image, n_colors)
    
    grid = create_grid(processed_image, grid_size)
    
    height, width = processed_image.shape[:2]
    cell_height = height // grid_size
    cell_width = width // grid_size
    
    mosaic = np.zeros_like(processed_image)
    
    for i in range(grid_size):
        for j in range(grid_size):
            y_start = i * cell_height
            x_start = j * cell_width
            y_end = height if i == grid_size - 1 else (i + 1) * cell_height
            x_end = width if j == grid_size - 1 else (j + 1) * cell_width

            avg_color = get_average_color(grid[i][j])
            mosaic[y_start:y_end, x_start:x_end] = avg_color

    
    return processed_image, mosaic

def calculate_similarity(original, mosaic):
    """Calculate similarity between original and mosaic images."""
    original_gray = cv2.cvtColor(original, cv2.COLOR_RGB2GRAY)
    mosaic_gray = cv2.cvtColor(mosaic, cv2.COLOR_RGB2GRAY)
    
    similarity = ssim(original_gray, mosaic_gray)
    mse = np.mean((original - mosaic) ** 2)
    
    return similarity, mse

def process_image(input_image, grid_size, use_quantization, n_colors):
    """Process image with K-means color quantization instead of a fixed palette."""
    original, mosaic = create_mosaic(input_image, grid_size, use_quantization, n_colors)
    similarity, mse = calculate_similarity(original, mosaic)
    
    return (mosaic, 
            f"Structural Similarity: {similarity:.4f}",
            f"Mean Squared Error: {mse:.4f}")

iface = gr.Interface(
    fn=process_image,
    inputs=[
        gr.Image(type="numpy", label="Upload Image"),
        gr.Slider(minimum=8, maximum=64, step=8, value=16, label="Grid Size"),
        gr.Checkbox(label="Use Color Quantization"),
        gr.Slider(minimum=2, maximum=16, step=1, value=8, label="Number of Colors (K-Means)")
    ],
    outputs=[
        gr.Image(type="numpy", label="Mosaic Result"),
        gr.Textbox(label="Structural Similarity (SSIM)"),
        gr.Textbox(label="Mean Squared Error (MSE)")
    ],
    title="Interactive Image Mosaic Generator (Optimized K-Means)",
    description="Upload an image to create a mosaic-style reconstruction. Adjust the grid size and color quantization settings.",
    examples=[["https://res.cloudinary.com/daigovpbf/image/upload/c_crop,g_auto,h_800,w_800/samples/cup-on-a-table", 8, False, 0],
              ["https://res.cloudinary.com/daigovpbf/image/upload/c_crop,g_auto,h_800,w_800/samples/dessert-on-a-plate", 16, True, 8],
              ["https://res.cloudinary.com/daigovpbf/image/upload/c_crop,g_auto,h_800,w_800/samples/breakfast", 32, True, 4],
              ["https://res.cloudinary.com/daigovpbf/image/upload/c_crop,g_auto,h_800,w_800/samples/balloons", 64, True, 8]],
    theme="default"
)

if __name__ == "__main__":
    iface.launch(share=True)