Spaces:
Sleeping
Sleeping
""" | |
Colour Quantizer by Peter Chibuikem Idoko | |
2025-02-03 | |
Uses K-Means Clustering to apply colour quantization for improved efficiency. | |
Enhancements: | |
- Logging for better debugging. | |
- Structured error handling. | |
- Dynamic parameters for better configurability. | |
- Supports multiple image formats. | |
""" | |
import os | |
import cv2 | |
import numpy as np | |
import logging | |
from typing import Optional | |
# Configure logging | |
logging.basicConfig( | |
filename="quantizer.log", | |
level=logging.INFO, | |
format="%(asctime)s - %(levelname)s - %(message)s", | |
) | |
logging.info("Colour Quantizer script started.") | |
# Constants | |
DEFAULT_K = 8 # Default number of clusters (colors) | |
RESIZE_DIM = (300, 300) # Fixed resolution of all images | |
INPUT_FOLDER = "scraped_photos" | |
OUTPUT_FOLDER = "quantized_photos" | |
SUPPORTED_FORMATS = (".jpg", ".jpeg", ".png", ".bmp") | |
# Ensure output directory exists | |
os.makedirs(OUTPUT_FOLDER, exist_ok=True) | |
# Function to quantize images may return None | |
def quantize_image(image_path: str, K: int = DEFAULT_K, resize_dim: tuple = RESIZE_DIM) -> Optional[np.ndarray]: | |
""" | |
Applies colour quantization using K-Means clustering. | |
Args: | |
image_path (str): Path to the image file. | |
K (int): Number of clusters (colors) to reduce the image to. | |
resize_dim (tuple): Target resolution (width, height) for resizing. | |
Returns: | |
np.ndarray: The quantized image, or None if an error occurs. | |
""" | |
try: | |
# Load the image | |
image = cv2.imread(image_path) | |
if image is None: | |
logging.error(f"Error loading image: {image_path}") | |
return None | |
# Resize image to fixed resolution (300x300) | |
image = cv2.resize(image, resize_dim) | |
# Convert image to RGB (OpenCV loads images in BGR) | |
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | |
# Reshape image into a 2D array of pixels (Each pixel is a 3D point [R, G, B]) | |
pixel_values = image.reshape((-1, 3)).astype(np.float32) | |
# Define stopping criteria and apply K-Means clustering | |
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2) | |
_, labels, centers = cv2.kmeans(pixel_values, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS) | |
# Convert centers back to uint8 (color values) | |
centers = np.uint8(centers) | |
# Replace each pixel value with its nearest centroid color | |
quantized_image = centers[labels.flatten()] | |
# Reshape back to the original image shape | |
quantized_image = quantized_image.reshape(image.shape) | |
return quantized_image | |
except Exception as e: | |
logging.error(f"Error processing image {image_path}: {e}") | |
return None | |
def process_images(input_folder: str = INPUT_FOLDER, output_folder: str = OUTPUT_FOLDER, K: int = DEFAULT_K): | |
""" | |
Processes all images in the input folder and applies color quantization. | |
Args: | |
input_folder (str): Directory containing images to process. | |
output_folder (str): Directory to save quantized images. | |
K (int): Number of colors (clusters) for quantization. | |
""" | |
logging.info(f"Processing images from {input_folder}, saving to {output_folder}") | |
total_images = len([f for f in os.listdir(input_folder) if f.lower().endswith(SUPPORTED_FORMATS)]) | |
processed_count = 0 # Track the number of images processed | |
for filename in os.listdir(input_folder): | |
if filename.lower().endswith(SUPPORTED_FORMATS): | |
input_path = os.path.join(input_folder, filename) | |
output_path = os.path.join(output_folder, filename) | |
quantized_img = quantize_image(input_path, K) | |
if quantized_img is not None: | |
cv2.imwrite(output_path, cv2.cvtColor(quantized_img, cv2.COLOR_RGB2BGR)) | |
processed_count += 1 | |
# Log progress to both console and log file | |
log_message = f"[{processed_count}/{total_images}] Quantized & saved: {output_path}" | |
print(log_message) # Console output | |
logging.info(log_message) # Log file entry | |
logging.info("Color quantization complete.") | |
print("Color quantization complete!") # Console notification | |
if __name__ == "__main__": | |
process_images() | |