2gif-maker / app.py
Jeffgold's picture
Update app.py
41953ff verified
raw
history blame
6.97 kB
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()