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)