Spaces:
Running
Running
File size: 6,347 Bytes
50eccc5 ccfb6c3 50eccc5 34bf8d5 89026bc 50eccc5 6f9d7f1 1a618e0 e8cdac5 34bf8d5 50eccc5 e8cdac5 1a618e0 e8cdac5 1a618e0 e8cdac5 1a618e0 e8cdac5 50eccc5 e8cdac5 1a618e0 03ceeb7 e8cdac5 1a618e0 e8cdac5 1a618e0 e8cdac5 1a618e0 e8cdac5 79754e9 e8cdac5 50eccc5 41953ff 34bf8d5 41953ff 03ceeb7 1a618e0 b46d304 ff33256 03ceeb7 ff33256 03ceeb7 1a618e0 b46d304 ff33256 03ceeb7 ff33256 03ceeb7 41953ff 03ceeb7 34bf8d5 03ceeb7 89026bc 1a618e0 89026bc 41953ff 1c46be0 34bf8d5 89026bc 41953ff 50eccc5 61cce60 cc2708d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
import gradio as gr
from PIL import Image, ImageDraw
import io
import numpy as np
from math import cos, radians, sin
import os
import time
# Global variable to store generated GIFs
generated_gifs = []
def preprocess_image(image):
# Ensure the image is square by padding it
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(editor1_output, editor2_output, transition_type):
frames = []
duration = 100 # Duration for each frame in milliseconds
total_frames = 18 # Total number of frames
try:
# Open images
img1 = editor1_output["composite"].convert('RGBA')
img2 = editor2_output["composite"].convert('RGBA')
# Preprocess images to make them square and same size
img1 = preprocess_image(img1)
img2 = preprocess_image(img2)
# Set size for the GIF
size = (256, 256)
img1 = img1.resize(size, Image.LANCZOS)
img2 = img2.resize(size, Image.LANCZOS)
if transition_type == "slide":
# Calculate step size for consistent speed
full_width = size[0]
step = full_width // (total_frames // 2) # Divide by 2 as we have 2 parts to the animation
# Generate frames from left to right
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)
# Convert frame to P mode which is a palette-based image
frame = frame.convert('P', palette=Image.ADAPTIVE)
frames.append(frame)
# Generate frames from right to left
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)
# Convert frame to P mode which is a palette-based image
frame = frame.convert('P', palette=Image.ADAPTIVE)
frames.append(frame)
else: # rotate 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)
# Save the GIF to a file with a unique name
os.makedirs("outputs", exist_ok=True)
timestamp = int(time.time())
output_path = f"outputs/output_{timestamp}.gif"
with open(output_path, "wb") as f:
f.write(output.getvalue())
# Add the new GIF to the list of generated GIFs
global generated_gifs
generated_gifs.append((output_path, f"{transition_type.capitalize()} transition"))
# Return the updated list of generated GIFs
return generated_gifs
except Exception as e:
raise ValueError(f"Error creating GIF: {e}")
# Gradio interface
with gr.Blocks() as iface:
gr.Markdown("# 2GIF Transition Slider")
with gr.Row():
with gr.Column(scale=2):
image_editor1 = gr.ImageEditor(
label="Edit Image 1",
brush=gr.Brush(colors=["#ff0000", "#00ff00", "#0000ff"]),
eraser=gr.Eraser(default_size=10),
height=400,
width=400,
crop_size=(256, 256),
layers=True,
type="pil"
)
with gr.Column(scale=2):
image_editor2 = gr.ImageEditor(
label="Edit Image 2",
brush=gr.Brush(colors=["#ff0000", "#00ff00", "#0000ff"]),
eraser=gr.Eraser(default_size=10),
height=400,
width=400,
crop_size=(256, 256),
layers=True,
type="pil"
)
with gr.Row():
transition_type = gr.Radio(["slide", "rotate"], label="Transition Type", value="slide")
generate_button = gr.Button("Generate GIF")
with gr.Row():
output_gallery = gr.Gallery(
label="Generated GIFs",
show_label=True,
elem_id="output_gallery",
columns=3,
rows=2,
height=400,
object_fit="contain"
)
generate_button.click(
create_gif,
inputs=[image_editor1, image_editor2, transition_type],
outputs=[output_gallery]
)
# Launch the interface
iface.launch(share=True) |