Shekarss commited on
Commit
e37efca
·
verified ·
1 Parent(s): 5920438

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -0
app.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from PIL import Image
3
+ import gradio as gr
4
+ import tempfile
5
+ import os
6
+
7
+ def initialize_centroids(X, k):
8
+ np.random.seed(42)
9
+ indices = np.random.choice(X.shape[0], k, replace=False)
10
+ return X[indices]
11
+
12
+ def compute_centroids(X, labels, k):
13
+ centroids = []
14
+ for i in range(k):
15
+ cluster_points = X[labels == i]
16
+ if len(cluster_points) > 0:
17
+ centroids.append(cluster_points.mean(axis=0))
18
+ else:
19
+ centroids.append(X[np.random.choice(len(X))])
20
+ return np.array(centroids)
21
+
22
+ def assign_clusters(X, centroids):
23
+ distances = np.linalg.norm(X[:, np.newaxis] - centroids, axis=2)
24
+ return np.argmin(distances, axis=1)
25
+
26
+ def kmeans(X, k, max_iters=100):
27
+ centroids = initialize_centroids(X, k)
28
+ for _ in range(max_iters):
29
+ labels = assign_clusters(X, centroids)
30
+ new_centroids = compute_centroids(X, labels, k)
31
+ if np.allclose(centroids, new_centroids):
32
+ break
33
+ centroids = new_centroids
34
+ return centroids, labels
35
+
36
+ def compress_image(img, clusters=16):
37
+ if clusters < 2:
38
+ return None, "Minimum 3 clusters required."
39
+
40
+ # Resize for speed & consistency
41
+ max_dim = 512
42
+ w, h = img.size
43
+ scale = max_dim / max(w, h)
44
+ img = img.resize((int(w * scale), int(h * scale)))
45
+
46
+ img_np = np.array(img)
47
+ shape = img_np.shape
48
+ img_flat = img_np.reshape(-1, 3).astype(float)
49
+
50
+ centroids, labels = kmeans(img_flat, clusters, max_iters=30)
51
+ compressed_flat = centroids[labels].astype(np.uint8)
52
+ compressed_img = compressed_flat.reshape(shape)
53
+
54
+ # Save original and compressed to compare file sizes (use PNG to avoid JPEG inconsistency)
55
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as orig:
56
+ Image.fromarray(img_np).save(orig.name, format='PNG')
57
+ orig_size = os.path.getsize(orig.name)
58
+
59
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as comp:
60
+ Image.fromarray(compressed_img).save(comp.name, format='PNG')
61
+ comp_size = os.path.getsize(comp.name)
62
+
63
+ os.remove(orig.name)
64
+ os.remove(comp.name)
65
+
66
+ percent = 100 * (orig_size - comp_size) / orig_size
67
+ if percent < 0:
68
+ status = f"⚠️ Larger by {abs(percent):.2f}% (due to more color detail)"
69
+ else:
70
+ status = f"✅ Compressed by {percent:.2f}%"
71
+
72
+ return Image.fromarray(compressed_img), status
73
+
74
+ iface = gr.Interface(
75
+ fn=compress_image,
76
+ inputs=[
77
+ gr.Image(type="pil", label="Upload Image"),
78
+ gr.Slider(2, 64, value=16, step=1, label="Number of Clusters")
79
+ ],
80
+ outputs=[
81
+ gr.Image(type="pil", label="Compressed Image"),
82
+ gr.Label(label="Compression Info")
83
+ ],
84
+ title="Fixed K-Means Image Compressor",
85
+ description="Uses KMeans clustering on color pixels. PNG is used to give accurate compression size comparison."
86
+ )
87
+
88
+ if __name__ == "__main__":
89
+ iface.launch()