Spaces:
Running
Running
| import gradio as gr | |
| import numpy as np | |
| from sklearn.cluster import MiniBatchKMeans | |
| from sklearn.utils import shuffle | |
| from PIL import Image | |
| import os | |
| import tempfile | |
| from io import BytesIO | |
| def compress_kmeans(image: np.ndarray, k: int, random_state: 42) -> np.ndarray: | |
| """ | |
| Faster K‑Means compression using a random pixel sample for centroid fitting. | |
| """ | |
| pixels = image.reshape(-1, 3).astype(np.float32) | |
| sample_size = min(20000, len(pixels)) | |
| sample = shuffle(pixels, random_state=random_state)[:sample_size] | |
| model = MiniBatchKMeans( | |
| n_clusters=k, | |
| batch_size=min(1024, sample_size), | |
| n_init='auto', | |
| max_iter=50, | |
| random_state=random_state, | |
| verbose=0, | |
| ) | |
| model.fit(sample) | |
| labels = model.predict(pixels) | |
| centres = model.cluster_centers_.astype(np.uint8) | |
| compressed_pixels = centres[labels] | |
| return compressed_pixels.reshape(image.shape) | |
| def process(filepath, k): | |
| if filepath is None: | |
| return None, "Please upload an image.", None | |
| # Load original | |
| orig = np.array(Image.open(filepath).convert('RGB')) | |
| # Compress | |
| comp = compress_kmeans(orig, k, random_state=42) | |
| def get_size(img): | |
| with BytesIO() as buf: | |
| Image.fromarray(img).save(buf, format='PNG') | |
| return len(buf.getvalue()) | |
| orig_size = get_size(orig) | |
| comp_size = get_size(comp) | |
| ratio = orig_size / comp_size | |
| saved = (1 - comp_size/orig_size) * 100 | |
| # PSNR | |
| mse = np.mean((orig.astype(float) - comp.astype(float)) ** 2) | |
| psnr = 20 * np.log10(255.0 / np.sqrt(mse)) if mse > 0 else float('inf') | |
| stats = (f"**Original:** {orig_size/1024:.1f} KB \n" | |
| f"**Compressed:** {comp_size/1024:.1f} KB \n" | |
| f"**Compression ratio:** {ratio:.1f}x \n" | |
| f"**Space saved:** {saved:.1f}% \n" | |
| f"**PSNR:** {psnr:.1f} dB") | |
| temp = tempfile.mkdtemp() | |
| out_path = os.path.join(temp, 'compressed.png') | |
| Image.fromarray(comp).save(out_path) | |
| return comp, stats, out_path | |
| iface = gr.Interface( | |
| fn=process, | |
| inputs=[ | |
| gr.Image(type='filepath', label='Upload an Image'), | |
| gr.Slider(minimum=2, maximum=64, step=2, value=16, label='Number of colours (k)') | |
| ], | |
| outputs=[ | |
| gr.Image(label='Compressed Image'), | |
| gr.Markdown(label='Compression Statistics'), | |
| gr.File(label='Download Compressed Image') | |
| ], | |
| title='Image Compression with K‑Means', | |
| description='Reduce the number of colours in an image using K-Means clustering.' | |
| ) | |
| iface.launch() |