Jeffgold commited on
Commit
61cce60
·
verified ·
1 Parent(s): 2ac7eca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -226
app.py CHANGED
@@ -1,16 +1,9 @@
1
- from math import cos, radians, sin
2
- from flask import Flask, render_template, request, send_file, jsonify
3
- from PIL import Image, ImageDraw, ImageOps
4
  import gradio as gr
5
- import os
6
  import io
 
7
  import subprocess
8
- import cairosvg
9
-
10
- app = Flask(__name__)
11
-
12
- UPLOAD_FOLDER = 'uploads'
13
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
14
 
15
  def preprocess_image(image):
16
  # Ensure the image is square by padding it
@@ -19,47 +12,12 @@ def preprocess_image(image):
19
  new_image.paste(image, ((size - image.width) // 2, (size - image.height) // 2))
20
  return new_image
21
 
22
- def convert_webp_to_jpeg(image_data):
23
- try:
24
- image = Image.open(io.BytesIO(image_data))
25
- if image.format == 'WEBP':
26
- output = io.BytesIO()
27
- image.convert('RGB').save(output, format='JPEG')
28
- return output.getvalue()
29
- return image_data
30
- except Exception as e:
31
- raise ValueError(f"Error converting WEBP to JPEG: {e}")
32
-
33
- def convert_svg_to_png(image_data):
34
- try:
35
- png_data = cairosvg.svg2png(bytestring=image_data)
36
- return png_data
37
- except Exception as e:
38
- raise ValueError(f"Error converting SVG to PNG: {e}")
39
-
40
- def create_gif(images):
41
  frames = []
42
  duration = 100 # Duration for each frame in milliseconds
43
  total_frames = 18 # Total number of frames
44
 
45
  try:
46
- img1_data = images[0].read()
47
- img2_data = images[1].read()
48
-
49
- # Convert WebP images to JPEG if necessary
50
- img1_data = convert_webp_to_jpeg(img1_data)
51
- img2_data = convert_webp_to_jpeg(img2_data)
52
-
53
- # Convert SVG images to PNG if necessary
54
- if img1_data.strip().startswith(b'<svg'):
55
- img1_data = convert_svg_to_png(img1_data)
56
- if img2_data.strip().startswith(b'<svg'):
57
- img2_data = convert_svg_to_png(img2_data)
58
-
59
- # Open images
60
- img1 = Image.open(io.BytesIO(img1_data)).convert('RGBA')
61
- img2 = Image.open(io.BytesIO(img2_data)).convert('RGBA')
62
-
63
  # Preprocess images to make them square and same size
64
  img1 = preprocess_image(img1)
65
  img2 = preprocess_image(img2)
@@ -69,144 +27,79 @@ def create_gif(images):
69
  img1 = img1.resize(size, Image.LANCZOS)
70
  img2 = img2.resize(size, Image.LANCZOS)
71
 
72
- # Calculate step size for consistent speed
73
- full_width = size[0]
74
- step = full_width // (total_frames // 2) # Divide by 2 as we have 2 parts to the animation
75
-
76
- # Generate frames from left to right, reversing direction 32 pixels from the edges
77
- for i in range(0, full_width, step):
78
- frame = Image.new('RGBA', size)
79
- frame.paste(img1, (0, 0))
80
- frame.paste(img2.crop((i, 0, full_width, size[1])), (i, 0), mask=img2.crop((i, 0, full_width, size[1])))
81
-
82
- draw = ImageDraw.Draw(frame)
83
- draw.line((i, 0, i, size[1]), fill=(0, 255, 0), width=2)
84
-
85
- # Convert frame to P mode which is a palette-based image
86
- frame = frame.convert('P', palette=Image.ADAPTIVE)
87
- frames.append(frame)
88
-
89
- # Generate frames from right to left, reversing direction 32 pixels from the edges
90
- for i in range(full_width, step, -step):
91
- frame = Image.new('RGBA', size)
92
- frame.paste(img1, (0, 0))
93
- frame.paste(img2.crop((i, 0, full_width, size[1])), (i, 0), mask=img2.crop((i, 0, full_width, size[1])))
94
-
95
- draw = ImageDraw.Draw(frame)
96
- draw.line((i, 0, i, size[1]), fill=(0, 255, 0), width=2)
97
-
98
- # Convert frame to P mode which is a palette-based image
99
- frame = frame.convert('P', palette=Image.ADAPTIVE)
100
- frames.append(frame)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  # Save as GIF
103
  output = io.BytesIO()
104
  frames[0].save(output, format='GIF', save_all=True, append_images=frames[1:], duration=duration, loop=0, optimize=True)
105
  output.seek(0)
106
 
107
- return output
108
  except Exception as e:
109
  raise ValueError(f"Error creating GIF: {e}")
110
 
111
- def create_rotating_gif(images):
112
- frames = []
113
- duration = 100 # Duration for each frame in milliseconds
114
- total_frames = 18 # Total number of frames for a smooth rotation
115
-
116
- try:
117
- img1_data = images[0].read()
118
- img2_data = images[1].read()
119
-
120
- # Convert WebP images to JPEG if necessary
121
- img1_data = convert_webp_to_jpeg(img1_data)
122
- img2_data = convert_webp_to_jpeg(img2_data)
123
-
124
- # Convert SVG images to PNG if necessary
125
- if img1_data.strip().startswith(b'<svg'):
126
- img1_data = convert_svg_to_png(img1_data)
127
- if img2_data.strip().startswith(b'<svg'):
128
- img2_data = convert_svg_to_png(img2_data)
129
-
130
- # Open images
131
- img1 = Image.open(io.BytesIO(img1_data)).convert('RGBA')
132
- img2 = Image.open(io.BytesIO(img2_data)).convert('RGBA')
133
-
134
- # Preprocess images to make them square and same size
135
- img1 = preprocess_image(img1)
136
- img2 = preprocess_image(img2)
137
-
138
- # Set size for the GIF
139
- size = (256, 256)
140
- img1 = img1.resize(size, Image.LANCZOS)
141
- img2 = img2.resize(size, Image.LANCZOS)
142
-
143
- # Create a mask that is 2x the size of the image
144
- mask_size = (size[0] * 2, size[1] * 2)
145
- mask = Image.new('L', mask_size, 0)
146
- draw = ImageDraw.Draw(mask)
147
- draw.rectangle([size[0], 0, mask_size[0], mask_size[1]], fill=255)
148
-
149
- center_x, center_y = size[0] // 2, size[1] // 2
150
-
151
- for angle in range(0, 360, 360 // total_frames):
152
- rotated_mask = mask.rotate(angle, center=(mask_size[0] // 2, mask_size[1] // 2), expand=False)
153
- cropped_mask = rotated_mask.crop((size[0] // 2, size[1] // 2, size[0] // 2 + size[0], size[1] // 2 + size[1]))
154
- frame = Image.composite(img1, img2, cropped_mask)
155
-
156
- # Draw the green dividing lines in the reverse direction
157
- draw = ImageDraw.Draw(frame)
158
- reverse_angle = -angle + 90 # Reverse the direction of the line and add 90 degrees
159
- end_x1 = center_x + int(size[0] * 1.5 * cos(radians(reverse_angle)))
160
- end_y1 = center_y + int(size[1] * 1.5 * sin(radians(reverse_angle)))
161
- end_x2 = center_x - int(size[0] * 1.5 * cos(radians(reverse_angle)))
162
- end_y2 = center_y - int(size[1] * 1.5 * sin(radians(reverse_angle)))
163
- draw.line([center_x, center_y, end_x1, end_y1], fill=(0, 255, 0), width=3) # Line in one direction
164
- draw.line([center_x, center_y, end_x2, end_y2], fill=(0, 255, 0), width=3) # Mirrored tail
165
-
166
- # Convert frame to P mode which is a palette-based image
167
- frame = frame.convert('P', palette=Image.ADAPTIVE)
168
- frames.append(frame)
169
-
170
- # Save as GIF
171
- output = io.BytesIO()
172
- frames[0].save(output, format='GIF', save_all=True, append_images=frames[1:], duration=duration, loop=0, optimize=True)
173
- output.seek(0)
174
-
175
- return output
176
- except Exception as e:
177
- raise ValueError(f"Error creating rotating GIF: {e}")
178
-
179
- def compress_gif(input_path, output_path, lossy=80, colors=256):
180
- # Optimize the GIF using gifsicle with lossy compression
181
- subprocess.run(['gifsicle', '--lossy={}'.format(lossy), '--colors', str(colors), input_path, '-o', output_path])
182
- print(f"Compressed GIF saved to {output_path}")
183
-
184
  def create_gif_gradio(image1, image2, transition_type):
185
- # Convert Gradio image inputs to file-like objects
186
- img1_io = io.BytesIO()
187
- img2_io = io.BytesIO()
188
- image1.save(img1_io, format='PNG')
189
- image2.save(img2_io, format='PNG')
190
- img1_io.seek(0)
191
- img2_io.seek(0)
192
-
193
- images = [img1_io, img2_io]
194
-
195
- if transition_type == 'rotate':
196
- gif_data = create_rotating_gif(images)
197
- else:
198
- gif_data = create_gif(images)
199
-
200
  # Save the GIF to a temporary file
201
- temp_input_path = "temp_input.gif"
202
- with open(temp_input_path, "wb") as f:
203
- f.write(gif_data.getbuffer())
204
-
205
- # Compress the GIF
206
- compressed_output_path = "compressed_output.gif"
207
- compress_gif(temp_input_path, compressed_output_path)
208
-
209
- return compressed_output_path
210
 
211
  # Gradio interface
212
  iface = gr.Interface(
@@ -221,56 +114,5 @@ iface = gr.Interface(
221
  description="Upload two images and select a transition type to generate an animated GIF."
222
  )
223
 
224
- @app.route('/')
225
- def index():
226
- return render_template('index.html')
227
-
228
- @app.route('/gradio', methods=['GET', 'POST'])
229
- def gradio_interface():
230
- return gr.routes.App.create_app(iface)()
231
-
232
- @app.route('/upload_image', methods=['POST'])
233
- def upload_image():
234
- try:
235
- image = request.files['image']
236
- if image:
237
- filepath = os.path.join(UPLOAD_FOLDER, image.filename)
238
- image.save(filepath)
239
- return jsonify({'success': True, 'filepath': filepath})
240
- else:
241
- return jsonify({'success': False, 'error': 'No image uploaded.'})
242
- except Exception as e:
243
- return jsonify({'success': False, 'error': str(e)})
244
-
245
- @app.route('/generate_gif', methods=['POST'])
246
- def generate_gif():
247
- try:
248
- images = request.files.getlist('images')
249
- if len(images) != 2:
250
- raise ValueError("Two images are required.")
251
-
252
- # Determine the type of transition
253
- transition_type = request.form.get('transition_type', 'default')
254
-
255
- # Create the GIF based on the transition type
256
- if transition_type == 'rotate':
257
- gif_data = create_rotating_gif(images)
258
- else:
259
- gif_data = create_gif(images)
260
-
261
- # Save the GIF to a temporary file
262
- temp_input_path = "temp_input.gif"
263
- with open(temp_input_path, "wb") as f:
264
- f.write(gif_data.getbuffer())
265
-
266
- # Compress the GIF
267
- compressed_output_path = "compressed_output.gif"
268
- compress_gif(temp_input_path, compressed_output_path)
269
-
270
- # Send the compressed GIF as the response
271
- return send_file(compressed_output_path, mimetype='image/gif', as_attachment=True, download_name='animation.gif')
272
- except Exception as e:
273
- return jsonify({'error': str(e)}), 400
274
-
275
- if __name__ == '__main__':
276
- app.run(debug=True)
 
 
 
 
1
  import gradio as gr
2
+ from PIL import Image, ImageDraw, ImageOps
3
  import io
4
+ import os
5
  import subprocess
6
+ from math import cos, radians, sin
 
 
 
 
 
7
 
8
  def preprocess_image(image):
9
  # Ensure the image is square by padding it
 
12
  new_image.paste(image, ((size - image.width) // 2, (size - image.height) // 2))
13
  return new_image
14
 
15
+ def create_gif(img1, img2, transition_type):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  frames = []
17
  duration = 100 # Duration for each frame in milliseconds
18
  total_frames = 18 # Total number of frames
19
 
20
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  # Preprocess images to make them square and same size
22
  img1 = preprocess_image(img1)
23
  img2 = preprocess_image(img2)
 
27
  img1 = img1.resize(size, Image.LANCZOS)
28
  img2 = img2.resize(size, Image.LANCZOS)
29
 
30
+ if transition_type == "default":
31
+ # Calculate step size for consistent speed
32
+ full_width = size[0]
33
+ step = full_width // (total_frames // 2) # Divide by 2 as we have 2 parts to the animation
34
+
35
+ # Generate frames from left to right
36
+ for i in range(0, full_width, step):
37
+ frame = Image.new('RGBA', size)
38
+ frame.paste(img1, (0, 0))
39
+ frame.paste(img2.crop((i, 0, full_width, size[1])), (i, 0), mask=img2.crop((i, 0, full_width, size[1])))
40
+
41
+ draw = ImageDraw.Draw(frame)
42
+ draw.line((i, 0, i, size[1]), fill=(0, 255, 0), width=2)
43
+
44
+ frame = frame.convert('P', palette=Image.ADAPTIVE)
45
+ frames.append(frame)
46
+
47
+ # Generate frames from right to left
48
+ for i in range(full_width, step, -step):
49
+ frame = Image.new('RGBA', size)
50
+ frame.paste(img1, (0, 0))
51
+ frame.paste(img2.crop((i, 0, full_width, size[1])), (i, 0), mask=img2.crop((i, 0, full_width, size[1])))
52
+
53
+ draw = ImageDraw.Draw(frame)
54
+ draw.line((i, 0, i, size[1]), fill=(0, 255, 0), width=2)
55
+
56
+ frame = frame.convert('P', palette=Image.ADAPTIVE)
57
+ frames.append(frame)
58
+
59
+ elif transition_type == "rotate":
60
+ # Create a mask that is 2x the size of the image
61
+ mask_size = (size[0] * 2, size[1] * 2)
62
+ mask = Image.new('L', mask_size, 0)
63
+ draw = ImageDraw.Draw(mask)
64
+ draw.rectangle([size[0], 0, mask_size[0], mask_size[1]], fill=255)
65
+
66
+ center_x, center_y = size[0] // 2, size[1] // 2
67
+
68
+ for angle in range(0, 360, 360 // total_frames):
69
+ rotated_mask = mask.rotate(angle, center=(mask_size[0] // 2, mask_size[1] // 2), expand=False)
70
+ cropped_mask = rotated_mask.crop((size[0] // 2, size[1] // 2, size[0] // 2 + size[0], size[1] // 2 + size[1]))
71
+ frame = Image.composite(img1, img2, cropped_mask)
72
+
73
+ draw = ImageDraw.Draw(frame)
74
+ reverse_angle = -angle + 90
75
+ end_x1 = center_x + int(size[0] * 1.5 * cos(radians(reverse_angle)))
76
+ end_y1 = center_y + int(size[1] * 1.5 * sin(radians(reverse_angle)))
77
+ end_x2 = center_x - int(size[0] * 1.5 * cos(radians(reverse_angle)))
78
+ end_y2 = center_y - int(size[1] * 1.5 * sin(radians(reverse_angle)))
79
+ draw.line([center_x, center_y, end_x1, end_y1], fill=(0, 255, 0), width=3)
80
+ draw.line([center_x, center_y, end_x2, end_y2], fill=(0, 255, 0), width=3)
81
+
82
+ frame = frame.convert('P', palette=Image.ADAPTIVE)
83
+ frames.append(frame)
84
 
85
  # Save as GIF
86
  output = io.BytesIO()
87
  frames[0].save(output, format='GIF', save_all=True, append_images=frames[1:], duration=duration, loop=0, optimize=True)
88
  output.seek(0)
89
 
90
+ return output.getvalue()
91
  except Exception as e:
92
  raise ValueError(f"Error creating GIF: {e}")
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  def create_gif_gradio(image1, image2, transition_type):
95
+ gif_data = create_gif(image1, image2, transition_type)
96
+
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  # Save the GIF to a temporary file
98
+ temp_output_path = "output.gif"
99
+ with open(temp_output_path, "wb") as f:
100
+ f.write(gif_data)
101
+
102
+ return temp_output_path
 
 
 
 
103
 
104
  # Gradio interface
105
  iface = gr.Interface(
 
114
  description="Upload two images and select a transition type to generate an animated GIF."
115
  )
116
 
117
+ # Launch the interface
118
+ iface.launch()