Image-Resize / test_logic.py
Zeph27's picture
position padding logic
3f3fff8
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")