Spaces:
Running
Running
import gradio as gr | |
from PIL import Image, ImageDraw, ImageOps | |
import io | |
import numpy as np | |
from math import cos, radians, sin | |
def preprocess_image(image, scale, crop_top, crop_left, crop_bottom, crop_right): | |
# Scale the image | |
new_size = (int(image.width * scale), int(image.height * scale)) | |
image = image.resize(new_size, Image.LANCZOS) | |
# Crop the image | |
width, height = image.size | |
left = int(width * crop_left) | |
top = int(height * crop_top) | |
right = int(width * (1 - crop_right)) | |
bottom = int(height * (1 - crop_bottom)) | |
image = image.crop((left, top, right, bottom)) | |
# Ensure the image is square by padding | |
size = max(image.size) | |
new_image = Image.new("RGBA", (size, size), (255, 255, 255, 0)) | |
new_image.paste(image, ((size - image.width) // 2, (size - image.height) // 2)) | |
return new_image | |
def create_gif(img1, img2, transition_type, scale1, crop_top1, crop_left1, crop_bottom1, crop_right1, | |
scale2, crop_top2, crop_left2, crop_bottom2, crop_right2): | |
frames = [] | |
duration = 100 # Duration for each frame in milliseconds | |
total_frames = 18 # Total number of frames | |
try: | |
# Preprocess images with scaling and cropping | |
img1 = preprocess_image(img1, scale1, crop_top1, crop_left1, crop_bottom1, crop_right1) | |
img2 = preprocess_image(img2, scale2, crop_top2, crop_left2, crop_bottom2, crop_right2) | |
# Set size for the GIF | |
size = (256, 256) | |
img1 = img1.resize(size, Image.LANCZOS) | |
img2 = img2.resize(size, Image.LANCZOS) | |
if transition_type == "default": | |
# Default sliding transition | |
full_width = size[0] | |
step = full_width // (total_frames // 2) | |
for i in range(0, full_width, step): | |
frame = Image.new('RGBA', size) | |
frame.paste(img1, (0, 0)) | |
frame.paste(img2.crop((i, 0, full_width, size[1])), (i, 0), mask=img2.crop((i, 0, full_width, size[1]))) | |
draw = ImageDraw.Draw(frame) | |
draw.line((i, 0, i, size[1]), fill=(0, 255, 0), width=2) | |
frame = frame.convert('P', palette=Image.ADAPTIVE) | |
frames.append(frame) | |
for i in range(full_width, step, -step): | |
frame = Image.new('RGBA', size) | |
frame.paste(img1, (0, 0)) | |
frame.paste(img2.crop((i, 0, full_width, size[1])), (i, 0), mask=img2.crop((i, 0, full_width, size[1]))) | |
draw = ImageDraw.Draw(frame) | |
draw.line((i, 0, i, size[1]), fill=(0, 255, 0), width=2) | |
frame = frame.convert('P', palette=Image.ADAPTIVE) | |
frames.append(frame) | |
elif transition_type == "rotate": | |
# Rotating transition | |
mask_size = (size[0] * 2, size[1] * 2) | |
mask = Image.new('L', mask_size, 0) | |
draw = ImageDraw.Draw(mask) | |
draw.rectangle([size[0], 0, mask_size[0], mask_size[1]], fill=255) | |
center_x, center_y = size[0] // 2, size[1] // 2 | |
for angle in range(0, 360, 360 // total_frames): | |
rotated_mask = mask.rotate(angle, center=(mask_size[0] // 2, mask_size[1] // 2), expand=False) | |
cropped_mask = rotated_mask.crop((size[0] // 2, size[1] // 2, size[0] // 2 + size[0], size[1] // 2 + size[1])) | |
frame = Image.composite(img1, img2, cropped_mask) | |
draw = ImageDraw.Draw(frame) | |
reverse_angle = -angle + 90 | |
end_x1 = center_x + int(size[0] * 1.5 * cos(radians(reverse_angle))) | |
end_y1 = center_y + int(size[1] * 1.5 * sin(radians(reverse_angle))) | |
end_x2 = center_x - int(size[0] * 1.5 * cos(radians(reverse_angle))) | |
end_y2 = center_y - int(size[1] * 1.5 * sin(radians(reverse_angle))) | |
draw.line([center_x, center_y, end_x1, end_y1], fill=(0, 255, 0), width=3) | |
draw.line([center_x, center_y, end_x2, end_y2], fill=(0, 255, 0), width=3) | |
frame = frame.convert('P', palette=Image.ADAPTIVE) | |
frames.append(frame) | |
# Save as GIF | |
output = io.BytesIO() | |
frames[0].save(output, format='GIF', save_all=True, append_images=frames[1:], duration=duration, loop=0, optimize=True) | |
output.seek(0) | |
return output.getvalue() | |
except Exception as e: | |
raise ValueError(f"Error creating GIF: {e}") | |
def create_gif_gradio(image1, image2, transition_type, scale1, crop_top1, crop_left1, crop_bottom1, crop_right1, | |
scale2, crop_top2, crop_left2, crop_bottom2, crop_right2): | |
gif_data = create_gif(image1, image2, transition_type, scale1, crop_top1, crop_left1, crop_bottom1, crop_right1, | |
scale2, crop_top2, crop_left2, crop_bottom2, crop_right2) | |
# Save the GIF to a temporary file | |
temp_output_path = "output.gif" | |
with open(temp_output_path, "wb") as f: | |
f.write(gif_data) | |
return temp_output_path | |
# Gradio interface | |
with gr.Blocks() as iface: | |
gr.Markdown("# GIF Generator with Image Scaling and Cropping") | |
with gr.Row(): | |
with gr.Column(): | |
image1 = gr.Image(type="pil", label="Image 1") | |
scale1 = gr.Slider(minimum=0.1, maximum=2.0, value=1.0, step=0.1, label="Scale Image 1") | |
crop_top1 = gr.Slider(minimum=0, maximum=0.5, value=0, step=0.01, label="Crop Top (Image 1)") | |
crop_left1 = gr.Slider(minimum=0, maximum=0.5, value=0, step=0.01, label="Crop Left (Image 1)") | |
crop_bottom1 = gr.Slider(minimum=0, maximum=0.5, value=0, step=0.01, label="Crop Bottom (Image 1)") | |
crop_right1 = gr.Slider(minimum=0, maximum=0.5, value=0, step=0.01, label="Crop Right (Image 1)") | |
with gr.Column(): | |
image2 = gr.Image(type="pil", label="Image 2") | |
scale2 = gr.Slider(minimum=0.1, maximum=2.0, value=1.0, step=0.1, label="Scale Image 2") | |
crop_top2 = gr.Slider(minimum=0, maximum=0.5, value=0, step=0.01, label="Crop Top (Image 2)") | |
crop_left2 = gr.Slider(minimum=0, maximum=0.5, value=0, step=0.01, label="Crop Left (Image 2)") | |
crop_bottom2 = gr.Slider(minimum=0, maximum=0.5, value=0, step=0.01, label="Crop Bottom (Image 2)") | |
crop_right2 = gr.Slider(minimum=0, maximum=0.5, value=0, step=0.01, label="Crop Right (Image 2)") | |
transition_type = gr.Radio(["default", "rotate"], label="Transition Type", value="default") | |
generate_button = gr.Button("Generate GIF") | |
output_gif = gr.Image(type="filepath", label="Generated GIF") | |
generate_button.click( | |
create_gif_gradio, | |
inputs=[image1, image2, transition_type, | |
scale1, crop_top1, crop_left1, crop_bottom1, crop_right1, | |
scale2, crop_top2, crop_left2, crop_bottom2, crop_right2], | |
outputs=output_gif | |
) | |
# Launch the interface | |
iface.launch() |