DILSHAD737 commited on
Commit
9c007e5
·
verified ·
1 Parent(s): 25b025f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -289
app.py CHANGED
@@ -1,353 +1,193 @@
1
  import gradio as gr
 
 
 
2
  import tempfile
3
- import os
4
  from pathlib import Path
5
  import zipfile
6
- import time
7
- from PIL import Image
8
- import io
9
 
10
- class ImageCompressor:
11
- def __init__(self):
12
- pass
13
-
14
- def compress(self, input_path, output_path, method="webp", quality=75):
15
- """Main compression entry point"""
16
- img = Image.open(input_path)
17
- original_size = Path(input_path).stat().st_size
18
-
19
- # Smart resize if image is huge (for HF memory limits)
20
- img = self.smart_resize(img)
21
-
22
- # Always use .webp or .png extension
23
- output_file = Path(output_path)
24
-
25
- if method == "png_optimized":
26
- output_file = output_file.with_suffix('.png')
27
- compressed = self._compress_png_optimized(img)
28
- elif method == "png_quantized":
29
- output_file = output_file.with_suffix('.png')
30
- compressed = self._compress_png_quantized(img)
31
- elif method == "jpeg":
32
- output_file = output_file.with_suffix('.jpg')
33
- compressed = self._compress_jpeg(img, quality)
34
- else:
35
- # Default to WebP
36
- output_file = output_file.with_suffix('.webp')
37
- compressed = self._compress_webp(img, quality)
38
-
39
- # Save compressed image
40
- with open(output_file, 'wb') as f:
41
- f.write(compressed)
42
-
43
- compressed_size = output_file.stat().st_size
44
- ratio = (1 - compressed_size / original_size) * 100
45
-
46
- return str(output_file), ratio
47
 
48
- def _compress_webp(self, img, quality=80):
49
- """Standard WebP compression"""
50
- output = io.BytesIO()
51
-
52
- # Handle transparency
53
- if img.mode == 'RGBA':
54
- img.save(output, format='WEBP', quality=quality, method=4, lossless=False)
55
- else:
56
- img.save(output, format='WEBP', quality=quality, method=4)
57
 
58
- return output.getvalue()
59
-
60
- def _compress_png_optimized(self, img):
61
- """PNG with optimization"""
62
- output = io.BytesIO()
63
- img.save(output, format='PNG', optimize=True)
64
- return output.getvalue()
65
-
66
- def _compress_png_quantized(self, img, colors=128):
67
- """Heavy PNG quantization for graphics"""
68
  output = io.BytesIO()
69
 
70
- # Aggressive color reduction
71
- if img.mode in ('RGB', 'RGBA'):
72
- img_quantized = img.quantize(colors=colors, method=Image.FASTOCTREE)
73
-
74
- # Convert back to RGB for better compatibility
75
- if img_quantized.mode == 'P':
76
- img_quantized = img_quantized.convert('RGB')
77
-
78
- img_quantized.save(output, format='PNG', optimize=True)
79
- else:
 
 
80
  img.save(output, format='PNG', optimize=True)
 
81
 
82
- return output.getvalue()
83
-
84
- def _compress_jpeg(self, img, quality=85):
85
- """JPEG compression"""
86
- output = io.BytesIO()
87
-
88
- # Convert to RGB if needed
89
- if img.mode in ('RGBA', 'LA', 'P'):
90
- # Create white background for transparency
91
- if img.mode == 'RGBA':
92
- background = Image.new('RGB', img.size, (255, 255, 255))
93
- background.paste(img, mask=img.split()[-1])
94
- img = background
95
- else:
96
- img = img.convert('RGB')
97
-
98
- img.save(output, format='JPEG', quality=quality, optimize=True, progressive=True)
99
- return output.getvalue()
100
-
101
- def smart_resize(self, img, max_dimension=2048):
102
- """Intelligently resize if image is too large"""
103
- width, height = img.size
104
- max_dim = max(width, height)
105
-
106
- if max_dim > max_dimension:
107
- scale = max_dimension / max_dim
108
- new_size = (int(width * scale), int(height * scale))
109
- return img.resize(new_size, Image.Resampling.LANCZOS)
110
- return img
111
-
112
- def simple_analyze(image_path):
113
- """Simple image type detection without OpenCV (for HF compatibility)"""
114
- try:
115
- img = Image.open(image_path)
116
-
117
- # Check for transparency
118
- has_transparency = img.mode in ('RGBA', 'LA', 'P')
119
 
120
- # Check if likely a screenshot (based on file size vs dimensions)
121
- width, height = img.size
122
- file_size = Path(image_path).stat().st_size
123
- estimated_raw_size = width * height * 3 # RGB bytes
124
- compression_ratio = estimated_raw_size / file_size if file_size > 0 else 0
125
-
126
- if has_transparency:
127
- return "graphic_with_transparency", "png_optimized"
128
- elif compression_ratio < 5: # Highly compressed already
129
- return "photo", "webp"
130
- else:
131
- return "screenshot_or_text", "webp"
132
- except:
133
- return "photo", "webp"
134
-
135
- # Initialize compressor
136
- compressor = ImageCompressor()
137
-
138
- def compress_single_image(file, preset="auto"):
139
- """Compress a single image"""
140
- if file is None:
141
- return None, "No file uploaded", None
142
-
143
- try:
144
- input_path = Path(file.name)
145
 
146
- # Determine method based on preset
147
- if preset == "auto":
148
- content_type, method = simple_analyze(input_path)
149
- quality = 80
150
- elif preset == "extreme":
151
- method = "webp"
152
- quality = 60
153
- elif preset == "high_quality":
154
- method = "webp"
155
- quality = 90
156
- elif preset == "png_optimized":
157
- method = "png_optimized"
158
- quality = 85
159
- else:
160
- method = "webp"
161
- quality = 75
162
 
163
- # Create output path in temp directory
164
- with tempfile.TemporaryDirectory() as temp_dir:
165
- output_path = Path(temp_dir) / f"compressed_{input_path.stem}"
166
-
167
- # Compress
168
- start_time = time.time()
169
- result_path, ratio = compressor.compress(
170
- input_path,
171
- output_path,
172
- method=method,
173
- quality=quality
174
- )
175
- compression_time = time.time() - start_time
176
-
177
- # Calculate metrics
178
- original_size = input_path.stat().st_size / 1024
179
- compressed_size = Path(result_path).stat().st_size / 1024
180
-
181
- info = f"""
182
- ✅ Compression Complete!
183
-
184
  Original: {original_size:.1f} KB
185
  Compressed: {compressed_size:.1f} KB
186
- Saved: {ratio:.1f}%
187
- Time: {compression_time:.2f}s
188
- Method: {method}
189
- Quality: {quality}
190
- """
191
-
192
- # Copy to persistent location for download
193
- persistent_path = Path("/tmp") / f"{input_path.stem}_compressed_{int(time.time())}{Path(result_path).suffix}"
194
- import shutil
195
- shutil.copy(result_path, persistent_path)
196
-
197
- return str(persistent_path), info, None
198
 
199
  except Exception as e:
200
- return None, f"❌ Error: {str(e)}", None
201
 
202
- def compress_batch(files, preset="auto"):
203
- """Compress multiple images"""
204
  if not files:
205
- return None, "No files uploaded"
206
 
207
- compressed_files = []
208
  total_saved = 0
209
 
210
  with tempfile.TemporaryDirectory() as temp_dir:
211
- for idx, file in enumerate(files):
212
  try:
213
- input_path = Path(file.name)
214
- output_path = Path(temp_dir) / f"compressed_{input_path.stem}_{idx}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
- # Auto-detect method
217
- content_type, method = simple_analyze(input_path)
218
- quality = 80
 
219
 
220
- result_path, ratio = compressor.compress(input_path, output_path, method=method, quality=quality)
221
- compressed_files.append(result_path)
222
- total_saved += ratio
 
 
223
 
224
  except Exception as e:
225
- print(f"Error compressing {file.name}: {e}")
226
 
227
- if not compressed_files:
228
  return None, "No files could be compressed"
229
 
230
  # Create ZIP
231
  zip_path = Path(temp_dir) / "compressed_images.zip"
232
  with zipfile.ZipFile(zip_path, 'w') as zipf:
233
- for cf in compressed_files:
234
- zipf.write(cf, Path(cf).name)
235
 
236
  # Copy to persistent location
237
- persistent_zip = Path("/tmp") / f"batch_compressed_{int(time.time())}.zip"
238
  import shutil
239
- shutil.copy(zip_path, persistent_zip)
240
 
241
- avg_saving = total_saved / len(compressed_files) if compressed_files else 0
242
- info = f"✅ Compressed {len(compressed_files)} files\nAverage saving: {avg_saving:.1f}%"
243
 
244
- return str(persistent_zip), info
245
 
246
- def compare_images(original, compressed):
247
- """Create side-by-side comparison"""
248
- if original is None or compressed is None:
249
- return None
250
-
251
- try:
252
- # Handle both file paths and file objects
253
- if hasattr(original, 'name'):
254
- orig_path = original.name
255
- else:
256
- orig_path = original
257
-
258
- if hasattr(compressed, 'name'):
259
- comp_path = compressed.name
260
- else:
261
- comp_path = compressed
262
-
263
- orig_img = Image.open(orig_path)
264
- comp_img = Image.open(comp_path)
265
-
266
- # Resize to same height for comparison
267
- target_height = 400
268
- ratio = target_height / orig_img.size[1]
269
- new_width = int(orig_img.size[0] * ratio)
270
- orig_img = orig_img.resize((new_width, target_height), Image.Resampling.LANCZOS)
271
- comp_img = comp_img.resize((new_width, target_height), Image.Resampling.LANCZOS)
272
-
273
- # Create comparison (side by side)
274
- total_width = new_width * 2
275
- comparison = Image.new('RGB', (total_width, target_height))
276
- comparison.paste(orig_img, (0, 0))
277
- comparison.paste(comp_img, (new_width, 0))
278
-
279
- # Save to temp file
280
- temp_compare = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
281
- comparison.save(temp_compare.name)
282
-
283
- return temp_compare.name
284
- except Exception as e:
285
- print(f"Comparison error: {e}")
286
- return None
287
-
288
- # Create Gradio interface
289
- with gr.Blocks(title="AI Image Compressor", theme=gr.themes.Soft()) as demo:
290
  gr.Markdown("""
291
- # 🚀 AI-Powered Image Compressor
292
 
293
- ### Better than imagecompressor.com - Completely free, runs entirely in your browser!
294
 
295
- **Features:**
296
- - 🎯 **Smart compression** - Automatically detects image type
297
- - 📦 **WebP support** - 25-35% smaller than JPEG
298
- - 🔄 **Batch processing** - Compress up to 10 images at once
299
- - 📊 **Real metrics** - See exact savings
300
- - 💯 **Lossless options** - Perfect for graphics
301
- - 🔒 **Privacy first** - No images stored on server
302
  """)
303
 
304
  with gr.Tabs():
305
- with gr.TabItem("📸 Single Image"):
306
  with gr.Row():
307
  with gr.Column():
308
- input_image = gr.File(label="Upload Image", file_types=["image"])
309
- preset = gr.Radio(
310
- choices=["auto", "extreme", "high_quality", "png_optimized"],
311
- value="auto",
312
- label="Compression Preset",
313
- info="auto = automatically choose best method"
 
 
 
 
 
314
  )
315
- compress_btn = gr.Button("Compress!", variant="primary", size="lg")
316
 
317
  with gr.Column():
318
- output_image = gr.File(label="📥 Download Compressed Image")
319
- info_text = gr.Textbox(label="Compression Info", lines=8)
320
-
321
- with gr.Row():
322
- compare_btn = gr.Button("🔄 Compare Original vs Compressed")
323
- comparison = gr.Image(label="Left: Original | Right: Compressed")
324
-
325
- with gr.TabItem("📦 Batch Processing"):
326
- batch_files = gr.File(label="Upload Multiple Images", file_types=["image"], file_count="multiple")
327
- batch_info_text = gr.Markdown("### Upload up to 10 images for batch compression")
328
- batch_btn = gr.Button("Compress All", variant="primary", size="lg")
329
- batch_output = gr.File(label="📥 Download ZIP with Compressed Images")
330
- batch_info = gr.Textbox(label="Batch Info", lines=5)
 
 
 
 
 
 
 
 
 
 
331
 
332
  # Connect functions
333
  compress_btn.click(
334
- compress_single_image,
335
- inputs=[input_image, preset],
336
- outputs=[output_image, info_text, comparison]
337
- )
338
-
339
- compare_btn.click(
340
- compare_images,
341
- inputs=[input_image, output_image],
342
- outputs=[comparison]
343
  )
344
 
345
  batch_btn.click(
346
  compress_batch,
347
- inputs=[batch_files, batch_preset],
348
  outputs=[batch_output, batch_info]
349
  )
350
 
351
- # Launch for HF
352
  if __name__ == "__main__":
353
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import gradio as gr
2
+ from PIL import Image
3
+ import io
4
+ import time
5
  import tempfile
 
6
  from pathlib import Path
7
  import zipfile
 
 
 
8
 
9
+ def compress_image(file, quality=75, format_type="webp"):
10
+ """Simple image compression function"""
11
+ if file is None:
12
+ return None, "Please upload an image"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ try:
15
+ # Open image
16
+ img = Image.open(file.name)
17
+ original_size = Path(file.name).stat().st_size / 1024
 
 
 
 
 
18
 
19
+ # Compress to selected format
 
 
 
 
 
 
 
 
 
20
  output = io.BytesIO()
21
 
22
+ if format_type == "webp":
23
+ img.save(output, format='WEBP', quality=quality, method=4)
24
+ extension = ".webp"
25
+ elif format_type == "jpeg":
26
+ if img.mode in ('RGBA', 'LA', 'P'):
27
+ # Convert to RGB for JPEG
28
+ rgb_img = Image.new('RGB', img.size, (255, 255, 255))
29
+ rgb_img.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
30
+ img = rgb_img
31
+ img.save(output, format='JPEG', quality=quality, optimize=True)
32
+ extension = ".jpg"
33
+ else: # png
34
  img.save(output, format='PNG', optimize=True)
35
+ extension = ".png"
36
 
37
+ output.seek(0)
38
+ compressed_size = len(output.getvalue()) / 1024
39
+ saved_percent = (1 - compressed_size / original_size) * 100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
+ # Save to temp file for download
42
+ temp_path = tempfile.NamedTemporaryFile(delete=False, suffix=extension)
43
+ temp_path.write(output.getvalue())
44
+ temp_path.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ info = f"""✅ Success!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  Original: {original_size:.1f} KB
49
  Compressed: {compressed_size:.1f} KB
50
+ Saved: {saved_percent:.1f}%
51
+ Format: {format_type.upper()}
52
+ Quality: {quality}"""
53
+
54
+ return temp_path.name, info
 
 
 
 
 
 
 
55
 
56
  except Exception as e:
57
+ return None, f"❌ Error: {str(e)}"
58
 
59
+ def compress_batch(files, quality=75, format_type="webp"):
60
+ """Batch compression"""
61
  if not files:
62
+ return None, "Please upload files"
63
 
64
+ compressed_paths = []
65
  total_saved = 0
66
 
67
  with tempfile.TemporaryDirectory() as temp_dir:
68
+ for file in files:
69
  try:
70
+ img = Image.open(file.name)
71
+ original_size = Path(file.name).stat().st_size / 1024
72
+
73
+ output = io.BytesIO()
74
+ if format_type == "webp":
75
+ img.save(output, format='WEBP', quality=quality)
76
+ ext = ".webp"
77
+ elif format_type == "jpeg":
78
+ if img.mode in ('RGBA', 'LA', 'P'):
79
+ rgb_img = Image.new('RGB', img.size, (255, 255, 255))
80
+ rgb_img.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
81
+ img = rgb_img
82
+ img.save(output, format='JPEG', quality=quality)
83
+ ext = ".jpg"
84
+ else:
85
+ img.save(output, format='PNG', optimize=True)
86
+ ext = ".png"
87
 
88
+ output.seek(0)
89
+ compressed_size = len(output.getvalue()) / 1024
90
+ saved = (1 - compressed_size / original_size) * 100
91
+ total_saved += saved
92
 
93
+ # Save file
94
+ temp_file = Path(temp_dir) / f"compressed_{Path(file.name).stem}{ext}"
95
+ with open(temp_file, 'wb') as f:
96
+ f.write(output.getvalue())
97
+ compressed_paths.append(temp_file)
98
 
99
  except Exception as e:
100
+ print(f"Error: {e}")
101
 
102
+ if not compressed_paths:
103
  return None, "No files could be compressed"
104
 
105
  # Create ZIP
106
  zip_path = Path(temp_dir) / "compressed_images.zip"
107
  with zipfile.ZipFile(zip_path, 'w') as zipf:
108
+ for path in compressed_paths:
109
+ zipf.write(path, path.name)
110
 
111
  # Copy to persistent location
112
+ final_zip = Path("/tmp") / f"batch_{int(time.time())}.zip"
113
  import shutil
114
+ shutil.copy(zip_path, final_zip)
115
 
116
+ avg_saved = total_saved / len(compressed_paths)
117
+ info = f"✅ Compressed {len(compressed_paths)} files\nAverage saving: {avg_saved:.1f}%"
118
 
119
+ return str(final_zip), info
120
 
121
+ # Create interface
122
+ with gr.Blocks(title="Image Compressor", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  gr.Markdown("""
124
+ # 🚀 Image Compressor
125
 
126
+ **Simple, fast, and private - all processing happens in your browser!**
127
 
128
+ ### Features:
129
+ - Compress JPEG, PNG, WebP
130
+ - Batch processing with ZIP download
131
+ - Adjustable quality
132
+ - No uploads to server
 
 
133
  """)
134
 
135
  with gr.Tabs():
136
+ with gr.TabItem("Single Image"):
137
  with gr.Row():
138
  with gr.Column():
139
+ input_file = gr.File(label="Upload Image", file_types=["image"])
140
+ format_choice = gr.Radio(
141
+ choices=["webp", "jpeg", "png"],
142
+ value="webp",
143
+ label="Output Format"
144
+ )
145
+ quality_slider = gr.Slider(
146
+ minimum=30,
147
+ maximum=100,
148
+ value=75,
149
+ label="Quality (higher = better quality, larger file)"
150
  )
151
+ compress_btn = gr.Button("Compress", variant="primary")
152
 
153
  with gr.Column():
154
+ output_file = gr.File(label="Download Compressed Image")
155
+ info_text = gr.Textbox(label="Results", lines=8)
156
+
157
+ with gr.TabItem("Batch Processing"):
158
+ batch_files = gr.File(
159
+ label="Upload Multiple Images",
160
+ file_types=["image"],
161
+ file_count="multiple"
162
+ )
163
+ batch_format = gr.Radio(
164
+ choices=["webp", "jpeg", "png"],
165
+ value="webp",
166
+ label="Output Format"
167
+ )
168
+ batch_quality = gr.Slider(
169
+ minimum=30,
170
+ maximum=100,
171
+ value=75,
172
+ label="Quality"
173
+ )
174
+ batch_btn = gr.Button("Compress All", variant="primary")
175
+ batch_output = gr.File(label="Download ZIP")
176
+ batch_info = gr.Textbox(label="Results", lines=5)
177
 
178
  # Connect functions
179
  compress_btn.click(
180
+ compress_image,
181
+ inputs=[input_file, quality_slider, format_choice],
182
+ outputs=[output_file, info_text]
 
 
 
 
 
 
183
  )
184
 
185
  batch_btn.click(
186
  compress_batch,
187
+ inputs=[batch_files, batch_quality, batch_format],
188
  outputs=[batch_output, batch_info]
189
  )
190
 
191
+ # Launch
192
  if __name__ == "__main__":
193
  demo.launch(server_name="0.0.0.0", server_port=7860)