dramp77 commited on
Commit
9c6e119
·
verified ·
1 Parent(s): 8a8613b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +586 -586
app.py CHANGED
@@ -1,587 +1,587 @@
1
- import os
2
- import zipfile
3
- import shutil
4
- import time
5
- from PIL import Image, ImageDraw
6
- import io
7
- from rembg import remove
8
- import gradio as gr
9
- from concurrent.futures import ThreadPoolExecutor
10
- from transformers import pipeline
11
- import numpy as np
12
- import json
13
- import os
14
-
15
- def remove_background_rembg(input_path):
16
- print(f"Removing background using rembg for image: {input_path}")
17
- with open(input_path, 'rb') as i:
18
- input_image = i.read()
19
- output_image = remove(input_image)
20
- img = Image.open(io.BytesIO(output_image)).convert("RGBA")
21
- return img
22
-
23
- def remove_background_bria(input_path):
24
- print(f"Removing background using bria for image: {input_path}")
25
- pipe = pipeline("image-segmentation", model="briaai/RMBG-1.4", trust_remote_code=True, device=0)
26
- pillow_image = pipe(input_path)
27
- return pillow_image
28
-
29
- def get_bounding_box_with_threshold(image, threshold):
30
- # Convert image to numpy array
31
- img_array = np.array(image)
32
-
33
- # Get alpha channel
34
- alpha = img_array[:,:,3]
35
-
36
- # Find rows and columns where alpha > threshold
37
- rows = np.any(alpha > threshold, axis=1)
38
- cols = np.any(alpha > threshold, axis=0)
39
-
40
- # Find the bounding box
41
- top, bottom = np.where(rows)[0][[0, -1]]
42
- left, right = np.where(cols)[0][[0, -1]]
43
-
44
- if left < right and top < bottom:
45
- return (left, top, right, bottom)
46
- else:
47
- return None
48
-
49
- def position_logic(image_path, canvas_size, padding_top, padding_right, padding_bottom, padding_left, use_threshold=True):
50
- image = Image.open(image_path)
51
- image = image.convert("RGBA")
52
-
53
- # Get the bounding box of the non-blank area with threshold
54
- if use_threshold:
55
- bbox = get_bounding_box_with_threshold(image, threshold=10)
56
- else:
57
- bbox = image.getbbox()
58
- log = []
59
-
60
- if bbox:
61
- # Check 1 pixel around the image for non-transparent pixels
62
- width, height = image.size
63
- cropped_sides = []
64
-
65
- # Define tolerance for transparency
66
- tolerance = 30 # Adjust this value as needed
67
-
68
- # Check top edge
69
- if any(image.getpixel((x, 0))[3] > tolerance for x in range(width)):
70
- cropped_sides.append("top")
71
-
72
- # Check bottom edge
73
- if any(image.getpixel((x, height-1))[3] > tolerance for x in range(width)):
74
- cropped_sides.append("bottom")
75
-
76
- # Check left edge
77
- if any(image.getpixel((0, y))[3] > tolerance for y in range(height)):
78
- cropped_sides.append("left")
79
-
80
- # Check right edge
81
- if any(image.getpixel((width-1, y))[3] > tolerance for y in range(height)):
82
- cropped_sides.append("right")
83
-
84
- if cropped_sides:
85
- info_message = f"Info for {os.path.basename(image_path)}: The following sides of the image may contain cropped objects: {', '.join(cropped_sides)}"
86
- print(info_message)
87
- log.append({"info": info_message})
88
- else:
89
- info_message = f"Info for {os.path.basename(image_path)}: The image is not cropped."
90
- print(info_message)
91
- log.append({"info": info_message})
92
-
93
- # Crop the image to the bounding box
94
- image = image.crop(bbox)
95
- log.append({"action": "crop", "bbox": [str(bbox[0]), str(bbox[1]), str(bbox[2]), str(bbox[3])]})
96
-
97
- # Calculate the new size to expand the image
98
- target_width, target_height = canvas_size
99
- aspect_ratio = image.width / image.height
100
-
101
- if len(cropped_sides) == 4:
102
- # If the image is cropped on all sides, center crop it to fit the canvas
103
- if aspect_ratio > 1: # Landscape
104
- new_height = target_height
105
- new_width = int(new_height * aspect_ratio)
106
- left = (new_width - target_width) // 2
107
- image = image.resize((new_width, new_height), Image.LANCZOS)
108
- image = image.crop((left, 0, left + target_width, target_height))
109
- else: # Portrait or square
110
- new_width = target_width
111
- new_height = int(new_width / aspect_ratio)
112
- top = (new_height - target_height) // 2
113
- image = image.resize((new_width, new_height), Image.LANCZOS)
114
- image = image.crop((0, top, target_width, top + target_height))
115
- log.append({"action": "center_crop_resize", "new_size": f"{target_width}x{target_height}"})
116
- x, y = 0, 0
117
- elif not cropped_sides:
118
- # If the image is not cropped, expand it from center until it touches the padding
119
- new_height = target_height - padding_top - padding_bottom
120
- new_width = int(new_height * aspect_ratio)
121
-
122
- if new_width > target_width - padding_left - padding_right:
123
- # If width exceeds available space, adjust based on width
124
- new_width = target_width - padding_left - padding_right
125
- new_height = int(new_width / aspect_ratio)
126
-
127
- # Resize the image
128
- image = image.resize((new_width, new_height), Image.LANCZOS)
129
- log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
130
-
131
- x = (target_width - new_width) // 2
132
- y = target_height - new_height - padding_bottom
133
- else:
134
- # New logic for handling cropped top and left, or top and right
135
- if set(cropped_sides) == {"top", "left"} or set(cropped_sides) == {"top", "right"}:
136
- new_height = target_height - padding_bottom
137
- new_width = int(new_height * aspect_ratio)
138
-
139
- # If new width exceeds canvas width, adjust based on width
140
- if new_width > target_width:
141
- new_width = target_width
142
- new_height = int(new_width / aspect_ratio)
143
-
144
- # Resize the image
145
- image = image.resize((new_width, new_height), Image.LANCZOS)
146
- log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
147
-
148
- # Set position
149
- if "left" in cropped_sides:
150
- x = 0
151
- else: # right in cropped_sides
152
- x = target_width - new_width
153
- y = 0
154
-
155
- # If the resized image is taller than the canvas minus padding, crop from the bottom
156
- if new_height > target_height - padding_bottom:
157
- crop_bottom = new_height - (target_height - padding_bottom)
158
- image = image.crop((0, 0, new_width, new_height - crop_bottom))
159
- new_height = target_height - padding_bottom
160
- log.append({"action": "crop_vertical", "bottom_pixels_removed": str(crop_bottom)})
161
-
162
- log.append({"action": "position", "x": str(x), "y": str(y)})
163
- elif set(cropped_sides) == {"bottom", "left"} or set(cropped_sides) == {"bottom", "right"}:
164
- # Handle bottom & left or bottom & right cropped images
165
- new_height = target_height - padding_top
166
- new_width = int(new_height * aspect_ratio)
167
-
168
- # If new width exceeds canvas width, adjust based on width
169
- if new_width > target_width - padding_left - padding_right:
170
- new_width = target_width - padding_left - padding_right
171
- new_height = int(new_width / aspect_ratio)
172
-
173
- # Resize the image without cropping or stretching
174
- image = image.resize((new_width, new_height), Image.LANCZOS)
175
- log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
176
-
177
- # Set position
178
- if "left" in cropped_sides:
179
- x = 0
180
- else: # right in cropped_sides
181
- x = target_width - new_width
182
- y = target_height - new_height
183
-
184
- log.append({"action": "position", "x": str(x), "y": str(y)})
185
- elif set(cropped_sides) == {"bottom", "left", "right"}:
186
- # Expand the image from the center
187
- new_width = target_width
188
- new_height = int(new_width / aspect_ratio)
189
-
190
- if new_height < target_height:
191
- new_height = target_height
192
- new_width = int(new_height * aspect_ratio)
193
-
194
- image = image.resize((new_width, new_height), Image.LANCZOS)
195
-
196
- # Crop to fit the canvas
197
- left = (new_width - target_width) // 2
198
- top = 0
199
- image = image.crop((left, top, left + target_width, top + target_height))
200
-
201
- log.append({"action": "expand_and_crop", "new_size": f"{target_width}x{target_height}"})
202
- x, y = 0, 0
203
- elif cropped_sides == ["top"]:
204
- # New logic for handling only top-cropped images
205
- if image.width > image.height:
206
- new_width = target_width
207
- new_height = int(target_width / aspect_ratio)
208
- else:
209
- new_height = target_height - padding_bottom
210
- new_width = int(new_height * aspect_ratio)
211
-
212
- # Resize the image
213
- image = image.resize((new_width, new_height), Image.LANCZOS)
214
- log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
215
-
216
- x = (target_width - new_width) // 2
217
- y = 0 # Align to top
218
-
219
- # Apply padding only to non-cropped sides
220
- x = max(padding_left, min(x, target_width - new_width - padding_right))
221
- elif cropped_sides in [["right"], ["left"]]:
222
- # New logic for handling only right-cropped or left-cropped images
223
- if image.width > image.height:
224
- new_width = target_width - max(padding_left, padding_right)
225
- new_height = int(new_width / aspect_ratio)
226
- else:
227
- new_height = target_height - padding_top - padding_bottom
228
- new_width = int(new_height * aspect_ratio)
229
-
230
- # Resize the image
231
- image = image.resize((new_width, new_height), Image.LANCZOS)
232
- log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
233
-
234
- if cropped_sides == ["right"]:
235
- x = target_width - new_width # Align to right
236
- else: # cropped_sides == ["left"]
237
- x = 0 # Align to left
238
- y = target_height - new_height - padding_bottom # Respect bottom padding
239
-
240
- # Ensure top padding is respected
241
- if y < padding_top:
242
- y = padding_top
243
-
244
- log.append({"action": "position", "x": str(x), "y": str(y)})
245
- elif set(cropped_sides) == {"left", "right"}:
246
- # Logic for handling images cropped on both left and right sides
247
- new_width = target_width # Expand to full width of canvas
248
-
249
- # Calculate the aspect ratio of the original image
250
- aspect_ratio = image.width / image.height
251
-
252
- # Calculate the new height while maintaining aspect ratio
253
- new_height = int(new_width / aspect_ratio)
254
-
255
- # Resize the image
256
- image = image.resize((new_width, new_height), Image.LANCZOS)
257
- log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
258
-
259
- # Set horizontal position (always 0 as it spans full width)
260
- x = 0
261
-
262
- # Calculate vertical position to respect bottom padding
263
- y = target_height - new_height - padding_bottom
264
-
265
- # If the resized image is taller than the canvas, crop from the top only
266
- if new_height > target_height - padding_bottom:
267
- crop_top = new_height - (target_height - padding_bottom)
268
- image = image.crop((0, crop_top, new_width, new_height))
269
- new_height = target_height - padding_bottom
270
- y = 0
271
- log.append({"action": "crop_vertical", "top_pixels_removed": str(crop_top)})
272
- else:
273
- # Align the image to the bottom with padding
274
- y = target_height - new_height - padding_bottom
275
-
276
- log.append({"action": "position", "x": str(x), "y": str(y)})
277
- elif cropped_sides == ["bottom"]:
278
- # Logic for handling images cropped on the bottom side
279
- # Calculate the aspect ratio of the original image
280
- aspect_ratio = image.width / image.height
281
-
282
- if aspect_ratio < 1: # Portrait orientation
283
- new_height = target_height - padding_top # Full height with top padding
284
- new_width = int(new_height * aspect_ratio)
285
-
286
- # If the new width exceeds the canvas width, adjust it
287
- if new_width > target_width:
288
- new_width = target_width
289
- new_height = int(new_width / aspect_ratio)
290
- else: # Landscape orientation
291
- new_width = target_width - padding_left - padding_right
292
- new_height = int(new_width / aspect_ratio)
293
-
294
- # If the new height exceeds the canvas height, adjust it
295
- if new_height > target_height:
296
- new_height = target_height
297
- new_width = int(new_height * aspect_ratio)
298
-
299
- # Resize the image
300
- image = image.resize((new_width, new_height), Image.LANCZOS)
301
- log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
302
-
303
- # Set horizontal position (centered)
304
- x = (target_width - new_width) // 2
305
-
306
- # Set vertical position (touching bottom edge for all cases)
307
- y = target_height - new_height
308
-
309
- log.append({"action": "position", "x": str(x), "y": str(y)})
310
- else:
311
- # Use the original resizing logic for other partially cropped images
312
- if image.width > image.height:
313
- new_width = target_width
314
- new_height = int(target_width / aspect_ratio)
315
- else:
316
- new_height = target_height
317
- new_width = int(target_height * aspect_ratio)
318
-
319
- # Resize the image
320
- image = image.resize((new_width, new_height), Image.LANCZOS)
321
- log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
322
-
323
- # Center horizontally for all images
324
- x = (target_width - new_width) // 2
325
- y = target_height - new_height - padding_bottom
326
-
327
- # Adjust positions for cropped sides
328
- if "top" in cropped_sides:
329
- y = 0
330
- elif "bottom" in cropped_sides:
331
- y = target_height - new_height
332
- if "left" in cropped_sides:
333
- x = 0
334
- elif "right" in cropped_sides:
335
- x = target_width - new_width
336
-
337
- # Apply padding only to non-cropped sides, but keep horizontal centering
338
- if "left" not in cropped_sides and "right" not in cropped_sides:
339
- x = (target_width - new_width) // 2 # Always center horizontally
340
- if "top" not in cropped_sides and "bottom" not in cropped_sides:
341
- y = max(padding_top, min(y, target_height - new_height - padding_bottom))
342
-
343
- return log, image, x, y
344
-
345
- def process_single_image(image_path, output_folder, bg_method, canvas_size_name, output_format, bg_choice, custom_color, watermark_path=None, twibbon_path=None):
346
- add_padding_line = False
347
-
348
- if canvas_size_name == 'Rox':
349
- canvas_size = (1080, 1080)
350
- padding_top = 112
351
- padding_right = 125
352
- padding_bottom = 116
353
- padding_left = 125
354
- elif canvas_size_name == 'Columbia':
355
- canvas_size = (730, 610)
356
- padding_top = 30
357
- padding_right = 105
358
- padding_bottom = 35
359
- padding_left = 105
360
- elif canvas_size_name == 'Zalora':
361
- canvas_size = (763, 1100)
362
- padding_top = 50
363
- padding_right = 50
364
- padding_bottom = 200
365
- padding_left = 50
366
-
367
-
368
- filename = os.path.basename(image_path)
369
- try:
370
- print(f"Processing image: {filename}")
371
- if bg_method == 'rembg':
372
- image_with_no_bg = remove_background_rembg(image_path)
373
- elif bg_method == 'bria':
374
- image_with_no_bg = remove_background_bria(image_path)
375
-
376
- temp_image_path = os.path.join(output_folder, f"temp_{filename}")
377
- image_with_no_bg.save(temp_image_path, format='PNG')
378
-
379
- log, new_image, x, y = position_logic(temp_image_path, canvas_size, padding_top, padding_right, padding_bottom, padding_left)
380
-
381
- # Create a new canvas with the appropriate background
382
- if bg_choice == 'white':
383
- canvas = Image.new("RGBA", canvas_size, "WHITE")
384
- elif bg_choice == 'custom':
385
- canvas = Image.new("RGBA", canvas_size, custom_color)
386
- else: # transparent
387
- canvas = Image.new("RGBA", canvas_size, (0, 0, 0, 0))
388
-
389
- # Paste the resized image onto the canvas
390
- canvas.paste(new_image, (x, y), new_image)
391
- log.append({"action": "paste", "position": [str(x), str(y)]})
392
-
393
- # Add visible black line for padding when background is not transparent
394
- if add_padding_line:
395
- draw = ImageDraw.Draw(canvas)
396
- draw.rectangle([padding_left, padding_top, canvas_size[0] - padding_right, canvas_size[1] - padding_bottom], outline="black", width=5)
397
- log.append({"action": "add_padding_line"})
398
-
399
- output_ext = 'jpg' if output_format == 'JPG' else 'png'
400
- output_filename = f"{os.path.splitext(filename)[0]}.{output_ext}"
401
- output_path = os.path.join(output_folder, output_filename)
402
-
403
- # Apply watermark only if the filename ends with "_01" and watermark_path is provided
404
- if os.path.splitext(filename)[0].endswith("_01") and watermark_path:
405
- watermark = Image.open(watermark_path).convert("RGBA")
406
- canvas.paste(watermark, (0, 0), watermark)
407
- log.append({"action": "add_watermark"})
408
-
409
- # Apply twibbon only if the filename ends with "_twb" and twibbon_path is provided
410
- if twibbon_path:
411
- twibbon = Image.open(twibbon_path).convert("RGBA")
412
- canvas.paste(twibbon, (0, 0), twibbon)
413
- log.append({"action": "twibbon"})
414
-
415
- if output_format == 'JPG':
416
- canvas.convert('RGB').save(output_path, format='JPEG')
417
- else:
418
- canvas.save(output_path, format='PNG')
419
-
420
- os.remove(temp_image_path)
421
-
422
- print(f"Processed image path: {output_path}")
423
- return [(output_path, image_path)], log
424
-
425
- except Exception as e:
426
- print(f"Error processing {filename}: {e}")
427
- return None, None
428
-
429
- def process_images(input_files, bg_method='rembg', watermark_path=None, twibbon_path=None, canvas_size='Rox', output_format='PNG', bg_choice='transparent', custom_color="#ffffff", num_workers=4, progress=gr.Progress()):
430
- start_time = time.time()
431
-
432
- output_folder = "processed_images"
433
- if os.path.exists(output_folder):
434
- shutil.rmtree(output_folder)
435
- os.makedirs(output_folder)
436
-
437
- processed_images = []
438
- original_images = []
439
- all_logs = []
440
-
441
- if isinstance(input_files, str) and input_files.lower().endswith(('.zip', '.rar')):
442
- # Handle zip file
443
- input_folder = "temp_input"
444
- if os.path.exists(input_folder):
445
- shutil.rmtree(input_folder)
446
- os.makedirs(input_folder)
447
-
448
- try:
449
- with zipfile.ZipFile(input_files, 'r') as zip_ref:
450
- zip_ref.extractall(input_folder)
451
- except zipfile.BadZipFile as e:
452
- print(f"Error extracting zip file: {e}")
453
- return [], None, 0
454
-
455
- image_files = [os.path.join(input_folder, f) for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.webp'))]
456
- elif isinstance(input_files, list):
457
- # Handle multiple files
458
- image_files = input_files
459
- else:
460
- # Handle single file
461
- image_files = [input_files]
462
-
463
- total_images = len(image_files)
464
- print(f"Total images to process: {total_images}")
465
-
466
- avg_processing_time = 0
467
- with ThreadPoolExecutor(max_workers=num_workers) as executor:
468
- future_to_image = {executor.submit(process_single_image, image_path, output_folder, bg_method, canvas_size, output_format, bg_choice, custom_color, watermark_path, twibbon_path): image_path for image_path in image_files}
469
- for idx, future in enumerate(future_to_image):
470
- try:
471
- start_time_image = time.time()
472
- result, log = future.result()
473
- end_time_image = time.time()
474
- image_processing_time = end_time_image - start_time_image
475
-
476
- # Update average processing time
477
- avg_processing_time = (avg_processing_time * idx + image_processing_time) / (idx + 1)
478
-
479
- if result:
480
- processed_images.extend(result)
481
- original_images.append(future_to_image[future])
482
- all_logs.append({os.path.basename(future_to_image[future]): log})
483
-
484
- # Estimate remaining time
485
- remaining_images = total_images - (idx + 1)
486
- estimated_remaining_time = remaining_images * avg_processing_time
487
-
488
- progress((idx + 1) / total_images, f"{idx + 1}/{total_images} images processed. Estimated time remaining: {estimated_remaining_time:.2f} seconds")
489
- except Exception as e:
490
- print(f"Error processing image {future_to_image[future]}: {e}")
491
-
492
- output_zip_path = "processed_images.zip"
493
- with zipfile.ZipFile(output_zip_path, 'w') as zipf:
494
- for file, _ in processed_images:
495
- zipf.write(file, os.path.basename(file))
496
-
497
- # Write the comprehensive log for all images
498
- with open(os.path.join(output_folder, 'process_log.json'), 'w') as log_file:
499
- json.dump(all_logs, log_file, indent=4)
500
- print("Comprehensive log saved to", os.path.join(output_folder, 'process_log.json'))
501
-
502
- end_time = time.time()
503
- processing_time = end_time - start_time
504
- print(f"Processing time: {processing_time} seconds")
505
-
506
- return original_images, processed_images, output_zip_path, processing_time
507
-
508
- def gradio_interface(input_files, bg_method, watermark, twibbon, canvas_size, output_format, bg_choice, custom_color, num_workers):
509
- progress = gr.Progress()
510
- watermark_path = watermark.name if watermark else None
511
- twibbon_path = twibbon.name if twibbon else None
512
-
513
- # Check input_files, is it single image, list image, or zip/rar
514
- if isinstance(input_files, str) and input_files.lower().endswith(('.zip', '.rar')):
515
- return process_images(input_files, bg_method, watermark_path, twibbon_path, canvas_size, output_format, bg_choice, custom_color, num_workers, progress)
516
- elif isinstance(input_files, list):
517
- return process_images(input_files, bg_method, watermark_path, twibbon_path, canvas_size, output_format, bg_choice, custom_color, num_workers, progress)
518
- else:
519
- return process_images(input_files.name, bg_method, watermark_path, twibbon_path, canvas_size, output_format, bg_choice, custom_color, num_workers, progress)
520
-
521
- def show_color_picker(bg_choice):
522
- if bg_choice == 'custom':
523
- return gr.update(visible=True)
524
- return gr.update(visible=False)
525
-
526
- def update_compare(evt: gr.SelectData):
527
- if isinstance(evt.value, dict) and 'caption' in evt.value:
528
- input_path = evt.value['caption']
529
- output_path = evt.value['image']['path']
530
- input_path = input_path.split("Input: ")[-1]
531
-
532
- # Open the original and processed images
533
- original_img = Image.open(input_path)
534
- processed_img = Image.open(output_path)
535
-
536
- # Calculate the aspect ratios
537
- original_ratio = f"{original_img.width}x{original_img.height}"
538
- processed_ratio = f"{processed_img.width}x{processed_img.height}"
539
-
540
- return gr.update(value=input_path), gr.update(value=output_path), gr.update(value=original_ratio), gr.update(value=processed_ratio)
541
- else:
542
- print("No caption found in selection")
543
- return gr.update(value=None), gr.update(value=None), gr.update(value=None), gr.update(value=None)
544
-
545
- def process(input_files, bg_method, watermark, twibbon, canvas_size, output_format, bg_choice, custom_color, num_workers):
546
- _, processed_images, zip_path, time_taken = gradio_interface(input_files, bg_method, watermark, twibbon, canvas_size, output_format, bg_choice, custom_color, num_workers)
547
- processed_images_with_captions = [(img, f"Input: {caption}") for img, caption in processed_images]
548
- return processed_images_with_captions, zip_path, f"{time_taken:.2f} seconds"
549
-
550
- with gr.Blocks(theme="NoCrypt/[email protected]") as iface:
551
- gr.Markdown("# Image Background Removal, Resizing with Optional Watermark, twibbon")
552
- gr.Markdown("Choose to upload multiple images or a ZIP/RAR file, select the crop mode, optionally upload a watermark image, and choose the output format.")
553
-
554
- with gr.Row():
555
- input_files = gr.File(label="Upload Image or ZIP/RAR file", file_types=[".zip", ".rar", "image"], interactive=True)
556
- watermark = gr.File(label="Upload Watermark Image (Optional)", file_types=[".png"])
557
- twibbon = gr.File(label="Upload twibbon Image (Optional)", file_types=[".png"])
558
-
559
- with gr.Row():
560
- canvas_size = gr.Radio(choices=["Rox", "Columbia", "Zalora"], label="Canvas Size", value="Rox")
561
- output_format = gr.Radio(choices=["PNG", "JPG"], label="Output Format", value="JPG")
562
- num_workers = gr.Slider(minimum=1, maximum=16, step=1, label="Number of Workers", value=5)
563
-
564
- with gr.Row():
565
- bg_method = gr.Radio(choices=["bria", "rembg"], label="Background Removal Method", value="bria")
566
- bg_choice = gr.Radio(choices=["transparent", "white", "custom"], label="Background Choice", value="white")
567
- custom_color = gr.ColorPicker(label="Custom Background Color", value="#ffffff", visible=False)
568
-
569
- process_button = gr.Button("Process Images")
570
-
571
- with gr.Row():
572
- gallery_processed = gr.Gallery(label="Processed Images")
573
- with gr.Row():
574
- image_original = gr.Image(label="Original Images", interactive=False)
575
- image_processed = gr.Image(label="Processed Images", interactive=False)
576
- with gr.Row():
577
- original_ratio = gr.Textbox(label="Original Ratio")
578
- processed_ratio = gr.Textbox(label="Processed Ratio")
579
- with gr.Row():
580
- output_zip = gr.File(label="Download Processed Images as ZIP")
581
- processing_time = gr.Textbox(label="Processing Time (seconds)")
582
-
583
- bg_choice.change(show_color_picker, inputs=bg_choice, outputs=custom_color)
584
- process_button.click(process, inputs=[input_files, bg_method, watermark, twibbon, canvas_size, output_format, bg_choice, custom_color, num_workers], outputs=[gallery_processed, output_zip, processing_time])
585
- gallery_processed.select(update_compare, outputs=[image_original, image_processed, original_ratio, processed_ratio])
586
-
587
  iface.launch(share=True)
 
1
+ import os
2
+ import zipfile
3
+ import shutil
4
+ import time
5
+ from PIL import Image, ImageDraw
6
+ import io
7
+ from rembg import remove
8
+ import gradio as gr
9
+ from concurrent.futures import ThreadPoolExecutor
10
+ from transformers import pipeline
11
+ import numpy as np
12
+ import json
13
+ import os
14
+
15
+ def remove_background_rembg(input_path):
16
+ print(f"Removing background using rembg for image: {input_path}")
17
+ with open(input_path, 'rb') as i:
18
+ input_image = i.read()
19
+ output_image = remove(input_image)
20
+ img = Image.open(io.BytesIO(output_image)).convert("RGBA")
21
+ return img
22
+
23
+ def remove_background_bria(input_path):
24
+ print(f"Removing background using bria for image: {input_path}")
25
+ pipe = pipeline("image-segmentation", model="briaai/RMBG-1.4", trust_remote_code=True, device=0)
26
+ pillow_image = pipe(input_path)
27
+ return pillow_image
28
+
29
+ def get_bounding_box_with_threshold(image, threshold):
30
+ # Convert image to numpy array
31
+ img_array = np.array(image)
32
+
33
+ # Get alpha channel
34
+ alpha = img_array[:,:,3]
35
+
36
+ # Find rows and columns where alpha > threshold
37
+ rows = np.any(alpha > threshold, axis=1)
38
+ cols = np.any(alpha > threshold, axis=0)
39
+
40
+ # Find the bounding box
41
+ top, bottom = np.where(rows)[0][[0, -1]]
42
+ left, right = np.where(cols)[0][[0, -1]]
43
+
44
+ if left < right and top < bottom:
45
+ return (left, top, right, bottom)
46
+ else:
47
+ return None
48
+
49
+ def position_logic(image_path, canvas_size, padding_top, padding_right, padding_bottom, padding_left, use_threshold=True):
50
+ image = Image.open(image_path)
51
+ image = image.convert("RGBA")
52
+
53
+ # Get the bounding box of the non-blank area with threshold
54
+ if use_threshold:
55
+ bbox = get_bounding_box_with_threshold(image, threshold=10)
56
+ else:
57
+ bbox = image.getbbox()
58
+ log = []
59
+
60
+ if bbox:
61
+ # Check 1 pixel around the image for non-transparent pixels
62
+ width, height = image.size
63
+ cropped_sides = []
64
+
65
+ # Define tolerance for transparency
66
+ tolerance = 30 # Adjust this value as needed
67
+
68
+ # Check top edge
69
+ if any(image.getpixel((x, 0))[3] > tolerance for x in range(width)):
70
+ cropped_sides.append("top")
71
+
72
+ # Check bottom edge
73
+ if any(image.getpixel((x, height-1))[3] > tolerance for x in range(width)):
74
+ cropped_sides.append("bottom")
75
+
76
+ # Check left edge
77
+ if any(image.getpixel((0, y))[3] > tolerance for y in range(height)):
78
+ cropped_sides.append("left")
79
+
80
+ # Check right edge
81
+ if any(image.getpixel((width-1, y))[3] > tolerance for y in range(height)):
82
+ cropped_sides.append("right")
83
+
84
+ if cropped_sides:
85
+ info_message = f"Info for {os.path.basename(image_path)}: The following sides of the image may contain cropped objects: {', '.join(cropped_sides)}"
86
+ print(info_message)
87
+ log.append({"info": info_message})
88
+ else:
89
+ info_message = f"Info for {os.path.basename(image_path)}: The image is not cropped."
90
+ print(info_message)
91
+ log.append({"info": info_message})
92
+
93
+ # Crop the image to the bounding box
94
+ image = image.crop(bbox)
95
+ log.append({"action": "crop", "bbox": [str(bbox[0]), str(bbox[1]), str(bbox[2]), str(bbox[3])]})
96
+
97
+ # Calculate the new size to expand the image
98
+ target_width, target_height = canvas_size
99
+ aspect_ratio = image.width / image.height
100
+
101
+ if len(cropped_sides) == 4:
102
+ # If the image is cropped on all sides, center crop it to fit the canvas
103
+ if aspect_ratio > 1: # Landscape
104
+ new_height = target_height
105
+ new_width = int(new_height * aspect_ratio)
106
+ left = (new_width - target_width) // 2
107
+ image = image.resize((new_width, new_height), Image.LANCZOS)
108
+ image = image.crop((left, 0, left + target_width, target_height))
109
+ else: # Portrait or square
110
+ new_width = target_width
111
+ new_height = int(new_width / aspect_ratio)
112
+ top = (new_height - target_height) // 2
113
+ image = image.resize((new_width, new_height), Image.LANCZOS)
114
+ image = image.crop((0, top, target_width, top + target_height))
115
+ log.append({"action": "center_crop_resize", "new_size": f"{target_width}x{target_height}"})
116
+ x, y = 0, 0
117
+ elif not cropped_sides:
118
+ # If the image is not cropped, expand it from center until it touches the padding
119
+ new_height = target_height - padding_top - padding_bottom
120
+ new_width = int(new_height * aspect_ratio)
121
+
122
+ if new_width > target_width - padding_left - padding_right:
123
+ # If width exceeds available space, adjust based on width
124
+ new_width = target_width - padding_left - padding_right
125
+ new_height = int(new_width / aspect_ratio)
126
+
127
+ # Resize the image
128
+ image = image.resize((new_width, new_height), Image.LANCZOS)
129
+ log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
130
+
131
+ x = (target_width - new_width) // 2
132
+ y = target_height - new_height - padding_bottom
133
+ else:
134
+ # New logic for handling cropped top and left, or top and right
135
+ if set(cropped_sides) == {"top", "left"} or set(cropped_sides) == {"top", "right"}:
136
+ new_height = target_height - padding_bottom
137
+ new_width = int(new_height * aspect_ratio)
138
+
139
+ # If new width exceeds canvas width, adjust based on width
140
+ if new_width > target_width:
141
+ new_width = target_width
142
+ new_height = int(new_width / aspect_ratio)
143
+
144
+ # Resize the image
145
+ image = image.resize((new_width, new_height), Image.LANCZOS)
146
+ log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
147
+
148
+ # Set position
149
+ if "left" in cropped_sides:
150
+ x = 0
151
+ else: # right in cropped_sides
152
+ x = target_width - new_width
153
+ y = 0
154
+
155
+ # If the resized image is taller than the canvas minus padding, crop from the bottom
156
+ if new_height > target_height - padding_bottom:
157
+ crop_bottom = new_height - (target_height - padding_bottom)
158
+ image = image.crop((0, 0, new_width, new_height - crop_bottom))
159
+ new_height = target_height - padding_bottom
160
+ log.append({"action": "crop_vertical", "bottom_pixels_removed": str(crop_bottom)})
161
+
162
+ log.append({"action": "position", "x": str(x), "y": str(y)})
163
+ elif set(cropped_sides) == {"bottom", "left"} or set(cropped_sides) == {"bottom", "right"}:
164
+ # Handle bottom & left or bottom & right cropped images
165
+ new_height = target_height - padding_top
166
+ new_width = int(new_height * aspect_ratio)
167
+
168
+ # If new width exceeds canvas width, adjust based on width
169
+ if new_width > target_width - padding_left - padding_right:
170
+ new_width = target_width - padding_left - padding_right
171
+ new_height = int(new_width / aspect_ratio)
172
+
173
+ # Resize the image without cropping or stretching
174
+ image = image.resize((new_width, new_height), Image.LANCZOS)
175
+ log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
176
+
177
+ # Set position
178
+ if "left" in cropped_sides:
179
+ x = 0
180
+ else: # right in cropped_sides
181
+ x = target_width - new_width
182
+ y = target_height - new_height
183
+
184
+ log.append({"action": "position", "x": str(x), "y": str(y)})
185
+ elif set(cropped_sides) == {"bottom", "left", "right"}:
186
+ # Expand the image from the center
187
+ new_width = target_width
188
+ new_height = int(new_width / aspect_ratio)
189
+
190
+ if new_height < target_height:
191
+ new_height = target_height
192
+ new_width = int(new_height * aspect_ratio)
193
+
194
+ image = image.resize((new_width, new_height), Image.LANCZOS)
195
+
196
+ # Crop to fit the canvas
197
+ left = (new_width - target_width) // 2
198
+ top = 0
199
+ image = image.crop((left, top, left + target_width, top + target_height))
200
+
201
+ log.append({"action": "expand_and_crop", "new_size": f"{target_width}x{target_height}"})
202
+ x, y = 0, 0
203
+ elif cropped_sides == ["top"]:
204
+ # New logic for handling only top-cropped images
205
+ if image.width > image.height:
206
+ new_width = target_width
207
+ new_height = int(target_width / aspect_ratio)
208
+ else:
209
+ new_height = target_height - padding_bottom
210
+ new_width = int(new_height * aspect_ratio)
211
+
212
+ # Resize the image
213
+ image = image.resize((new_width, new_height), Image.LANCZOS)
214
+ log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
215
+
216
+ x = (target_width - new_width) // 2
217
+ y = 0 # Align to top
218
+
219
+ # Apply padding only to non-cropped sides
220
+ x = max(padding_left, min(x, target_width - new_width - padding_right))
221
+ elif cropped_sides in [["right"], ["left"]]:
222
+ # New logic for handling only right-cropped or left-cropped images
223
+ if image.width > image.height:
224
+ new_width = target_width - max(padding_left, padding_right)
225
+ new_height = int(new_width / aspect_ratio)
226
+ else:
227
+ new_height = target_height - padding_top - padding_bottom
228
+ new_width = int(new_height * aspect_ratio)
229
+
230
+ # Resize the image
231
+ image = image.resize((new_width, new_height), Image.LANCZOS)
232
+ log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
233
+
234
+ if cropped_sides == ["right"]:
235
+ x = target_width - new_width # Align to right
236
+ else: # cropped_sides == ["left"]
237
+ x = 0 # Align to left
238
+ y = target_height - new_height - padding_bottom # Respect bottom padding
239
+
240
+ # Ensure top padding is respected
241
+ if y < padding_top:
242
+ y = padding_top
243
+
244
+ log.append({"action": "position", "x": str(x), "y": str(y)})
245
+ elif set(cropped_sides) == {"left", "right"}:
246
+ # Logic for handling images cropped on both left and right sides
247
+ new_width = target_width # Expand to full width of canvas
248
+
249
+ # Calculate the aspect ratio of the original image
250
+ aspect_ratio = image.width / image.height
251
+
252
+ # Calculate the new height while maintaining aspect ratio
253
+ new_height = int(new_width / aspect_ratio)
254
+
255
+ # Resize the image
256
+ image = image.resize((new_width, new_height), Image.LANCZOS)
257
+ log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
258
+
259
+ # Set horizontal position (always 0 as it spans full width)
260
+ x = 0
261
+
262
+ # Calculate vertical position to respect bottom padding
263
+ y = target_height - new_height - padding_bottom
264
+
265
+ # If the resized image is taller than the canvas, crop from the top only
266
+ if new_height > target_height - padding_bottom:
267
+ crop_top = new_height - (target_height - padding_bottom)
268
+ image = image.crop((0, crop_top, new_width, new_height))
269
+ new_height = target_height - padding_bottom
270
+ y = 0
271
+ log.append({"action": "crop_vertical", "top_pixels_removed": str(crop_top)})
272
+ else:
273
+ # Align the image to the bottom with padding
274
+ y = target_height - new_height - padding_bottom
275
+
276
+ log.append({"action": "position", "x": str(x), "y": str(y)})
277
+ elif cropped_sides == ["bottom"]:
278
+ # Logic for handling images cropped on the bottom side
279
+ # Calculate the aspect ratio of the original image
280
+ aspect_ratio = image.width / image.height
281
+
282
+ if aspect_ratio < 1: # Portrait orientation
283
+ new_height = target_height - padding_top # Full height with top padding
284
+ new_width = int(new_height * aspect_ratio)
285
+
286
+ # If the new width exceeds the canvas width, adjust it
287
+ if new_width > target_width:
288
+ new_width = target_width
289
+ new_height = int(new_width / aspect_ratio)
290
+ else: # Landscape orientation
291
+ new_width = target_width - padding_left - padding_right
292
+ new_height = int(new_width / aspect_ratio)
293
+
294
+ # If the new height exceeds the canvas height, adjust it
295
+ if new_height > target_height:
296
+ new_height = target_height
297
+ new_width = int(new_height * aspect_ratio)
298
+
299
+ # Resize the image
300
+ image = image.resize((new_width, new_height), Image.LANCZOS)
301
+ log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
302
+
303
+ # Set horizontal position (centered)
304
+ x = (target_width - new_width) // 2
305
+
306
+ # Set vertical position (touching bottom edge for all cases)
307
+ y = target_height - new_height
308
+
309
+ log.append({"action": "position", "x": str(x), "y": str(y)})
310
+ else:
311
+ # Use the original resizing logic for other partially cropped images
312
+ if image.width > image.height:
313
+ new_width = target_width
314
+ new_height = int(target_width / aspect_ratio)
315
+ else:
316
+ new_height = target_height
317
+ new_width = int(target_height * aspect_ratio)
318
+
319
+ # Resize the image
320
+ image = image.resize((new_width, new_height), Image.LANCZOS)
321
+ log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
322
+
323
+ # Center horizontally for all images
324
+ x = (target_width - new_width) // 2
325
+ y = target_height - new_height - padding_bottom
326
+
327
+ # Adjust positions for cropped sides
328
+ if "top" in cropped_sides:
329
+ y = 0
330
+ elif "bottom" in cropped_sides:
331
+ y = target_height - new_height
332
+ if "left" in cropped_sides:
333
+ x = 0
334
+ elif "right" in cropped_sides:
335
+ x = target_width - new_width
336
+
337
+ # Apply padding only to non-cropped sides, but keep horizontal centering
338
+ if "left" not in cropped_sides and "right" not in cropped_sides:
339
+ x = (target_width - new_width) // 2 # Always center horizontally
340
+ if "top" not in cropped_sides and "bottom" not in cropped_sides:
341
+ y = max(padding_top, min(y, target_height - new_height - padding_bottom))
342
+
343
+ return log, image, x, y
344
+
345
+ def process_single_image(image_path, output_folder, bg_method, canvas_size_name, output_format, bg_choice, custom_color, watermark_path=None, twibbon_path=None):
346
+ add_padding_line = False
347
+
348
+ if canvas_size_name == 'Rox':
349
+ canvas_size = (1080, 1080)
350
+ padding_top = 112
351
+ padding_right = 125
352
+ padding_bottom = 116
353
+ padding_left = 125
354
+ elif canvas_size_name == 'Columbia':
355
+ canvas_size = (730, 610)
356
+ padding_top = 30
357
+ padding_right = 105
358
+ padding_bottom = 35
359
+ padding_left = 105
360
+ elif canvas_size_name == 'Zalora':
361
+ canvas_size = (763, 1100)
362
+ padding_top = 50
363
+ padding_right = 50
364
+ padding_bottom = 200
365
+ padding_left = 50
366
+
367
+
368
+ filename = os.path.basename(image_path)
369
+ try:
370
+ print(f"Processing image: {filename}")
371
+ if bg_method == 'rembg':
372
+ image_with_no_bg = remove_background_rembg(image_path)
373
+ elif bg_method == 'bria':
374
+ image_with_no_bg = remove_background_bria(image_path)
375
+
376
+ temp_image_path = os.path.join(output_folder, f"temp_{filename}")
377
+ image_with_no_bg.save(temp_image_path, format='PNG')
378
+
379
+ log, new_image, x, y = position_logic(temp_image_path, canvas_size, padding_top, padding_right, padding_bottom, padding_left)
380
+
381
+ # Create a new canvas with the appropriate background
382
+ if bg_choice == 'white':
383
+ canvas = Image.new("RGBA", canvas_size, "WHITE")
384
+ elif bg_choice == 'custom':
385
+ canvas = Image.new("RGBA", canvas_size, custom_color)
386
+ else: # transparent
387
+ canvas = Image.new("RGBA", canvas_size, (0, 0, 0, 0))
388
+
389
+ # Paste the resized image onto the canvas
390
+ canvas.paste(new_image, (x, y), new_image)
391
+ log.append({"action": "paste", "position": [str(x), str(y)]})
392
+
393
+ # Add visible black line for padding when background is not transparent
394
+ if add_padding_line:
395
+ draw = ImageDraw.Draw(canvas)
396
+ draw.rectangle([padding_left, padding_top, canvas_size[0] - padding_right, canvas_size[1] - padding_bottom], outline="black", width=5)
397
+ log.append({"action": "add_padding_line"})
398
+
399
+ output_ext = 'jpg' if output_format == 'JPG' else 'png'
400
+ output_filename = f"{os.path.splitext(filename)[0]}.{output_ext}"
401
+ output_path = os.path.join(output_folder, output_filename)
402
+
403
+ # Apply watermark only if the filename ends with "_01" and watermark_path is provided
404
+ if os.path.splitext(filename)[0].endswith("_01") and watermark_path:
405
+ watermark = Image.open(watermark_path).convert("RGBA")
406
+ canvas.paste(watermark, (0, 0), watermark)
407
+ log.append({"action": "add_watermark"})
408
+
409
+ # Apply twibbon only if the filename ends with "_twb" and twibbon_path is provided
410
+ if twibbon_path:
411
+ twibbon = Image.open(twibbon_path).convert("RGBA")
412
+ canvas.paste(twibbon, (0, 0), twibbon)
413
+ log.append({"action": "twibbon"})
414
+
415
+ if output_format == 'JPG':
416
+ canvas.convert('RGB').save(output_path, format='JPEG')
417
+ else:
418
+ canvas.save(output_path, format='PNG')
419
+
420
+ os.remove(temp_image_path)
421
+
422
+ print(f"Processed image path: {output_path}")
423
+ return [(output_path, image_path)], log
424
+
425
+ except Exception as e:
426
+ print(f"Error processing {filename}: {e}")
427
+ return None, None
428
+
429
+ def process_images(input_files, bg_method='rembg', watermark_path=None, twibbon_path=None, canvas_size='Rox', output_format='PNG', bg_choice='transparent', custom_color="#ffffff", num_workers=4, progress=gr.Progress()):
430
+ start_time = time.time()
431
+
432
+ output_folder = "processed_images"
433
+ if os.path.exists(output_folder):
434
+ shutil.rmtree(output_folder)
435
+ os.makedirs(output_folder)
436
+
437
+ processed_images = []
438
+ original_images = []
439
+ all_logs = []
440
+
441
+ if isinstance(input_files, str) and input_files.lower().endswith(('.zip', '.rar')):
442
+ # Handle zip file
443
+ input_folder = "temp_input"
444
+ if os.path.exists(input_folder):
445
+ shutil.rmtree(input_folder)
446
+ os.makedirs(input_folder)
447
+
448
+ try:
449
+ with zipfile.ZipFile(input_files, 'r') as zip_ref:
450
+ zip_ref.extractall(input_folder)
451
+ except zipfile.BadZipFile as e:
452
+ print(f"Error extracting zip file: {e}")
453
+ return [], None, 0
454
+
455
+ image_files = [os.path.join(input_folder, f) for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.webp'))]
456
+ elif isinstance(input_files, list):
457
+ # Handle multiple files
458
+ image_files = input_files
459
+ else:
460
+ # Handle single file
461
+ image_files = [input_files]
462
+
463
+ total_images = len(image_files)
464
+ print(f"Total images to process: {total_images}")
465
+
466
+ avg_processing_time = 0
467
+ with ThreadPoolExecutor(max_workers=num_workers) as executor:
468
+ future_to_image = {executor.submit(process_single_image, image_path, output_folder, bg_method, canvas_size, output_format, bg_choice, custom_color, watermark_path, twibbon_path): image_path for image_path in image_files}
469
+ for idx, future in enumerate(future_to_image):
470
+ try:
471
+ start_time_image = time.time()
472
+ result, log = future.result()
473
+ end_time_image = time.time()
474
+ image_processing_time = end_time_image - start_time_image
475
+
476
+ # Update average processing time
477
+ avg_processing_time = (avg_processing_time * idx + image_processing_time) / (idx + 1)
478
+
479
+ if result:
480
+ processed_images.extend(result)
481
+ original_images.append(future_to_image[future])
482
+ all_logs.append({os.path.basename(future_to_image[future]): log})
483
+
484
+ # Estimate remaining time
485
+ remaining_images = total_images - (idx + 1)
486
+ estimated_remaining_time = remaining_images * avg_processing_time
487
+
488
+ progress((idx + 1) / total_images, f"{idx + 1}/{total_images} images processed. Estimated time remaining: {estimated_remaining_time:.2f} seconds")
489
+ except Exception as e:
490
+ print(f"Error processing image {future_to_image[future]}: {e}")
491
+
492
+ output_zip_path = "processed_images.zip"
493
+ with zipfile.ZipFile(output_zip_path, 'w') as zipf:
494
+ for file, _ in processed_images:
495
+ zipf.write(file, os.path.basename(file))
496
+
497
+ # Write the comprehensive log for all images
498
+ with open(os.path.join(output_folder, 'process_log.json'), 'w') as log_file:
499
+ json.dump(all_logs, log_file, indent=4)
500
+ print("Comprehensive log saved to", os.path.join(output_folder, 'process_log.json'))
501
+
502
+ end_time = time.time()
503
+ processing_time = end_time - start_time
504
+ print(f"Processing time: {processing_time} seconds")
505
+
506
+ return original_images, processed_images, output_zip_path, processing_time
507
+
508
+ def gradio_interface(input_files, bg_method, watermark, twibbon, canvas_size, output_format, bg_choice, custom_color, num_workers):
509
+ progress = gr.Progress()
510
+ watermark_path = watermark.name if watermark else None
511
+ twibbon_path = twibbon.name if twibbon else None
512
+
513
+ # Check input_files, is it single image, list image, or zip/rar
514
+ if isinstance(input_files, str) and input_files.lower().endswith(('.zip', '.rar')):
515
+ return process_images(input_files, bg_method, watermark_path, twibbon_path, canvas_size, output_format, bg_choice, custom_color, num_workers, progress)
516
+ elif isinstance(input_files, list):
517
+ return process_images(input_files, bg_method, watermark_path, twibbon_path, canvas_size, output_format, bg_choice, custom_color, num_workers, progress)
518
+ else:
519
+ return process_images(input_files.name, bg_method, watermark_path, twibbon_path, canvas_size, output_format, bg_choice, custom_color, num_workers, progress)
520
+
521
+ def show_color_picker(bg_choice):
522
+ if bg_choice == 'custom':
523
+ return gr.update(visible=True)
524
+ return gr.update(visible=False)
525
+
526
+ def update_compare(evt: gr.SelectData):
527
+ if isinstance(evt.value, dict) and 'caption' in evt.value:
528
+ input_path = evt.value['caption']
529
+ output_path = evt.value['image']['path']
530
+ input_path = input_path.split("Input: ")[-1]
531
+
532
+ # Open the original and processed images
533
+ original_img = Image.open(input_path)
534
+ processed_img = Image.open(output_path)
535
+
536
+ # Calculate the aspect ratios
537
+ original_ratio = f"{original_img.width}x{original_img.height}"
538
+ processed_ratio = f"{processed_img.width}x{processed_img.height}"
539
+
540
+ return gr.update(value=input_path), gr.update(value=output_path), gr.update(value=original_ratio), gr.update(value=processed_ratio)
541
+ else:
542
+ print("No caption found in selection")
543
+ return gr.update(value=None), gr.update(value=None), gr.update(value=None), gr.update(value=None)
544
+
545
+ def process(input_files, bg_method, watermark, twibbon, canvas_size, output_format, bg_choice, custom_color, num_workers):
546
+ _, processed_images, zip_path, time_taken = gradio_interface(input_files, bg_method, watermark, twibbon, canvas_size, output_format, bg_choice, custom_color, num_workers)
547
+ processed_images_with_captions = [(img, f"Input: {caption}") for img, caption in processed_images]
548
+ return processed_images_with_captions, zip_path, f"{time_taken:.2f} seconds"
549
+
550
+ with gr.Blocks(theme="NoCrypt/[email protected]") as iface:
551
+ gr.Markdown("# Image Background Removal, Resizing with Optional Watermark, twibbon")
552
+ gr.Markdown("Choose to upload multiple images or a ZIP/RAR file, select the crop mode, optionally upload a watermark image, and choose the output format.")
553
+
554
+ with gr.Row():
555
+ input_files = gr.File(label="Upload Image or ZIP/RAR file", file_types=[".zip", ".rar", "image"], interactive=True)
556
+ watermark = gr.File(label="Upload Watermark Image (Optional)", file_types=[".png"])
557
+ twibbon = gr.File(label="Upload twibbon Image (Optional)", file_types=[".png"])
558
+
559
+ with gr.Row():
560
+ canvas_size = gr.Radio(choices=["Rox", "Columbia", "Zalora"], label="Canvas Size", value="Rox")
561
+ output_format = gr.Radio(choices=["PNG", "JPG"], label="Output Format", value="JPG")
562
+ num_workers = gr.Slider(minimum=1, maximum=16, step=1, label="Number of Workers", value=5)
563
+
564
+ with gr.Row():
565
+ bg_method = gr.Radio(choices=["bria", "rembg"], label="Background Removal Method", value="bria")
566
+ bg_choice = gr.Radio(choices=["transparent", "white", "custom"], label="Background Choice", value="white")
567
+ custom_color = gr.ColorPicker(label="Custom Background Color", value="#ffffff", visible=False)
568
+
569
+ process_button = gr.Button("Process Images")
570
+
571
+ with gr.Row():
572
+ gallery_processed = gr.Gallery(label="Processed Images")
573
+ with gr.Row():
574
+ image_original = gr.Image(label="Original Images", interactive=False)
575
+ image_processed = gr.Image(label="Processed Images", interactive=False)
576
+ with gr.Row():
577
+ original_ratio = gr.Textbox(label="Original Ratio")
578
+ processed_ratio = gr.Textbox(label="Processed Ratio")
579
+ with gr.Row():
580
+ output_zip = gr.File(label="Download Processed Images as ZIP")
581
+ processing_time = gr.Textbox(label="Processing Time (seconds)")
582
+
583
+ bg_choice.change(show_color_picker, inputs=bg_choice, outputs=custom_color)
584
+ process_button.click(process, inputs=[input_files, bg_method, watermark, twibbon, canvas_size, output_format, bg_choice, custom_color, num_workers], outputs=[gallery_processed, output_zip, processing_time])
585
+ gallery_processed.select(update_compare, outputs=[image_original, image_processed, original_ratio, processed_ratio])
586
+
587
  iface.launch(share=True)