nagolinc commited on
Commit
dbbf602
1 Parent(s): a5fc09f

initial commit

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