NiranjanSathish's picture
Upload 41 files
f647a80 verified
"""
ImagePreprocessor.py - FULLY OPTIMIZED
Minimal overhead, fast operations, no unnecessary prints.
"""
import cv2
import numpy as np
from PIL import Image
from typing import Tuple, Optional
from sklearn.cluster import MiniBatchKMeans
import warnings
class ImagePreprocessor:
"""OPTIMIZED image preprocessing for mosaic generation."""
def __init__(self, target_resolution: Tuple[int, int] = (800, 600),
grid_size: Tuple[int, int] = (20, 15),
verbose: bool = False):
"""Initialize preprocessor - MINIMAL overhead."""
self.target_resolution = target_resolution
self.grid_size = grid_size
self.verbose = verbose
# Pre-compute (avoid division later)
self.tile_width = target_resolution[0] // grid_size[0]
self.tile_height = target_resolution[1] // grid_size[1]
self.adjusted_width = self.tile_width * grid_size[0]
self.adjusted_height = self.tile_height * grid_size[1]
def load_and_preprocess_image(self, image_path: str,
apply_quantization: bool = False,
n_colors: int = 16) -> Optional[np.ndarray]:
"""Load and preprocess - OPTIMIZED for speed."""
try:
# Load and convert
image = cv2.imread(image_path)
if image is None:
raise ValueError(f"Could not load image: {image_path}")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
processed_image = self._resize_and_crop(image)
# Only quantize if explicitly requested
if apply_quantization and n_colors < 256:
processed_image = self._apply_color_quantization(processed_image, n_colors)
return processed_image
except Exception as e:
if self.verbose:
print(f"Error processing {image_path}: {str(e)}")
return None
def preprocess_numpy_image(self, image: np.ndarray,
apply_quantization: bool = False,
n_colors: int = 16) -> Optional[np.ndarray]:
"""Preprocess numpy image (for Gradio)."""
try:
if len(image.shape) != 3 or image.shape[2] != 3:
raise ValueError("Image must be RGB (H, W, 3)")
processed_image = self._resize_and_crop(image)
if apply_quantization and n_colors < 256:
processed_image = self._apply_color_quantization(processed_image, n_colors)
return processed_image
except Exception as e:
if self.verbose:
print(f"Error processing image: {str(e)}")
return None
def _resize_and_crop(self, image: np.ndarray) -> np.ndarray:
"""Resize and crop - OPTIMIZED."""
h, w = image.shape[:2]
target_w, target_h = self.adjusted_width, self.adjusted_height
# Scale to fill
scale = max(target_w / w, target_h / h)
new_w = int(w * scale)
new_h = int(h * scale)
# Use INTER_LINEAR for speed (faster than INTER_AREA)
resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
# Center crop
start_x = (new_w - target_w) // 2
start_y = (new_h - target_h) // 2
cropped = resized[start_y:start_y + target_h, start_x:start_x + target_w]
return cropped
def _apply_color_quantization(self, image: np.ndarray, n_colors: int) -> np.ndarray:
"""OPTIMIZED color quantization with aggressive sampling."""
h, w, c = image.shape
pixels = image.reshape(-1, c)
total_pixels = len(pixels)
# Aggressive sampling
max_sample_size = 10000
if total_pixels > max_sample_size:
# Sample for fitting
sample_indices = np.random.randint(0, total_pixels, size=max_sample_size)
sampled_pixels = pixels[sample_indices].astype(np.float32)
batch_size = min(max(len(sampled_pixels) // 10, 500), 2000)
with warnings.catch_warnings():
warnings.filterwarnings("ignore")
kmeans = MiniBatchKMeans(
n_clusters=n_colors, batch_size=batch_size,
random_state=42, n_init=1, max_iter=50
)
# Fit on sample, predict on full
kmeans.fit(sampled_pixels)
labels = kmeans.predict(pixels.astype(np.float32))
else:
# Small image
batch_size = min(max(total_pixels // 100, 500), 5000)
with warnings.catch_warnings():
warnings.filterwarnings("ignore")
kmeans = MiniBatchKMeans(
n_clusters=n_colors, batch_size=batch_size,
random_state=42, n_init=1, max_iter=50
)
labels = kmeans.fit_predict(pixels.astype(np.float32))
# Apply quantization
quantized_pixels = kmeans.cluster_centers_[labels]
quantized_image = quantized_pixels.reshape(h, w, c).astype(np.uint8)
return quantized_image
def save_preprocessed_image(self, image: np.ndarray, output_path: str):
"""Save preprocessed image."""
try:
pil_image = Image.fromarray(image)
pil_image.save(output_path, quality=95)
except Exception as e:
if self.verbose:
print(f"Error saving image: {str(e)}")