Spaces:
Sleeping
Sleeping
File size: 12,998 Bytes
50eccc5 ccfb6c3 50eccc5 34bf8d5 89026bc 19d7628 9c898b1 70a12a4 9c898b1 60ff4c4 89026bc 3f3ab80 1a618e0 e256bcb 9c898b1 3f3ab80 e256bcb e8cdac5 3f3ab80 50eccc5 70a12a4 b014ae7 e8cdac5 1a618e0 e8cdac5 70a12a4 3f3ab80 1a618e0 70a12a4 e8cdac5 70a12a4 b014ae7 70a12a4 e8cdac5 70a12a4 b014ae7 70a12a4 03ceeb7 e8cdac5 e256bcb e8cdac5 fdbfd48 b014ae7 fdbfd48 70a12a4 e8cdac5 e256bcb e8cdac5 70a12a4 fdbfd48 70a12a4 fdbfd48 b014ae7 fdbfd48 70a12a4 e8cdac5 70a12a4 e8cdac5 70a12a4 e8cdac5 70a12a4 e8cdac5 70a12a4 e256bcb e8cdac5 70a12a4 b014ae7 70a12a4 fdbfd48 b014ae7 fdbfd48 70a12a4 e8cdac5 70a12a4 e8cdac5 7e91417 70a12a4 9c898b1 70a12a4 b014ae7 e8cdac5 70a12a4 e8cdac5 70a12a4 50eccc5 41953ff 70a12a4 41953ff 03ceeb7 1a618e0 b46d304 e256bcb 03ceeb7 ff33256 03ceeb7 1a618e0 b46d304 e256bcb 03ceeb7 ff33256 03ceeb7 70a12a4 03ceeb7 34bf8d5 3f3ab80 70a12a4 b014ae7 948d76a b014ae7 03ceeb7 350c36e 70a12a4 3f3ab80 70a12a4 3f3ab80 350c36e 70a12a4 350c36e 70a12a4 41953ff 70a12a4 3f3ab80 350c36e 41953ff 50eccc5 61cce60 3f3ab80 | 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | 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
import json
import random
import imageio
# Load emojis from JSON file
with open("emojis.json") as f:
emojis = json.load(f)["emojis"]
# Directory where emoji PNGs are stored
emoji_dir = "emoji_pngs"
def preprocess_image(image, emoji_name=None, enable_emoji=True):
# 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))
# Create resized images for GIF and Video
new_image_gif = new_image.resize((256, 256), Image.LANCZOS) # Resize for GIF
new_image_video = new_image.resize((720, 720), Image.LANCZOS) # Resize for Video
# Only add emoji if enabled
if enable_emoji:
# Use the provided emoji or select a random one
if not emoji_name:
emoji_name = random.choice(list(emojis.keys()))
emoji_path = os.path.join(emoji_dir, f"{emoji_name}.png")
if os.path.exists(emoji_path):
emoji_image = Image.open(emoji_path).convert("RGBA")
emoji_image_gif = emoji_image.resize((256, 256), Image.LANCZOS)
emoji_image_video = emoji_image.resize((720, 720), Image.LANCZOS)
new_image_gif.paste(emoji_image_gif, (0, 0), emoji_image_gif)
new_image_video.paste(emoji_image_video, (0, 0), emoji_image_video)
else:
emoji_name = "none" # Use a placeholder when emojis are disabled
return new_image_gif, new_image_video, emoji_name
def generate_media(editor1_output, editor2_output, transition_type, fps, transition_speed, color, thickness, enable_emoji):
frames = []
gif_duration = 100 # Duration for each frame in the GIF in milliseconds
total_frames_gif = 18 # Total number of frames for GIF
total_frames_mp4 = int(3 * fps) # Calculate number of frames based on speed
try:
# Open images
img1 = editor1_output["composite"].convert('RGBA')
img2 = editor2_output["composite"].convert('RGBA')
# Preprocess images to make them square and same size, select emojis once
img1_gif, img1_video, random_emoji1 = preprocess_image(img1, enable_emoji=enable_emoji)
img2_gif, img2_video, random_emoji2 = preprocess_image(img2, enable_emoji=enable_emoji)
# Set size for the GIF
gif_size = (256, 256)
# Set size for the MP4
video_size = (720, 720) # Locked to 1:1 aspect ratio
# Initialize video frames list
video_frames = []
# Generate frames for GIF and video
if transition_type == "slide":
# Calculate step size for consistent speed
full_width_gif = gif_size[0]
full_width_video = video_size[0]
step_gif = full_width_gif // (total_frames_gif // 2)
step_video = int(full_width_video / (total_frames_mp4 / transition_speed))
# Generate frames from left to right for GIF
for i in range(0, full_width_gif, step_gif):
frame = Image.new('RGBA', gif_size)
frame.paste(img1_gif, (0, 0))
frame.paste(img2_gif.crop((i, 0, full_width_gif, gif_size[1])), (i, 0),
mask=img2_gif.crop((i, 0, full_width_gif, gif_size[1])))
draw = ImageDraw.Draw(frame)
draw.line((i, 0, i, gif_size[1]), fill=color, width=thickness // 2) # Reduced line width for GIF
frame = frame.convert('P', palette=Image.ADAPTIVE)
frames.append(frame)
# Generate frames for video from left to right
for i in range(0, full_width_video, step_video):
frame = Image.new('RGBA', video_size)
frame.paste(img1_video, (0, 0))
frame.paste(img2_video.crop((i, 0, full_width_video, video_size[1])), (i, 0),
mask=img2_video.crop((i, 0, full_width_video, video_size[1])))
# Add the green divider line
draw = ImageDraw.Draw(frame)
draw.line((i, 0, i, video_size[1]), fill=color, width=thickness) # Thicker line for video
video_frames.append(np.array(frame))
# Generate frames from right to left for GIF
for i in range(full_width_gif, step_gif, -step_gif):
frame = Image.new('RGBA', gif_size)
frame.paste(img1_gif, (0, 0))
frame.paste(img2_gif.crop((i, 0, full_width_gif, gif_size[1])), (i, 0),
mask=img2_gif.crop((i, 0, full_width_gif, gif_size[1])))
draw = ImageDraw.Draw(frame)
draw.line((i, 0, i, gif_size[1]), fill=color, width=thickness // 2) # Reduced line width for GIF
frame = frame.convert('P', palette=Image.ADAPTIVE)
frames.append(frame)
# Generate frames for video from right to left
for i in range(full_width_video, step_video, -step_video):
frame = Image.new('RGBA', video_size)
frame.paste(img1_video, (0, 0))
frame.paste(img2_video.crop((i, 0, full_width_video, video_size[1])), (i, 0),
mask=img2_video.crop((i, 0, full_width_video, video_size[1])))
# Add the green divider line
draw = ImageDraw.Draw(frame)
draw.line((i, 0, i, video_size[1]), fill=color, width=thickness) # Thicker line for video
video_frames.append(np.array(frame))
else: # rotate transition
mask_size_gif = (gif_size[0] * 2, gif_size[1] * 2)
mask_size_video = (video_size[0] * 2, video_size[1] * 2)
# Prepare mask and draw objects
mask_gif = Image.new('L', mask_size_gif, 0)
draw_gif = ImageDraw.Draw(mask_gif)
draw_gif.rectangle([gif_size[0], 0, mask_size_gif[0], mask_size_gif[1]], fill=255)
mask_video = Image.new('L', mask_size_video, 0)
draw_video = ImageDraw.Draw(mask_video)
draw_video.rectangle([video_size[0], 0, mask_size_video[0], mask_size_video[1]], fill=255)
# Rotate transition for GIF
for angle in range(0, 360, 360 // total_frames_gif):
rotated_mask = mask_gif.rotate(angle, center=(mask_size_gif[0] // 2, mask_size_gif[1] // 2), expand=False)
cropped_mask = rotated_mask.crop(
(gif_size[0] // 2, gif_size[1] // 2, gif_size[0] // 2 + gif_size[0], gif_size[1] // 2 + gif_size[1]))
frame = Image.composite(img1_gif, img2_gif, cropped_mask)
draw = ImageDraw.Draw(frame)
reverse_angle = -angle + 90
center_x, center_y = gif_size[0] // 2, gif_size[1] // 2
end_x1 = center_x + int(gif_size[0] * 1.5 * cos(radians(reverse_angle)))
end_y1 = center_y + int(gif_size[1] * 1.5 * sin(radians(reverse_angle)))
end_x2 = center_x - int(gif_size[0] * 1.5 * cos(radians(reverse_angle)))
end_y2 = center_y - int(gif_size[1] * 1.5 * sin(radians(reverse_angle)))
draw.line([center_x, center_y, end_x1, end_y1], fill=color, width=thickness // 2) # Reduced line width for GIF
draw.line([center_x, center_y, end_x2, end_y2], fill=color, width=thickness // 2) # Reduced line width for GIF
frame = frame.convert('P', palette=Image.ADAPTIVE)
frames.append(frame)
# Rotate transition for video
for angle in range(0, 360, int(360 / (total_frames_mp4 / transition_speed))):
rotated_mask = mask_video.rotate(angle, center=(mask_size_video[0] // 2, mask_size_video[1] // 2),
expand=False)
cropped_mask = rotated_mask.crop(
(video_size[0] // 2, video_size[1] // 2, video_size[0] // 2 + video_size[0],
video_size[1] // 2 + video_size[1]))
frame = Image.composite(img1_video, img2_video, cropped_mask)
draw = ImageDraw.Draw(frame)
reverse_angle = -angle + 90
center_x, center_y = video_size[0] // 2, video_size[1] // 2
end_x1 = center_x + int(video_size[0] * 1.5 * cos(radians(reverse_angle)))
end_y1 = center_y + int(video_size[1] * 1.5 * sin(radians(reverse_angle)))
end_x2 = center_x - int(video_size[0] * 1.5 * cos(radians(reverse_angle)))
end_y2 = center_y - int(video_size[1] * 1.5 * sin(radians(reverse_angle)))
draw.line([center_x, center_y, end_x1, end_y1], fill=color, width=thickness) # Thicker line for video
draw.line([center_x, center_y, end_x2, end_y2], fill=color, width=thickness) # Thicker line for video
video_frames.append(np.array(frame))
# Save as GIF
output_gif = io.BytesIO()
frames[0].save(output_gif, format='GIF', save_all=True, append_images=frames[1:], duration=gif_duration, loop=0,
optimize=True)
output_gif.seek(0)
# Save the GIF to a file with a unique name
os.makedirs("outputs", exist_ok=True)
timestamp = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
nonce = int((time.time() * 2) % 2) # create a nonce to the 1/2 second
output_gif_path = f"outputs/{random_emoji1}-{random_emoji2}-{timestamp}{nonce}.gif"
with open(output_gif_path, "wb") as f:
f.write(output_gif.getvalue())
# Save as MP4
output_mp4_path = f"outputs/{random_emoji1}-{random_emoji2}-{timestamp}{nonce}.mp4"
imageio.mimsave(output_mp4_path, video_frames, fps=fps, macro_block_size=None) # Frame rate
# Return paths for download
return output_gif_path, output_mp4_path
except Exception as e:
print(f"Error creating media: {e}") # Debugging output
raise ValueError(f"Error creating media: {e}")
# Gradio interface
with gr.Blocks() as iface:
gr.Markdown("# 2GIF and MP4 Transition Generator")
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=(720, 720), # Specify exact crop size
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=(720, 720), # Specify exact crop size
layers=True,
type="pil"
)
with gr.Row():
transition_type = gr.Radio(["slide", "rotate"], label="Transition Type", value="slide")
enable_emoji = gr.Checkbox(label="Enable Emojis", value=True, info="Toggle emoji overlays on/off")
generate_button = gr.Button("Generate GIF & MP4")
with gr.Accordion("Advanced Settings", open=False):
with gr.Row():
fps = gr.Slider(label="Frame Rate (fps)", minimum=1, maximum=60, value=18, step=1)
transition_speed = gr.Slider(label="Transition Speed", minimum=1, maximum=10, value=1.42069, step=0.1)
color = gr.ColorPicker(label="Divider Line Color", value="#00ff00")
thickness = gr.Slider(label="Line Thickness", minimum=1, maximum=20, value=6, step=1)
with gr.Row():
gif_display = gr.Image(label="Generated GIF", elem_id="gif_display", visible=True)
gif_download = gr.File(label="Download GIF", elem_id="gif_download", visible=True)
mp4_download = gr.File(label="Download MP4", elem_id="mp4_download", visible=True)
def handle_generation(editor1_output, editor2_output, transition_type, fps, transition_speed, color, thickness, enable_emoji):
try:
gif_path, mp4_path = generate_media(editor1_output, editor2_output, transition_type, fps, transition_speed, color, thickness, enable_emoji)
return gif_path, gif_path, mp4_path
except Exception as e:
print(f"Error in handle_generation: {e}")
return None, None, None
generate_button.click(
handle_generation,
inputs=[image_editor1, image_editor2, transition_type, fps, transition_speed, color, thickness, enable_emoji],
outputs=[gif_display, gif_download, mp4_download]
)
# Launch the interface
iface.launch(share=False)
|