File size: 14,231 Bytes
3f3fff8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import os
import json
from PIL import Image, ImageDraw
import concurrent.futures
import numpy as np

def remove_blank_zone(input_folder, output_folder):
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    log = []
    try:
        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
            futures = []
            for filename in os.listdir(input_folder):
                if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp')):
                    input_path = os.path.join(input_folder, filename)
                    output_path = os.path.join(output_folder, filename)
                    future = executor.submit(process_image, input_path, output_path)
                    futures.append((filename, future))

            for filename, future in futures:
                image_log = future.result()
                log.append({
                    "image": filename,
                    "actions": image_log
                })

        with open(os.path.join(output_folder, 'process_log.json'), 'w') as log_file:
            json.dump(log, log_file, indent=4)
        print("Log saved to", os.path.join(output_folder, 'process_log.json'))
    except Exception as e:
        print("Error:", e)

def get_bounding_box_with_threshold(image, threshold):
    # Convert image to numpy array
    img_array = np.array(image)
    
    # Get alpha channel
    alpha = img_array[:,:,3]
    
    # Find rows and columns where alpha > threshold
    rows = np.any(alpha > threshold, axis=1)
    cols = np.any(alpha > threshold, axis=0)
    
    # Find the bounding box
    top, bottom = np.where(rows)[0][[0, -1]]
    left, right = np.where(cols)[0][[0, -1]]
    
    if left < right and top < bottom:
        return (left, top, right, bottom)
    else:
        return None

def process_image(image_path, output_path, add_padding_line=False, use_threshold=True):
    image = Image.open(image_path)
    image = image.convert("RGBA")
    
    # Get the bounding box of the non-blank area with threshold
    if use_threshold:
        bbox = get_bounding_box_with_threshold(image, threshold=10)
    else:
        bbox = image.getbbox()
    log = []

    if bbox:
        # Check 1 pixel around the image for non-transparent pixels
        width, height = image.size
        cropped_sides = []
        
        # Define tolerance for transparency
        tolerance = 10  # Adjust this value as needed
        
        # Check top edge
        if any(image.getpixel((x, 0))[3] > tolerance for x in range(width)):
            cropped_sides.append("top")
        
        # Check bottom edge
        if any(image.getpixel((x, height-1))[3] > tolerance for x in range(width)):
            cropped_sides.append("bottom")
        
        # Check left edge
        if any(image.getpixel((0, y))[3] > tolerance for y in range(height)):
            cropped_sides.append("left")
        
        # Check right edge
        if any(image.getpixel((width-1, y))[3] > tolerance for y in range(height)):
            cropped_sides.append("right")
        
        if cropped_sides:
            info_message = f"Info for {os.path.basename(image_path)}: The following sides of the image may contain cropped objects: {', '.join(cropped_sides)}"
            print(info_message)
            log.append({"info": info_message})
        else:
            info_message = f"Info for {os.path.basename(image_path)}: The image is not cropped."
            print(info_message)
            log.append({"info": info_message})
        
        # Crop the image to the bounding box
        image = image.crop(bbox)
        log.append({"action": "crop", "bbox": [str(bbox[0]), str(bbox[1]), str(bbox[2]), str(bbox[3])]})
        
        # Calculate the new size to expand the image
        padding = 125
        target_size = 1080
        aspect_ratio = image.width / image.height
        
        if len(cropped_sides) == 4:
            # If the image is cropped on all sides, center crop it to fit the canvas
            if aspect_ratio > 1:  # Landscape
                new_height = target_size
                new_width = int(new_height * aspect_ratio)
                left = (new_width - target_size) // 2
                image = image.resize((new_width, new_height), Image.LANCZOS)
                image = image.crop((left, 0, left + target_size, target_size))
            else:  # Portrait or square
                new_width = target_size
                new_height = int(new_width / aspect_ratio)
                top = (new_height - target_size) // 2
                image = image.resize((new_width, new_height), Image.LANCZOS)
                image = image.crop((0, top, target_size, top + target_size))
            log.append({"action": "center_crop_resize", "new_size": f"{target_size}x{target_size}"})
            x, y = 0, 0
        elif not cropped_sides:
            # If the image is not cropped, expand it from center until it touches the padding
            new_height = 1080 - 2 * padding  # Ensure it touches top and bottom padding
            new_width = int(new_height * aspect_ratio)
            
            if new_width > 1080 - 2 * padding:
                # If width exceeds available space, adjust based on width
                new_width = 1080 - 2 * padding
                new_height = int(new_width / aspect_ratio)
            
            # Resize the image
            image = image.resize((new_width, new_height), Image.LANCZOS)
            log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
            
            x = (1080 - new_width) // 2
            y = 1080 - new_height - padding
        else:
            # New logic for handling cropped top and left, or top and right
            if set(cropped_sides) == {"top", "left"} or set(cropped_sides) == {"top", "right"}:
                new_height = target_size - padding  # Ensure bottom padding
                new_width = int(new_height * aspect_ratio)
                
                # If new width exceeds canvas width, adjust based on width
                if new_width > target_size:
                    new_width = target_size
                    new_height = int(new_width / aspect_ratio)
                
                # Resize the image
                image = image.resize((new_width, new_height), Image.LANCZOS)
                log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
                
                # Set position
                if "left" in cropped_sides:
                    x = 0
                else:  # right in cropped_sides
                    x = target_size - new_width
                y = 0
                
                # If the resized image is taller than the canvas minus padding, crop from the bottom
                if new_height > target_size - padding:
                    crop_bottom = new_height - (target_size - padding)
                    image = image.crop((0, 0, new_width, new_height - crop_bottom))
                    new_height = target_size - padding
                    log.append({"action": "crop_vertical", "bottom_pixels_removed": str(crop_bottom)})
                
                log.append({"action": "position", "x": str(x), "y": str(y)})
            elif set(cropped_sides) == {"bottom", "left", "right"}:
                # Expand the image from the center
                new_width = target_size
                new_height = int(new_width / aspect_ratio)
                
                if new_height < target_size:
                    new_height = target_size
                    new_width = int(new_height * aspect_ratio)
                
                image = image.resize((new_width, new_height), Image.LANCZOS)
                
                # Crop to fit the canvas
                left = (new_width - target_size) // 2
                top = 0
                image = image.crop((left, top, left + target_size, top + target_size))
                
                log.append({"action": "expand_and_crop", "new_size": f"{target_size}x{target_size}"})
                x, y = 0, 0
            elif cropped_sides == ["top"]:
                # New logic for handling only top-cropped images
                if image.width > image.height:
                    new_width = target_size
                    new_height = int(target_size / aspect_ratio)
                else:
                    new_height = target_size - padding  # Ensure bottom padding
                    new_width = int(new_height * aspect_ratio)
                
                # Resize the image
                image = image.resize((new_width, new_height), Image.LANCZOS)
                log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
                
                x = (1080 - new_width) // 2
                y = 0  # Align to top
                
                # Apply padding only to non-cropped sides
                x = max(padding, min(x, 1080 - new_width - padding))
            elif cropped_sides in [["right"], ["left"]]:
                # New logic for handling only right-cropped or left-cropped images
                if image.width > image.height:
                    new_width = target_size - padding  # Ensure padding on non-cropped side
                    new_height = int(new_width / aspect_ratio)
                else:
                    new_height = target_size - padding  # Ensure bottom padding
                    new_width = int(new_height * aspect_ratio)
                
                # Resize the image
                image = image.resize((new_width, new_height), Image.LANCZOS)
                log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
                
                if cropped_sides == ["right"]:
                    x = 1080 - new_width  # Align to right
                else:  # cropped_sides == ["left"]
                    x = 0  # Align to left without padding
                y = 1080 - new_height - padding  # Respect bottom padding
            elif set(cropped_sides) == {"left", "right"}:
                # Logic for handling images cropped on both left and right sides
                new_width = 1080  # Expand to full width of canvas
                
                # Calculate the aspect ratio of the original image
                aspect_ratio = image.width / image.height
                
                # Calculate the new height while maintaining aspect ratio
                new_height = int(new_width / aspect_ratio)
                
                # Resize the image
                image = image.resize((new_width, new_height), Image.LANCZOS)
                log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
                
                # Set horizontal position (always 0 as it spans full width)
                x = 0
                
                # Calculate vertical position to respect bottom padding
                y = 1080 - new_height - padding
                
                # If the resized image is taller than the canvas, crop from the top only
                if new_height > 1080 - padding:
                    crop_top = new_height - (1080 - padding)
                    image = image.crop((0, crop_top, new_width, new_height))
                    new_height = 1080 - padding
                    y = 0
                    log.append({"action": "crop_vertical", "top_pixels_removed": str(crop_top)})
                else:
                    # Align the image to the bottom with padding
                    y = 1080 - new_height - padding
                
                log.append({"action": "position", "x": str(x), "y": str(y)})
            else:
                # Use the original resizing logic for other partially cropped images
                if image.width > image.height:
                    new_width = target_size
                    new_height = int(target_size / aspect_ratio)
                else:
                    new_height = target_size
                    new_width = int(target_size * aspect_ratio)
                
                # Resize the image
                image = image.resize((new_width, new_height), Image.LANCZOS)
                log.append({"action": "resize", "new_width": str(new_width), "new_height": str(new_height)})
                
                # Center horizontally for all images
                x = (1080 - new_width) // 2
                y = 1080 - new_height - padding
                
                # Adjust positions for cropped sides
                if "top" in cropped_sides:
                    y = 0
                elif "bottom" in cropped_sides:
                    y = 1080 - new_height
                if "left" in cropped_sides:
                    x = 0
                elif "right" in cropped_sides:
                    x = 1080 - new_width
                
                # Apply padding only to non-cropped sides, but keep horizontal centering
                if "left" not in cropped_sides and "right" not in cropped_sides:
                    x = (1080 - new_width) // 2  # Always center horizontally
                if "top" not in cropped_sides and "bottom" not in cropped_sides:
                    y = max(padding, min(y, 1080 - new_height - padding))
        # Create a new 1080x1080 canvas with a white background
        canvas = Image.new("RGBA", (1080, 1080), (255, 255, 255, 255))
        
        # Paste the resized image onto the canvas
        canvas.paste(image, (x, y), image)
        log.append({"action": "paste", "position": [str(x), str(y)]})
        
        # Add visible black line for padding (for all images)
        if add_padding_line:
            draw = ImageDraw.Draw(canvas)
            draw.rectangle([padding, padding, 1080 - padding, 1080 - padding], outline="black", width=5)
            log.append({"action": "add_padding_line"})
        
        # Save the final image
        canvas.save(output_path)
        log.append({"action": "save", "output_path": output_path})

    return log

# Example usage
remove_blank_zone("bria_output", "test_output")