devskale commited on
Commit
8620613
·
1 Parent(s): 7c31cdb

download in multiple file formats

Browse files
Files changed (2) hide show
  1. gradio_imager.py +188 -47
  2. image_processor.py +171 -1
gradio_imager.py CHANGED
@@ -2,7 +2,8 @@ import gradio as gr
2
  from PIL import Image
3
  import tempfile
4
  import os
5
- from image_processor import process_image
 
6
 
7
 
8
  def apply_standard_settings(setting):
@@ -15,12 +16,12 @@ def apply_standard_settings(setting):
15
  "M dark": (True, True, "480x480", 96, "#2A373D"),
16
  "L dark": (True, True, "960x960", 128, "#2A373D"),
17
  }
18
- # Default to no special settings
19
  return settings_dict.get(setting, (None, None, None, None, None))
20
 
21
 
22
- def settings_description(crop, remove_bg, resize, padding, background):
23
- """Generate an HTML text description of the current settings in a smaller font and list format."""
 
24
  description = f"""
25
  <ul style="font-size:small;">
26
  <li>Crop: {crop}</li>
@@ -28,79 +29,219 @@ def settings_description(crop, remove_bg, resize, padding, background):
28
  <li>Resize: {resize if resize else 'No resize'}</li>
29
  <li>Padding: {padding}</li>
30
  <li>Background: {background}</li>
 
31
  </ul>
32
  """
33
  return description
34
 
35
 
36
- def gradio_interface(image, standard_settings, crop=False, remove_bg=False, resize=None, padding=0, background="white"):
37
- # Apply standard settings if selected and not "None"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  if image is None:
39
- # Load the standard image from the specified path if no image is uploaded
40
  standard_image_path = './data/examples/supermario.png'
41
  image = Image.open(standard_image_path)
42
 
43
- if standard_settings and standard_settings != "None":
44
- crop, remove_bg, resize, padding, background = apply_standard_settings(
45
- standard_settings)
 
 
 
 
46
 
47
- # Generate settings description
48
- applied_settings = settings_description(
49
- crop, remove_bg, resize, padding, background)
 
 
 
50
 
51
- # Convert resize string to tuple (if provided)
52
  resize_dimensions = None
53
  if resize:
54
  try:
55
  width, height = map(int, resize.split('x'))
56
  resize_dimensions = (width, height)
57
  except ValueError:
58
- return "Invalid format for resize dimensions. Please use 'WxH'.", "original", applied_settings
59
- # Process the image directly
 
60
  processed_image = process_image(
61
- image, crop, remove_bg, resize_dimensions, padding, background)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  # Generate settings description
64
  applied_settings = settings_description(
65
- crop, remove_bg, resize, padding, background)
 
66
 
67
- return processed_image, applied_settings
 
 
 
 
68
 
69
 
 
70
  example_images = [
71
  [os.path.join("data", "examples", "supermario.png"),
72
- "S light", True, True, "480x420", 10, "whitesmoke"],
73
  [os.path.join("data", "examples",
74
- "depositphotos_520707962-stock-photo-fujifilm-s10-body-black-fujifilm.jpg"), "None", True, True, "480x320", 48, "blue"],
 
75
  [os.path.join("data", "examples", "batman_b_c_320x280_bg.png"),
76
- "None", True, True, "360x360", 48, "yellow"],
 
77
 
78
 
79
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- # Define the Gradio interface
82
- interface = gr.Interface(fn=gradio_interface,
83
- inputs=[
84
- gr.components.Image(
85
- type="pil", label="Input Image"),
86
- gr.components.Radio(choices=[
87
- "None", "S light", "M light", "L light", "S dark", "M dark", "L dark"], label="Settings"),
88
- gr.components.Checkbox(label="Crop"),
89
- gr.components.Checkbox(label="Remove Background"),
90
- gr.components.Textbox(
91
- label="Resize (WxH)", placeholder="Example: 100x100"),
92
- gr.components.Slider(
93
- minimum=0, maximum=200, label="Padding"),
94
- gr.components.Textbox(
95
- label="Background", placeholder="Color name or hex code")
96
- ],
97
- outputs=[
98
- gr.components.Image(type="pil"),
99
- gr.components.HTML(label="Applied Settings")
100
- ],
101
- examples=example_images,
102
- title="IMAGER ___ Image Processor",
103
- description="Upload an image and select processing options or choose a standard setting. Supports crop, autoremove background, resize, add padding, and set the background color.",)
104
 
105
  if __name__ == "__main__":
106
- interface.launch()
 
2
  from PIL import Image
3
  import tempfile
4
  import os
5
+ from image_processor import process_image, save_image_with_format
6
+ import io
7
 
8
 
9
  def apply_standard_settings(setting):
 
16
  "M dark": (True, True, "480x480", 96, "#2A373D"),
17
  "L dark": (True, True, "960x960", 128, "#2A373D"),
18
  }
 
19
  return settings_dict.get(setting, (None, None, None, None, None))
20
 
21
 
22
+ # Update settings description for simpler UI
23
+ def settings_description(crop, remove_bg, resize, padding, background, quality):
24
+ """Generate an HTML text description of the current settings."""
25
  description = f"""
26
  <ul style="font-size:small;">
27
  <li>Crop: {crop}</li>
 
29
  <li>Resize: {resize if resize else 'No resize'}</li>
30
  <li>Padding: {padding}</li>
31
  <li>Background: {background}</li>
32
+ <li>Quality: {quality}%</li>
33
  </ul>
34
  """
35
  return description
36
 
37
 
38
+
39
+ def get_image_info(image):
40
+ """Get image dimensions and file size in a formatted string."""
41
+ import os
42
+ import io
43
+
44
+ if image is None:
45
+ return ""
46
+
47
+ # Extract original filename
48
+ original_name = "image" # Default fallback
49
+ if isinstance(image, dict): # Check if image is a dictionary (Gradio type='file')
50
+ if 'name' in image:
51
+ original_name = os.path.splitext(os.path.basename(image['name']))[0]
52
+ image = image.get('image') # Extract actual image object
53
+ elif hasattr(image, 'filename') and image.filename: # Check for filename attribute
54
+ original_name = os.path.splitext(os.path.basename(image.filename))[0]
55
+
56
+ # Validate the image object
57
+ if not hasattr(image, 'size'):
58
+ return f"<p>Invalid image object provided.</p>"
59
+
60
+ # Get image dimensions
61
+ width, height = image.size
62
+
63
+ # Calculate file size
64
+ buffer = io.BytesIO()
65
+ image.save(buffer, format='PNG')
66
+ size_bytes = buffer.tell()
67
+ size_kb = size_bytes / 1024
68
+
69
+ return f"""<div style="text-align: left; padding: 5px;">
70
+ <p>Original Name: {original_name}</p>
71
+ <p>Size: {width}x{height}px ({size_kb:.1f} KB)</p>
72
+ </div>"""
73
+
74
+
75
+ def get_formatted_filename(original_name, width, height, format):
76
+ """Generate formatted filename with size."""
77
+ # Remove extension from original name
78
+ base_name = os.path.splitext(os.path.basename(original_name))[0]
79
+ return f"{base_name}_{width}x{height}.{format}"
80
+
81
+
82
+
83
+ def gradio_interface(image, standard_settings, crop=False, remove_bg=False,
84
+ resize=None, padding=0, background="white",
85
+ quality=90):
86
+ """Main interface function for the Gradio app."""
87
+ quality = int(quality)
88
+
89
  if image is None:
 
90
  standard_image_path = './data/examples/supermario.png'
91
  image = Image.open(standard_image_path)
92
 
93
+ # Get original filename without extension
94
+ if isinstance(image, dict) and 'name' in image: # Check if image contains metadata
95
+ original_name = os.path.splitext(image['name'])[0]
96
+ elif hasattr(image, 'filename') and image.filename:
97
+ original_name = os.path.splitext(os.path.basename(image.filename))[0]
98
+ else:
99
+ original_name = "image" # Default name if no filename available
100
 
101
+ print(f"Debug - Image info: {type(image)}") # Debug line
102
+ if isinstance(image, dict):
103
+ print(f"Debug - Image dict: {image.keys()}") # Debug line
104
+
105
+ if standard_settings and standard_settings != "None":
106
+ crop, remove_bg, resize, padding, background = apply_standard_settings(standard_settings)
107
 
 
108
  resize_dimensions = None
109
  if resize:
110
  try:
111
  width, height = map(int, resize.split('x'))
112
  resize_dimensions = (width, height)
113
  except ValueError:
114
+ return None, None, None, None, "Invalid format for resize dimensions. Please use 'WxH'."
115
+
116
+ # Process the image
117
  processed_image = process_image(
118
+ image,
119
+ crop=crop,
120
+ remove_bg=remove_bg,
121
+ resize=resize_dimensions,
122
+ padding=padding,
123
+ background=background
124
+ )
125
+
126
+ # Get temp directory for saving files
127
+ temp_dir = tempfile.gettempdir()
128
+
129
+ # Save in multiple formats
130
+ downloads = {}
131
+ # Save WebP
132
+ downloads['webp'] = save_image_with_format(
133
+ processed_image,
134
+ os.path.join(temp_dir, 'temp'),
135
+ format='webp',
136
+ quality=quality,
137
+ custom_filename=original_name
138
+ )
139
+
140
+ # Save PNG
141
+ downloads['png'] = save_image_with_format(
142
+ processed_image,
143
+ os.path.join(temp_dir, 'temp'),
144
+ format='png',
145
+ custom_filename=original_name
146
+ )
147
+
148
+ # Save JPG
149
+ downloads['jpg'] = save_image_with_format(
150
+ processed_image,
151
+ os.path.join(temp_dir, 'temp'),
152
+ format='jpg',
153
+ quality=quality,
154
+ custom_filename=original_name
155
+ )
156
 
157
  # Generate settings description
158
  applied_settings = settings_description(
159
+ crop, remove_bg, resize, padding, background, quality
160
+ )
161
 
162
+ return (processed_image,
163
+ downloads['webp'],
164
+ downloads['png'],
165
+ downloads['jpg'],
166
+ applied_settings)
167
 
168
 
169
+ # Define example images first (update to remove output_format parameter)
170
  example_images = [
171
  [os.path.join("data", "examples", "supermario.png"),
172
+ "S light", True, True, "480x420", 10, "whitesmoke", 90],
173
  [os.path.join("data", "examples",
174
+ "depositphotos_520707962-stock-photo-fujifilm-s10-body-black-fujifilm.jpg"),
175
+ "None", True, True, "480x320", 48, "blue", 90],
176
  [os.path.join("data", "examples", "batman_b_c_320x280_bg.png"),
177
+ "None", True, True, "360x360", 48, "yellow", 90],
178
+ ]
179
 
180
 
181
+ with gr.Blocks(title="IMAGER ___ Image Processor") as interface:
182
+ gr.Markdown("Upload an image and select processing options or choose a standard setting. Supports crop, autoremove background, resize, add padding, and set the background color. Download in multiple formats.")
183
+
184
+ with gr.Row():
185
+ with gr.Column(scale=1):
186
+ # Input column
187
+ input_image = gr.Image(type="pil", label="Input Image")
188
+ input_info = gr.HTML(label="Input Image Information") # For showing input image info
189
+
190
+ # Update image info when image changes
191
+ input_image.change(
192
+ fn=get_image_info,
193
+ inputs=[input_image],
194
+ outputs=[input_info]
195
+ )
196
+
197
+ settings = gr.Radio(
198
+ choices=["None", "S light", "M light", "L light", "S dark", "M dark", "L dark"],
199
+ label="Settings",
200
+ value="None"
201
+ )
202
+ crop = gr.Checkbox(label="Crop")
203
+ remove_bg = gr.Checkbox(label="Remove Background")
204
+ resize = gr.Textbox(label="Resize (WxH)", placeholder="Example: 100x100")
205
+ padding = gr.Slider(minimum=0, maximum=200, label="Padding")
206
+ background = gr.Textbox(label="Background", placeholder="Color name or hex code")
207
+ quality = gr.Slider(
208
+ minimum=1,
209
+ maximum=95,
210
+ value=85,
211
+ step=1,
212
+ label="Image Quality %"
213
+ )
214
+
215
+ with gr.Column(scale=1):
216
+ # Output column
217
+ output_image = gr.Image(type="pil", label="Preview")
218
+ webp_download = gr.File(label="Download WebP")
219
+ png_download = gr.File(label="Download PNG")
220
+ jpg_download = gr.File(label="Download JPG")
221
+ settings_info = gr.HTML(label="Applied Settings")
222
+
223
+ # Process button
224
+ process_btn = gr.Button("Process Image")
225
+ process_btn.click(
226
+ fn=gradio_interface,
227
+ inputs=[
228
+ input_image, settings, crop, remove_bg, resize,
229
+ padding, background, quality
230
+ ],
231
+ outputs=[
232
+ output_image, webp_download, png_download, jpg_download, settings_info
233
+ ]
234
+ )
235
+
236
+ # Examples
237
+ gr.Examples(
238
+ examples=example_images,
239
+ inputs=[
240
+ input_image, settings, crop, remove_bg, resize,
241
+ padding, background, quality
242
+ ]
243
+ )
244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
  if __name__ == "__main__":
247
+ interface.launch()
image_processor.py CHANGED
@@ -84,7 +84,37 @@ def remove_bg_func(image):
84
  return result_image
85
 
86
 
87
- def process_image(image_data, crop=False, remove_bg=False, resize=None, padding=0, background=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  """
89
  Processes a single image based on the provided options.
90
 
@@ -289,3 +319,143 @@ def process_images(input_dir="./input", output_dir="./output", crop=False, remov
289
  print(f"Error processing image {input_path}: {e}")
290
 
291
  print("All images have been processed.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  return result_image
85
 
86
 
87
+ # Update the process_image function to include format selection
88
+ def process_image(img, crop=False, remove_bg=False, resize=None, padding=0, background=None, output_format='webp'):
89
+ """
90
+ Processes a single image with the specified options and format.
91
+
92
+ Args:
93
+ - img: Input image
94
+ - crop: Whether to autocrop
95
+ - remove_bg: Whether to remove background
96
+ - resize: Tuple of (width, height) for resizing
97
+ - padding: Padding to add
98
+ - background: Background color or image
99
+ - output_format: 'webp', 'png', or 'png-transparent'
100
+ """
101
+ # Existing processing code remains the same
102
+ if remove_bg:
103
+ img = remove_bg_func(img)
104
+
105
+ if crop:
106
+ img = autocrop_image(img)
107
+
108
+ if resize:
109
+ img = resize_and_pad_image(img, resize, padding)
110
+
111
+ if background and output_format != 'png-transparent':
112
+ img = add_background(img, background)
113
+
114
+ return img
115
+
116
+
117
+ def process_image2(image_data, crop=False, remove_bg=False, resize=None, padding=0, background=None):
118
  """
119
  Processes a single image based on the provided options.
120
 
 
319
  print(f"Error processing image {input_path}: {e}")
320
 
321
  print("All images have been processed.")
322
+
323
+
324
+
325
+ def save_image_with_format(image, output_path, format='webp', quality=90, custom_filename=None):
326
+ """
327
+ Saves the image in the specified format with appropriate settings.
328
+
329
+ Args:
330
+ - image (PIL.Image.Image): The image to save
331
+ - output_path (str): Base path for the output file (without extension)
332
+ - format (str): 'webp', 'png', 'png-transparent', or 'jpg'
333
+ - quality (int): Quality setting for compression (1-100)
334
+ - custom_filename (str): Optional custom filename for the output
335
+ """
336
+ # Get image dimensions for filename
337
+ width, height = image.size
338
+
339
+ # Generate filename with schema: originalname_size.type
340
+ if custom_filename:
341
+ base_dir = os.path.dirname(output_path)
342
+ filename = f"{custom_filename}_{width}x{height}"
343
+ final_path = os.path.join(base_dir, filename)
344
+ else:
345
+ final_path = output_path
346
+
347
+ if format == 'webp':
348
+ final_path = f"{final_path}.webp"
349
+ image.save(final_path, 'webp', quality=quality)
350
+ elif format == 'png-transparent':
351
+ final_path = f"{final_path}.png"
352
+ image.save(final_path, 'PNG', optimize=True)
353
+ elif format == 'png':
354
+ final_path = f"{final_path}.png"
355
+ if image.mode in ('RGBA', 'LA'):
356
+ background = Image.new('RGB', image.size, 'white')
357
+ background.paste(image, mask=image.split()[-1])
358
+ background.save(final_path, 'PNG', optimize=True)
359
+ else:
360
+ image.save(final_path, 'PNG', optimize=True)
361
+ elif format == 'jpg':
362
+ final_path = f"{final_path}.jpg"
363
+ if image.mode in ('RGBA', 'LA'):
364
+ background = Image.new('RGB', image.size, 'white')
365
+ background.paste(image, mask=image.split()[-1])
366
+ background.save(final_path, 'JPEG', quality=quality, optimize=True)
367
+ else:
368
+ image.convert('RGB').save(final_path, 'JPEG', quality=quality, optimize=True)
369
+ else:
370
+ raise ValueError(f"Unsupported format: {format}")
371
+
372
+ return final_path
373
+
374
+ """
375
+ Saves the image in the specified format with appropriate settings.
376
+
377
+ Args:
378
+ - image (PIL.Image.Image): The image to save
379
+ - output_path (str): Base path for the output file (without extension)
380
+ - format (str): 'webp', 'png', 'png-transparent', or 'jpg'
381
+ - quality (int): Quality setting for compression (1-100)
382
+ - custom_filename (str): Optional custom filename for the output
383
+ """
384
+ # Get image dimensions for filename
385
+ width, height = image.size
386
+
387
+ # Generate the complete filename with path
388
+ if custom_filename:
389
+ base_dir = os.path.dirname(output_path)
390
+ final_path = os.path.join(base_dir, f"{custom_filename}_{width}x{height}")
391
+ else:
392
+ final_path = output_path
393
+
394
+ if format == 'webp':
395
+ final_path = f"{final_path}.webp"
396
+ image.save(final_path, 'webp', quality=quality)
397
+ elif format == 'png-transparent':
398
+ final_path = f"{final_path}.png"
399
+ image.save(final_path, 'PNG', optimize=True)
400
+ elif format == 'png':
401
+ final_path = f"{final_path}.png"
402
+ if image.mode in ('RGBA', 'LA'):
403
+ background = Image.new('RGB', image.size, 'white')
404
+ background.paste(image, mask=image.split()[-1])
405
+ background.save(final_path, 'PNG', optimize=True)
406
+ else:
407
+ image.save(final_path, 'PNG', optimize=True)
408
+ elif format == 'jpg':
409
+ final_path = f"{final_path}.jpg"
410
+ if image.mode in ('RGBA', 'LA'):
411
+ background = Image.new('RGB', image.size, 'white')
412
+ background.paste(image, mask=image.split()[-1])
413
+ background.save(final_path, 'JPEG', quality=quality, optimize=True)
414
+ else:
415
+ image.convert('RGB').save(final_path, 'JPEG', quality=quality, optimize=True)
416
+ else:
417
+ raise ValueError(f"Unsupported format: {format}")
418
+
419
+ return final_path
420
+
421
+ """
422
+ Saves the image in the specified format with appropriate settings.
423
+
424
+ Args:
425
+ - image (PIL.Image.Image): The image to save
426
+ - output_path (str): Base path for the output file (without extension)
427
+ - format (str): 'webp', 'png', 'png-transparent', or 'jpg'
428
+ - quality (int): Quality setting for compression (1-100)
429
+ """
430
+ if format == 'webp':
431
+ # Save as WebP with specified quality
432
+ final_path = f"{output_path}.webp"
433
+ image.save(final_path, 'webp', quality=quality)
434
+ elif format == 'png-transparent':
435
+ # Save as PNG with transparency
436
+ final_path = f"{output_path}.png"
437
+ image.save(final_path, 'PNG', optimize=True)
438
+ elif format == 'png':
439
+ # Save as PNG without transparency, with white background
440
+ final_path = f"{output_path}.png"
441
+ if image.mode in ('RGBA', 'LA'):
442
+ background = Image.new('RGB', image.size, 'white')
443
+ background.paste(image, mask=image.split()[-1])
444
+ background.save(final_path, 'PNG', optimize=True)
445
+ else:
446
+ image.save(final_path, 'PNG', optimize=True)
447
+ elif format == 'jpg':
448
+ # Save as JPG with white background
449
+ final_path = f"{output_path}.jpg"
450
+ if image.mode in ('RGBA', 'LA'):
451
+ # Convert transparent background to white for JPG
452
+ background = Image.new('RGB', image.size, 'white')
453
+ background.paste(image, mask=image.split()[-1])
454
+ background.save(final_path, 'JPEG', quality=quality, optimize=True)
455
+ else:
456
+ # Save directly if no transparency
457
+ image.convert('RGB').save(final_path, 'JPEG', quality=quality, optimize=True)
458
+ else:
459
+ raise ValueError(f"Unsupported format: {format}")
460
+
461
+ return final_path