nagolinc commited on
Commit
96cf246
·
1 Parent(s): a5fc09f

initial commit

Browse files
Files changed (2) hide show
  1. README.md +3 -0
  2. app.py +265 -0
README.md CHANGED
@@ -9,4 +9,7 @@ app_file: app.py
9
  pinned: false
10
  ---
11
 
 
 
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
9
  pinned: false
10
  ---
11
 
12
+
13
+ Convert a spritesheet (that is slightly misaligned) to a gif automagically
14
+
15
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from PIL import Image
3
+ from io import BytesIO
4
+ import base64
5
+
6
+ from collections import Counter
7
+
8
+ from PIL import Image
9
+
10
+ import numpy as np
11
+
12
+ import matplotlib.pyplot as plt
13
+
14
+
15
+ def compute_fft_cross_correlation(img1, img2):
16
+
17
+ fft1 = np.fft.fft2(img1)
18
+
19
+ fft2 = np.fft.fft2(np.rot90(img2, 2), s=img1.shape)
20
+
21
+ result = np.fft.ifft2(fft1 * fft2).real
22
+
23
+ return result
24
+
25
+
26
+
27
+ def compute_offsets(reference, images, window_size):
28
+
29
+ reference_gray = np.array(reference.convert('L'))
30
+
31
+ offsets = []
32
+
33
+ for img in images:
34
+
35
+ img_gray = np.array(img.convert('L'))
36
+
37
+ correlation = compute_fft_cross_correlation(reference_gray, img_gray)
38
+
39
+ # Roll the correlation by half the width and height
40
+ height, width = correlation.shape
41
+ correlation = np.roll(correlation, height // 2, axis=0)
42
+ correlation = np.roll(correlation, width // 2, axis=1)
43
+
44
+
45
+ # Find the peak in the central region of the correlation
46
+ center_x, center_y = height // 2, width // 2
47
+ start_x, start_y = center_x - window_size // 2, center_y - window_size // 2
48
+ end_x, end_y = start_x + window_size, start_y + window_size
49
+
50
+ #make sure starts and ends are in the range(0,height) and (0,width)
51
+ start_x = max(start_x,0)
52
+ start_y = max(start_y,0)
53
+ end_x = min(end_x,height-1)
54
+ end_y = min(end_y,width-1)
55
+
56
+
57
+ window_size_x = end_x - start_x
58
+ window_size_y = end_y - start_y
59
+
60
+
61
+ peak_x, peak_y = np.unravel_index(np.argmax(correlation[start_x:end_x, start_y:end_y]), (window_size_x, window_size_y))
62
+
63
+
64
+
65
+
66
+ '''
67
+ #plot the correlation
68
+ fig, axs = plt.subplots(1, 5, figsize=(10, 5))
69
+ axs[0].imshow(reference_gray, cmap='gray')
70
+ axs[0].set_title('Reference')
71
+ axs[1].imshow(img_gray, cmap='gray')
72
+ axs[1].set_title('Image')
73
+ axs[2].imshow(correlation, cmap='hot', interpolation='nearest', extent=[-window_size, window_size, -window_size, window_size])
74
+ axs[2].set_title('Correlation')
75
+ axs[3].imshow(correlation, cmap='hot', interpolation='nearest')
76
+ axs[3].set_title('Correlation full')
77
+ axs[4].imshow(correlation[start_x:end_x, start_y:end_y], cmap='hot', interpolation='nearest')
78
+ axs[4].set_title('Correlation cropped')
79
+ plt.show()
80
+
81
+
82
+ print("what?",np.argmax(correlation[start_x:end_x, start_y:end_y]))
83
+
84
+ print(peak_x, peak_y,start_x,end_x,start_y,end_y,center_x,center_y)
85
+ '''
86
+
87
+
88
+ # Compute the offset in the range [-window_size, window_size]
89
+ peak_x += start_x - center_x + 1
90
+ peak_y += start_y - center_y + 1
91
+
92
+ #signs are wrong
93
+ #peak_x = -peak_x
94
+ #peak_y = -peak_y
95
+
96
+ print(peak_x, peak_y)
97
+
98
+ # Compute the offset in the range [-window_size, window_size]
99
+ if peak_x > correlation.shape[0] // 2:
100
+ peak_x -= correlation.shape[0]
101
+ if peak_y > correlation.shape[1] // 2:
102
+ peak_y -= correlation.shape[1]
103
+
104
+ if peak_x >= 0:
105
+ peak_x = min(peak_x, window_size)
106
+ else:
107
+ peak_x = max(peak_x, -window_size)
108
+
109
+ if peak_y >= 0:
110
+ peak_y = min(peak_y, window_size)
111
+ else:
112
+ peak_y = max(peak_y, -window_size)
113
+
114
+ offsets.append((peak_x, peak_y))
115
+
116
+ return offsets
117
+
118
+
119
+ def find_most_common_color(image):
120
+
121
+ pixels = list(image.getdata())
122
+
123
+ color_counter = Counter(pixels)
124
+
125
+ return color_counter.most_common(1)[0][0]
126
+
127
+
128
+
129
+ def slice_frames_final(original, centers, frame_width, frame_height, background_color=(255, 255, 0, 255)):
130
+
131
+ sliced_frames = []
132
+
133
+ original_width, original_height = original.size
134
+
135
+ for center_x, center_y in centers:
136
+
137
+ left = center_x - frame_width // 2
138
+
139
+ upper = center_y - frame_height // 2
140
+
141
+ right = left + frame_width
142
+
143
+ lower = upper + frame_height
144
+
145
+ new_frame = Image.new("RGBA", (frame_width, frame_height), background_color)
146
+
147
+ paste_x = max(0, -left)
148
+
149
+ paste_y = max(0, -upper)
150
+
151
+ cropped_frame = original.crop((max(0, left), max(0, upper), min(original_width, right), min(original_height, lower)))
152
+
153
+ new_frame.paste(cropped_frame, (paste_x, paste_y))
154
+
155
+ sliced_frames.append(new_frame)
156
+
157
+ return sliced_frames
158
+
159
+
160
+
161
+ def create_aligned_gif(original_image, columns_per_row, window_size=200, duration=100,output_gif_path = 'output.gif'):
162
+
163
+
164
+ original_width, original_height = original_image.size
165
+
166
+ rows = len(columns_per_row)
167
+
168
+ total_frames = sum(columns_per_row)
169
+
170
+ background_color = find_most_common_color(original_image)
171
+
172
+ frame_height = original_height // rows
173
+
174
+ min_frame_width = min([original_width // cols for cols in columns_per_row])
175
+
176
+ frames = []
177
+
178
+ for i in range(rows):
179
+
180
+ frame_width = original_width // columns_per_row[i]
181
+
182
+ for j in range(columns_per_row[i]):
183
+
184
+ left = j * frame_width + (frame_width - min_frame_width) // 2
185
+
186
+ upper = i * frame_height
187
+
188
+ right = left + min_frame_width
189
+
190
+ lower = upper + frame_height
191
+
192
+ frame = original_image.crop((left, upper, right, lower))
193
+
194
+ frames.append(frame)
195
+
196
+ fft_offsets = compute_offsets(frames[0], frames, window_size=window_size)
197
+
198
+ center_coordinates = []
199
+
200
+ frame_idx = 0
201
+
202
+ for i in range(rows):
203
+
204
+ frame_width = original_width // columns_per_row[i]
205
+
206
+ for j in range(columns_per_row[i]):
207
+
208
+ offset_y,offset_x = fft_offsets[frame_idx]
209
+
210
+ center_x = j * frame_width + (frame_width) // 2 - offset_x
211
+
212
+ center_y = frame_height * i + frame_height//2 - offset_y
213
+
214
+ center_coordinates.append((center_x, center_y))
215
+
216
+ frame_idx += 1
217
+
218
+ sliced_frames = slice_frames_final(original_image, center_coordinates, min_frame_width, frame_height, background_color=background_color)
219
+
220
+
221
+
222
+ sliced_frames[0].save(output_gif_path, save_all=True, append_images=sliced_frames[1:], loop=0, duration=duration)
223
+
224
+ '''
225
+ #display frames
226
+ for frame in sliced_frames:
227
+ plt.figure()
228
+ plt.imshow(frame)
229
+ '''
230
+
231
+
232
+ return output_gif_path
233
+
234
+ def wrapper_func(img_arr, columns_per_row_str):
235
+ #img = Image.open(BytesIO(file))
236
+
237
+ img = Image.fromarray(img_arr)
238
+
239
+ columns_per_row = [int(x.strip()) for x in columns_per_row_str.split(',')]
240
+ output_gif_path = 'output.gif'
241
+
242
+
243
+ print("about to die",img,columns_per_row)
244
+
245
+ create_aligned_gif(img, columns_per_row)
246
+ #with open(output_gif_path, "rb") as f:
247
+ #return base64.b64encode(f.read()).decode()
248
+ # Image.open(output_gif_path)
249
+
250
+ return output_gif_path
251
+
252
+ iface = gr.Interface(
253
+ fn=wrapper_func,
254
+ inputs=[
255
+ gr.components.Image(label="Upload Spritesheet"),
256
+ gr.components.Textbox(label="Columns per Row", default="3,4,3")
257
+ ],
258
+ outputs=gr.components.Image(type="filepath", label="Generated GIF"),
259
+ live=False,
260
+ server_name="Hugging Face Spaces",
261
+ server_port=80,
262
+ analytics_enabled=False
263
+ )
264
+
265
+ iface.launch()