File size: 3,503 Bytes
4376584
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from __future__ import annotations
import numpy as np
from PIL import Image
from sklearn.cluster import KMeans
from .utils import pil_to_np, np_to_pil
from .config import Config


def apply_uniform_quantization(image: Image.Image, levels: int) -> Image.Image:
    """
    Apply uniform color quantization to reduce color variations.
    
    Args:
        image: Input PIL Image
        levels: Number of quantization levels per channel
        
    Returns:
        Quantized PIL Image
    """
    img_array = pil_to_np(image)
    
    # Quantize each channel uniformly
    quantized = np.zeros_like(img_array)
    for channel in range(3):
        # Create quantization levels
        channel_data = img_array[:, :, channel]
        
        # Uniform quantization
        quantized_channel = np.round(channel_data * (levels - 1)) / (levels - 1)
        quantized_channel = np.clip(quantized_channel, 0, 1)
        quantized[:, :, channel] = quantized_channel
    
    return np_to_pil(quantized)


def apply_kmeans_quantization(image: Image.Image, k_colors: int) -> Image.Image:
    """
    Apply K-means clustering for color quantization.
    
    Args:
        image: Input PIL Image
        k_colors: Number of colors to reduce to
        
    Returns:
        Quantized PIL Image
    """
    img_array = pil_to_np(image)
    h, w, c = img_array.shape
    
    # Reshape image to list of pixels
    pixels = img_array.reshape(-1, c)
    
    # Apply K-means clustering
    kmeans = KMeans(n_clusters=k_colors, random_state=42, n_init=10)
    kmeans.fit(pixels)
    
    # Replace each pixel with its cluster center
    labels = kmeans.labels_
    quantized_pixels = kmeans.cluster_centers_[labels]
    
    # Reshape back to image
    quantized_img = quantized_pixels.reshape(h, w, c)
    
    return np_to_pil(quantized_img)


def apply_color_quantization(image: Image.Image, config: Config) -> Image.Image:
    """
    Apply color quantization based on configuration.
    
    Args:
        image: Input PIL Image
        config: Configuration object
        
    Returns:
        Quantized PIL Image
    """
    if config.use_uniform_q:
        return apply_uniform_quantization(image, config.q_levels)
    elif config.use_kmeans_q:
        return apply_kmeans_quantization(image, config.k_colors)
    else:
        # No quantization
        return image


def analyze_quantization_effect(original: Image.Image, quantized: Image.Image) -> dict:
    """
    Analyze the effect of quantization on the image.
    
    Args:
        original: Original image
        quantized: Quantized image
        
    Returns:
        Dictionary with analysis results
    """
    orig_array = pil_to_np(original)
    quant_array = pil_to_np(quantized)
    
    # Calculate differences
    diff = np.abs(orig_array - quant_array)
    
    # Calculate statistics
    mse = np.mean((orig_array - quant_array) ** 2)
    psnr = 20 * np.log10(1.0 / np.sqrt(mse)) if mse > 0 else float('inf')
    
    # Count unique colors
    orig_colors = len(np.unique(orig_array.reshape(-1, 3), axis=0))
    quant_colors = len(np.unique(quant_array.reshape(-1, 3), axis=0))
    
    return {
        'mse': float(mse),
        'psnr': float(psnr),
        'mean_difference': float(np.mean(diff)),
        'max_difference': float(np.max(diff)),
        'original_colors': orig_colors,
        'quantized_colors': quant_colors,
        'color_reduction_ratio': orig_colors / quant_colors if quant_colors > 0 else float('inf')
    }