woodmastr commited on
Commit
be90a2f
·
verified ·
1 Parent(s): 509a2a9

Update image_processor.py

Browse files
Files changed (1) hide show
  1. image_processor.py +98 -236
image_processor.py CHANGED
@@ -1,244 +1,106 @@
1
- import argparse
2
- import os
3
- import shutil
4
- from rembg import remove
5
  from PIL import Image
6
-
7
-
8
- def add_background(image_path, background, output_path, default_color="#FFFFFF"):
9
- """
10
- Adds a background to an image, with a fallback to a default color if the specified color is not available.
11
-
12
- Args:
13
- - image_path (str): Path to the input image with transparent background.
14
- - background (str): Background color (as a name or hex code) or path to a background image file.
15
- - output_path (str): Path where the image with the new background should be saved.
16
- - default_color (str): Fallback color if the specified background color is not valid. Defaults to white.
17
- """
18
- with Image.open(image_path).convert("RGBA") as foreground:
19
- try:
20
- # Attempt to create a background layer with the specified color or image
21
- if background.startswith("#") or background.isalpha():
22
- # Check if the color name is valid by creating a small test image
23
- Image.new("RGBA", (1, 1), background)
24
- background_layer = Image.new(
25
- "RGBA", foreground.size, background)
26
- else:
27
- # If background is an image file
28
- with Image.open(background).convert("RGBA") as bg_img:
29
- bg_img = bg_img.resize(foreground.size)
30
- background_layer = bg_img
31
- except ValueError:
32
- # If the color is invalid, use the default color
33
- print(
34
- f"Invalid color '{background}'. Using default color '{default_color}'.")
35
- background_layer = Image.new(
36
- "RGBA", foreground.size, default_color)
37
-
38
- # Composite the foreground over the background
39
- with Image.alpha_composite(background_layer, foreground) as final_img:
40
- # Convert to RGB to save in formats other than PNG
41
- final_img = final_img.convert("RGB")
42
- final_img.save(output_path)
43
-
44
-
45
- def autocrop_image(image_path, output_path):
46
- """
47
- Autocrops an image, focusing on the non-transparent pixels and saves as PNG.
48
-
49
- Args:
50
- - image_path (str): Path to the input image.
51
- - output_path (str): Path where the cropped image should be saved.
52
- """
53
- with Image.open(image_path).convert("RGBA") as image:
54
- bbox = image.getbbox()
55
- if bbox:
56
- cropped_image = image.crop(bbox)
57
- cropped_image.save(output_path, format='PNG')
58
- else:
59
- image.save(output_path, format='PNG')
60
-
61
-
62
- def process_image(input_path, output_path, crop=False, remove_bg=False, resize=None, padding=0, background=None):
63
- """
64
- Processes a single image based on the provided options and saves it.
65
-
66
- Args:
67
- - input_path (str): Path to the input image.
68
- - output_path (str): Path where the processed image should be saved.
69
- - crop (bool): Whether to autocrop the image.
70
- - remove_bg (bool): Whether to remove the background of the image.
71
- - resize (tuple): Optional dimensions (width, height) to resize the image.
72
- - padding (int): Number of padding pixels to add around the image.
73
- - background (str): Optional background color (hex code or name) or path to an image file to set as the background.
74
- """
75
- need_processing = crop or resize or remove_bg or background
76
- temp_path = output_path + ".tmp.png"
77
-
78
- if remove_bg:
79
- with open(input_path, 'rb') as input_file:
80
- image_data = input_file.read()
81
- image_data = remove(image_data)
82
- with open(temp_path, 'wb') as temp_file:
83
- temp_file.write(image_data)
84
- else:
85
- # Copy original image to temp_path if no background removal
86
- shutil.copy(input_path, temp_path)
87
-
88
- if crop:
89
- autocrop_image(temp_path, temp_path)
90
-
91
- if resize:
92
- # adjusted_resize = (resize[0],
93
- # resize[1]) if padding else resize
94
- resize_and_pad_image(temp_path, temp_path, resize, padding)
95
-
96
- if background:
97
- add_background(temp_path, background, temp_path)
98
-
99
- # Finalize the process: move from temp_path to output_path
100
- os.rename(temp_path, output_path)
101
-
102
-
103
- def resize_and_pad_image(image_path, output_path, dimensions, padding=0):
104
- """
105
- Resizes an image to fit within specified dimensions (AxB) and adds padding to make it exactly AxB,
106
- ensuring the image content is centered within these dimensions.
107
-
108
- Args:
109
- - image_path (str): Path to the input image.
110
- - output_path (str): Path where the resized and padded image should be saved.
111
- - dimensions (tuple): Target dimensions (width, height) in pixels, before adding padding.
112
- - padding (int): Number of padding pixels to add around the image.
113
- """
114
- target_width, target_height = dimensions
115
- content_width, content_height = target_width - \
116
- 2*padding, target_height - 2*padding
117
-
118
- with Image.open(image_path) as img:
119
- # Resize the image, preserving the aspect ratio
120
- img.thumbnail((content_width, content_height),
121
- Image.Resampling.LANCZOS)
122
-
123
- # Create a new image with the target dimensions and a transparent background
124
- # new image shall include padding spacig
125
-
126
- new_img = Image.new("RGBA", dimensions, (255, 255, 255, 0))
127
-
128
- # Calculate the position to paste the resized image to center it
129
- paste_position = ((target_width - img.width) // 2,
130
- (target_height - img.height) // 2)
131
-
132
- # Paste the resized image onto the new image, centered
133
- new_img.paste(img, paste_position, img if img.mode == 'RGBA' else None)
134
-
135
- # Save the output
136
- new_img.save(output_path, format='PNG')
137
-
138
-
139
- def generate_output_filename(input_path, remove_bg=False, crop=False, resize=None, background=None):
140
- """
141
- Generates an output filename based on the input path and processing options applied.
142
- Appends specific suffixes based on the operations: '_b' for background removal, '_c' for crop,
143
- and '_bg' if a background is added. It ensures the file extension is '.png'.
144
-
145
- Args:
146
- - input_path (str): Path to the input image.
147
- - remove_bg (bool): Indicates if background removal was applied.
148
- - crop (bool): Indicates if autocrop was applied.
149
- - resize (tuple): Optional dimensions (width, height) for resizing the image.
150
- - background (str): Indicates if a background was added (None if not used).
151
-
152
- Returns:
153
- - (str): Modified filename with appropriate suffix and '.png' extension.
154
  """
155
- base, _ = os.path.splitext(os.path.basename(input_path))
156
- suffix = ""
157
 
158
- if remove_bg:
159
- suffix += "_b"
160
- if crop:
161
- suffix += "_c"
162
- if resize:
163
- width, height = resize
164
- suffix += f"_{width}x{height}"
165
- if background:
166
- suffix += "_bg" # Append "_bg" if the background option was used
167
 
168
- # Ensure the file saves as PNG, accommodating for transparency or added backgrounds
169
- return f"{base}{suffix}.png"
 
 
 
 
170
 
 
 
 
171
 
172
- def generate_output_filename2(input_path, remove_bg=False, crop=False, resize=None, padding=0, background=None):
173
- """
174
- Generates an output filename based on the input path and processing options applied.
175
- Takes into account the effect of padding on the final image size for naming.
176
- """
177
- base, _ = os.path.splitext(os.path.basename(input_path))
178
- suffix = ""
179
 
180
- if remove_bg:
181
- suffix += "_b"
182
- if crop:
183
- suffix += "_c"
184
  if resize:
185
- # Adjust the resize dimensions to reflect the final image size after padding
186
- if padding > 0:
187
- # Adjust dimensions to reflect content size before padding if that's the intent
188
- # Otherwise, add padding to the dimensions to reflect final size including padding
189
- adjusted_width = resize[0] + 2*padding
190
- adjusted_height = resize[1] + 2*padding
191
- suffix += f"_{adjusted_width}x{adjusted_height}"
192
- else:
193
- width, height = resize
194
- suffix += f"_{width}x{height}"
195
- if background:
196
- suffix += "_bg"
197
-
198
- return f"{base}{suffix}.png"
199
-
200
- # The main and process_images functions remain the same, but ensure to update them to handle the new PNG output correctly.
201
-
202
- # Update the process_images and main functions to include the new autocrop functionality
203
- # Ensure to pass the crop argument to process_image and adjust the output filename generation accordingly
204
-
205
-
206
- def process_images(input_dir="./input", output_dir="./output", crop=False, remove_bg=False, resize=None, padding=0, background=None):
207
- """
208
- Processes images in the specified directory based on the provided options.
209
-
210
- Args:
211
- - input_dir (str): Directory containing the images to be processed.
212
- - output_dir (str): Directory where processed images will be saved.
213
- - crop (bool): Whether to crop the images.
214
- - remove_bg (bool): Whether to remove the background of the images.
215
- - resize (tuple): Optional dimensions (width, height) to resize the image.
216
- - padding (int): Number of padding pixels to add around the image.
217
- - background (str): Optional background color (hex code or name) or path to an image file to set as the background.
218
- """
219
- processed_input_dir = os.path.join(input_dir, "processed")
220
- os.makedirs(processed_input_dir, exist_ok=True)
221
- os.makedirs(output_dir, exist_ok=True)
222
-
223
- inputs = [os.path.join(input_dir, f) for f in os.listdir(
224
- input_dir) if os.path.isfile(os.path.join(input_dir, f))]
225
-
226
- # if images are not in the input directory, print a message and return
227
- if not inputs:
228
- print("No images found in the input directory.")
229
- return
230
-
231
- for i, input_path in enumerate(inputs, start=1):
232
- filename = os.path.basename(input_path)
233
- output_filename = generate_output_filename(
234
- input_path, remove_bg=remove_bg, crop=crop, resize=resize, background=background)
235
- output_path = os.path.join(output_dir, output_filename)
236
- print(f"Processing image {i}/{len(inputs)}...{filename}")
237
-
238
- # Update the call to process_image with all parameters including background
239
- process_image(input_path, output_path, crop=crop, remove_bg=remove_bg,
240
- resize=resize, padding=padding, background=background)
241
-
242
- shutil.move(input_path, os.path.join(processed_input_dir, filename))
243
-
244
- print("All images have been processed.")
 
1
+ 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):
9
+ """Returns the parameters for the selected standard setting."""
10
+ settings_dict = {
11
+ "S light": (True, True, "240x240", 48, "whitesmoke"),
12
+ "M light": (True, True, "480x480", 96, "whitesmoke"),
13
+ "L light": (True, True, "960x960", 128, "whitesmoke"),
14
+ "S dark": (True, True, "240x240", 48, "#2A373D"),
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>
27
+ <li>Remove Background: {remove_bg}</li>
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()