Spaces:
Build error
Build error
| 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") | |