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