File size: 6,458 Bytes
50eccc5
ccfb6c3
50eccc5
34bf8d5
 
89026bc
 
19d7628
89026bc
6f9d7f1
1a618e0
 
 
 
f2fbc59
e8cdac5
 
3679676
50eccc5
 
e8cdac5
 
 
1a618e0
 
 
e8cdac5
1a618e0
 
 
 
 
e8cdac5
1a618e0
 
e8cdac5
 
 
 
 
50eccc5
e8cdac5
 
 
 
1a618e0
03ceeb7
e8cdac5
1a618e0
e8cdac5
 
 
 
 
 
 
 
1a618e0
 
e8cdac5
 
1a618e0
e8cdac5
 
 
 
 
 
 
79754e9
e8cdac5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19d7628
 
 
e8cdac5
 
fe0fb2e
e8cdac5
 
 
50eccc5
 
41953ff
34bf8d5
41953ff
 
03ceeb7
 
 
 
 
1a618e0
b46d304
f2fbc59
03ceeb7
ff33256
03ceeb7
 
 
 
 
 
1a618e0
b46d304
f2fbc59
03ceeb7
ff33256
03ceeb7
41953ff
03ceeb7
34bf8d5
03ceeb7
 
 
89026bc
 
 
 
 
 
1a618e0
89026bc
 
41953ff
3679676
 
41953ff
1c46be0
3679676
 
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
import json

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))
    new_image = new_image.resize((256, 256), Image.LANCZOS)  # Resize to match crop size
    return new_image

def create_gif(editor1_output, editor2_output, transition_type, history):
    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 history
        history.append((output_path, f"{transition_type.capitalize()} transition"))

        # Return the updated list of generated GIFs
        return history, history

    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),  # 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=(256, 256),  # Specify exact crop size
                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"
        )

    history = gr.State([])
    
    generate_button.click(
        create_gif,
        inputs=[image_editor1, image_editor2, transition_type, history],
        outputs=[output_gallery, history]
    )

# Launch the interface
iface.launch(share=True)