aiqcamp commited on
Commit
5b79d86
Β·
verified Β·
1 Parent(s): 5f63bf7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +204 -112
app.py CHANGED
@@ -43,8 +43,6 @@ pipe = StableDiffusionXLFillPipeline.from_pretrained(
43
 
44
  pipe.scheduler = TCDScheduler.from_config(pipe.scheduler.config)
45
 
46
- # The key differences are below - don't modify the text encoder directly
47
- # We'll fix it in the pipeline code instead
48
 
49
  def can_expand(source_width, source_height, target_width, target_height, alignment):
50
  """Checks if the image can be expanded based on the alignment."""
@@ -54,108 +52,146 @@ def can_expand(source_width, source_height, target_width, target_height, alignme
54
  return False
55
  return True
56
 
57
- @spaces.GPU(duration=24)
58
- def infer(image, width, height, overlap_width, num_inference_steps, resize_option, custom_resize_size, prompt_input=None, alignment="Middle"):
59
- source = image
60
  target_size = (width, height)
61
- overlap = overlap_width
62
-
63
- # Upscale if source is smaller than target in both dimensions
64
- if source.width < target_size[0] and source.height < target_size[1]:
65
- scale_factor = min(target_size[0] / source.width, target_size[1] / source.height)
66
- new_width = int(source.width * scale_factor)
67
- new_height = int(source.height * scale_factor)
68
- source = source.resize((new_width, new_height), Image.LANCZOS)
69
-
70
- if source.width > target_size[0] or source.height > target_size[1]:
71
- scale_factor = min(target_size[0] / source.width, target_size[1] / source.height)
72
- new_width = int(source.width * scale_factor)
73
- new_height = int(source.height * scale_factor)
74
- source = source.resize((new_width, new_height), Image.LANCZOS)
75
 
 
 
 
 
76
  if resize_option == "Full":
77
- resize_size = max(source.width, source.height)
78
- elif resize_option == "1/2":
79
- resize_size = max(source.width, source.height) // 2
80
- elif resize_option == "1/3":
81
- resize_size = max(source.width, source.height) // 3
82
- elif resize_option == "1/4":
83
- resize_size = max(source.width, source.height) // 4
84
  else: # Custom
85
- resize_size = custom_resize_size
86
 
87
- aspect_ratio = source.height / source.width
88
- new_width = resize_size
89
- new_height = int(resize_size * aspect_ratio)
 
 
 
 
 
 
 
90
  source = source.resize((new_width, new_height), Image.LANCZOS)
91
 
92
- if not can_expand(source.width, source.height, target_size[0], target_size[1], alignment):
93
- alignment = "Middle"
 
 
 
 
 
94
 
95
  # Calculate margins based on alignment
96
  if alignment == "Middle":
97
- margin_x = (target_size[0] - source.width) // 2
98
- margin_y = (target_size[1] - source.height) // 2
99
  elif alignment == "Left":
100
  margin_x = 0
101
- margin_y = (target_size[1] - source.height) // 2
102
  elif alignment == "Right":
103
- margin_x = target_size[0] - source.width
104
- margin_y = (target_size[1] - source.height) // 2
105
  elif alignment == "Top":
106
- margin_x = (target_size[0] - source.width) // 2
107
  margin_y = 0
108
  elif alignment == "Bottom":
109
- margin_x = (target_size[0] - source.width) // 2
110
- margin_y = target_size[1] - source.height
 
 
 
 
111
 
 
112
  background = Image.new('RGB', target_size, (255, 255, 255))
113
  background.paste(source, (margin_x, margin_y))
114
 
 
115
  mask = Image.new('L', target_size, 255)
116
  mask_draw = ImageDraw.Draw(mask)
117
 
118
- # Adjust mask generation based on alignment
119
- if alignment == "Middle":
120
- mask_draw.rectangle([
121
- (margin_x + overlap, margin_y + overlap),
122
- (margin_x + source.width - overlap, margin_y + source.height - overlap)
123
- ], fill=0)
124
- elif alignment == "Left":
125
- mask_draw.rectangle([
126
- (margin_x, margin_y),
127
- (margin_x + source.width - overlap, margin_y + source.height)
128
- ], fill=0)
129
  elif alignment == "Right":
130
- mask_draw.rectangle([
131
- (margin_x + overlap, margin_y),
132
- (margin_x + source.width, margin_y + source.height)
133
- ], fill=0)
134
  elif alignment == "Top":
135
- mask_draw.rectangle([
136
- (margin_x, margin_y),
137
- (margin_x + source.width, margin_y + source.height - overlap)
138
- ], fill=0)
139
  elif alignment == "Bottom":
140
- mask_draw.rectangle([
141
- (margin_x, margin_y + overlap),
142
- (margin_x + source.width, margin_y + source.height)
143
- ], fill=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
  cnet_image = background.copy()
146
  cnet_image.paste(0, (0, 0), mask)
147
 
148
- final_prompt = f"{prompt_input} , high quality, 4k" if prompt_input else "high quality, 4k"
 
 
 
 
 
 
 
149
 
150
- # Important fix: Use the original pipeline's method without modifications
151
- # Let it handle the dtype conversions internally
152
- encoded_prompts = pipe.encode_prompt(final_prompt, "cuda", True)
153
-
154
  for image in pipe(
155
- prompt_embeds=encoded_prompts[0],
156
- negative_prompt_embeds=encoded_prompts[1],
157
- pooled_prompt_embeds=encoded_prompts[2],
158
- negative_pooled_prompt_embeds=encoded_prompts[3],
159
  image=cnet_image,
160
  num_inference_steps=num_inference_steps
161
  ):
@@ -175,15 +211,15 @@ def preload_presets(target_ratio, ui_width, ui_height):
175
  if target_ratio == "9:16":
176
  changed_width = 720
177
  changed_height = 1280
178
- return changed_width, changed_height, gr.update(open=False)
179
  elif target_ratio == "16:9":
180
  changed_width = 1280
181
  changed_height = 720
182
- return changed_width, changed_height, gr.update(open=False)
183
  elif target_ratio == "1:1":
184
  changed_width = 1024
185
  changed_height = 1024
186
- return changed_width, changed_height, gr.update(open=False)
187
  elif target_ratio == "Custom":
188
  return ui_width, ui_height, gr.update(open=True)
189
 
@@ -200,14 +236,34 @@ def select_the_right_preset(user_width, user_height):
200
  def toggle_custom_resize_slider(resize_option):
201
  return gr.update(visible=(resize_option == "Custom"))
202
 
 
 
 
 
 
 
 
203
  css = """
204
- footer {
205
- visibility: hidden;
206
  }
207
  """
208
 
 
 
 
 
 
 
 
 
 
 
 
209
  with gr.Blocks(css=css) as demo:
210
  with gr.Column():
 
 
211
  with gr.Row():
212
  with gr.Column():
213
  input_image = gr.Image(
@@ -232,59 +288,72 @@ with gr.Blocks(css=css) as demo:
232
  alignment_dropdown = gr.Dropdown(
233
  choices=["Middle", "Left", "Right", "Top", "Bottom"],
234
  value="Middle",
235
- label="κ³ μ • μœ„μΉ˜ 선택"
236
  )
237
 
238
  with gr.Accordion(label="Advanced settings", open=False) as settings_panel:
239
  with gr.Column():
240
  with gr.Row():
241
  width_slider = gr.Slider(
242
- label="Width",
243
- minimum=256,
244
  maximum=1536,
245
  step=8,
246
- value=720,
247
  )
248
  height_slider = gr.Slider(
249
- label="Height",
250
- minimum=256,
251
  maximum=1536,
252
  step=8,
253
- value=480,
254
  )
255
- with gr.Row():
256
- num_inference_steps = gr.Slider(label="Steps", minimum=4, maximum=12, step=1, value=8)
257
- overlap_width = gr.Slider(
258
- label="Mask overlap width",
 
259
  minimum=1,
260
  maximum=50,
261
- value=42,
262
  step=1
263
  )
 
 
 
 
 
 
264
  with gr.Row():
265
  resize_option = gr.Radio(
266
  label="Resize input image",
267
- choices=["Full", "1/2", "1/3", "1/4", "Custom"],
268
  value="Full"
269
  )
270
-
271
- custom_resize_size = gr.Slider(
272
- label="Custom resize size",
273
- minimum=256,
274
- maximum=1024,
275
- step=8,
276
- value=512,
277
  visible=False
278
  )
 
 
 
 
 
279
  gr.Examples(
280
  examples=[
281
- ["./examples/example_2.jpg", 1440, 810, "Middle"],
 
282
  ["./examples/example_3.jpg", 1024, 1024, "Bottom"],
283
- ["./examples/example_4.png", 1024, 1024, "Top"],
284
  ],
285
  inputs=[input_image, width_slider, height_slider, alignment_dropdown],
286
  )
287
 
 
 
288
  with gr.Column():
289
  result = ImageSlider(
290
  interactive=False,
@@ -292,6 +361,11 @@ with gr.Blocks(css=css) as demo:
292
  )
293
  use_as_input_button = gr.Button("Use as Input Image", visible=False)
294
 
 
 
 
 
 
295
  def use_output_as_input(output_image):
296
  """Sets the generated output as the new input image."""
297
  return gr.update(value=output_image[1])
@@ -326,38 +400,56 @@ with gr.Blocks(css=css) as demo:
326
  resize_option.change(
327
  fn=toggle_custom_resize_slider,
328
  inputs=[resize_option],
329
- outputs=[custom_resize_size],
330
  queue=False
331
  )
332
 
333
- run_button.click(
334
  fn=clear_result,
335
  inputs=None,
336
  outputs=result,
337
- ).then(
338
  fn=infer,
339
- inputs=[input_image, width_slider, height_slider, overlap_width, num_inference_steps,
340
- resize_option, custom_resize_size, prompt_input, alignment_dropdown],
341
- outputs=result
342
- ).then(
 
 
 
 
 
343
  fn=lambda: gr.update(visible=True),
344
  inputs=None,
345
  outputs=use_as_input_button,
346
  )
347
 
348
- prompt_input.submit(
349
  fn=clear_result,
350
  inputs=None,
351
  outputs=result,
352
- ).then(
353
  fn=infer,
354
- inputs=[input_image, width_slider, height_slider, overlap_width, num_inference_steps,
355
- resize_option, custom_resize_size, prompt_input, alignment_dropdown],
356
- outputs=result
357
- ).then(
 
 
 
 
 
358
  fn=lambda: gr.update(visible=True),
359
  inputs=None,
360
  outputs=use_as_input_button,
361
  )
362
 
 
 
 
 
 
 
 
 
363
  demo.queue(max_size=12).launch(share=False)
 
43
 
44
  pipe.scheduler = TCDScheduler.from_config(pipe.scheduler.config)
45
 
 
 
46
 
47
  def can_expand(source_width, source_height, target_width, target_height, alignment):
48
  """Checks if the image can be expanded based on the alignment."""
 
52
  return False
53
  return True
54
 
55
+ def prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
 
 
56
  target_size = (width, height)
57
+
58
+ # Calculate the scaling factor to fit the image within the target size
59
+ scale_factor = min(target_size[0] / image.width, target_size[1] / image.height)
60
+ new_width = int(image.width * scale_factor)
61
+ new_height = int(image.height * scale_factor)
 
 
 
 
 
 
 
 
 
62
 
63
+ # Resize the source image to fit within target size
64
+ source = image.resize((new_width, new_height), Image.LANCZOS)
65
+
66
+ # Apply resize option using percentages
67
  if resize_option == "Full":
68
+ resize_percentage = 100
69
+ elif resize_option == "50%":
70
+ resize_percentage = 50
71
+ elif resize_option == "33%":
72
+ resize_percentage = 33
73
+ elif resize_option == "25%":
74
+ resize_percentage = 25
75
  else: # Custom
76
+ resize_percentage = custom_resize_percentage
77
 
78
+ # Calculate new dimensions based on percentage
79
+ resize_factor = resize_percentage / 100
80
+ new_width = int(source.width * resize_factor)
81
+ new_height = int(source.height * resize_factor)
82
+
83
+ # Ensure minimum size of 64 pixels
84
+ new_width = max(new_width, 64)
85
+ new_height = max(new_height, 64)
86
+
87
+ # Resize the image
88
  source = source.resize((new_width, new_height), Image.LANCZOS)
89
 
90
+ # Calculate the overlap in pixels based on the percentage
91
+ overlap_x = int(new_width * (overlap_percentage / 100))
92
+ overlap_y = int(new_height * (overlap_percentage / 100))
93
+
94
+ # Ensure minimum overlap of 1 pixel
95
+ overlap_x = max(overlap_x, 1)
96
+ overlap_y = max(overlap_y, 1)
97
 
98
  # Calculate margins based on alignment
99
  if alignment == "Middle":
100
+ margin_x = (target_size[0] - new_width) // 2
101
+ margin_y = (target_size[1] - new_height) // 2
102
  elif alignment == "Left":
103
  margin_x = 0
104
+ margin_y = (target_size[1] - new_height) // 2
105
  elif alignment == "Right":
106
+ margin_x = target_size[0] - new_width
107
+ margin_y = (target_size[1] - new_height) // 2
108
  elif alignment == "Top":
109
+ margin_x = (target_size[0] - new_width) // 2
110
  margin_y = 0
111
  elif alignment == "Bottom":
112
+ margin_x = (target_size[0] - new_width) // 2
113
+ margin_y = target_size[1] - new_height
114
+
115
+ # Adjust margins to eliminate gaps
116
+ margin_x = max(0, min(margin_x, target_size[0] - new_width))
117
+ margin_y = max(0, min(margin_y, target_size[1] - new_height))
118
 
119
+ # Create a new background image and paste the resized source image
120
  background = Image.new('RGB', target_size, (255, 255, 255))
121
  background.paste(source, (margin_x, margin_y))
122
 
123
+ # Create the mask
124
  mask = Image.new('L', target_size, 255)
125
  mask_draw = ImageDraw.Draw(mask)
126
 
127
+ # Calculate overlap areas
128
+ white_gaps_patch = 2
129
+
130
+ left_overlap = margin_x + overlap_x if overlap_left else margin_x + white_gaps_patch
131
+ right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width - white_gaps_patch
132
+ top_overlap = margin_y + overlap_y if overlap_top else margin_y + white_gaps_patch
133
+ bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height - white_gaps_patch
134
+
135
+ if alignment == "Left":
136
+ left_overlap = margin_x + overlap_x if overlap_left else margin_x
 
137
  elif alignment == "Right":
138
+ right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width
 
 
 
139
  elif alignment == "Top":
140
+ top_overlap = margin_y + overlap_y if overlap_top else margin_y
 
 
 
141
  elif alignment == "Bottom":
142
+ bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height
143
+
144
+
145
+ # Draw the mask
146
+ mask_draw.rectangle([
147
+ (left_overlap, top_overlap),
148
+ (right_overlap, bottom_overlap)
149
+ ], fill=0)
150
+
151
+ return background, mask
152
+
153
+ def preview_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
154
+ background, mask = prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom)
155
+
156
+ # Create a preview image showing the mask
157
+ preview = background.copy().convert('RGBA')
158
+
159
+ # Create a semi-transparent red overlay
160
+ red_overlay = Image.new('RGBA', background.size, (255, 0, 0, 64)) # Reduced alpha to 64 (25% opacity)
161
+
162
+ # Convert black pixels in the mask to semi-transparent red
163
+ red_mask = Image.new('RGBA', background.size, (0, 0, 0, 0))
164
+ red_mask.paste(red_overlay, (0, 0), mask)
165
+
166
+ # Overlay the red mask on the background
167
+ preview = Image.alpha_composite(preview, red_mask)
168
+
169
+ return preview
170
+
171
+ @spaces.GPU(duration=24)
172
+ def infer(image, width, height, overlap_percentage, num_inference_steps, resize_option, custom_resize_percentage, prompt_input, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
173
+ background, mask = prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom)
174
+
175
+ if not can_expand(background.width, background.height, width, height, alignment):
176
+ alignment = "Middle"
177
 
178
  cnet_image = background.copy()
179
  cnet_image.paste(0, (0, 0), mask)
180
 
181
+ final_prompt = f"{prompt_input} , high quality, 4k"
182
+
183
+ (
184
+ prompt_embeds,
185
+ negative_prompt_embeds,
186
+ pooled_prompt_embeds,
187
+ negative_pooled_prompt_embeds,
188
+ ) = pipe.encode_prompt(final_prompt, "cuda", True)
189
 
 
 
 
 
190
  for image in pipe(
191
+ prompt_embeds=prompt_embeds,
192
+ negative_prompt_embeds=negative_prompt_embeds,
193
+ pooled_prompt_embeds=pooled_prompt_embeds,
194
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
195
  image=cnet_image,
196
  num_inference_steps=num_inference_steps
197
  ):
 
211
  if target_ratio == "9:16":
212
  changed_width = 720
213
  changed_height = 1280
214
+ return changed_width, changed_height, gr.update()
215
  elif target_ratio == "16:9":
216
  changed_width = 1280
217
  changed_height = 720
218
+ return changed_width, changed_height, gr.update()
219
  elif target_ratio == "1:1":
220
  changed_width = 1024
221
  changed_height = 1024
222
+ return changed_width, changed_height, gr.update()
223
  elif target_ratio == "Custom":
224
  return ui_width, ui_height, gr.update(open=True)
225
 
 
236
  def toggle_custom_resize_slider(resize_option):
237
  return gr.update(visible=(resize_option == "Custom"))
238
 
239
+ def update_history(new_image, history):
240
+ """Updates the history gallery with the new image."""
241
+ if history is None:
242
+ history = []
243
+ history.insert(0, new_image)
244
+ return history
245
+
246
  css = """
247
+ .gradio-container {
248
+ width: 1200px !important;
249
  }
250
  """
251
 
252
+ title = """<h1 align="center">Diffusers Image Outpaint</h1>
253
+ <div align="center">Drop an image you would like to extend, pick your expected ratio and hit Generate.</div>
254
+ <div style="display: flex; justify-content: center; align-items: center; text-align: center;">
255
+ <p style="display: flex;gap: 6px;">
256
+ <a href="https://huggingface.co/spaces/fffiloni/diffusers-image-outpout?duplicate=true">
257
+ <img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/duplicate-this-space-md.svg" alt="Duplicate this Space">
258
+ </a> to skip the queue and enjoy faster inference on the GPU of your choice
259
+ </p>
260
+ </div>
261
+ """
262
+
263
  with gr.Blocks(css=css) as demo:
264
  with gr.Column():
265
+ gr.HTML(title)
266
+
267
  with gr.Row():
268
  with gr.Column():
269
  input_image = gr.Image(
 
288
  alignment_dropdown = gr.Dropdown(
289
  choices=["Middle", "Left", "Right", "Top", "Bottom"],
290
  value="Middle",
291
+ label="Alignment"
292
  )
293
 
294
  with gr.Accordion(label="Advanced settings", open=False) as settings_panel:
295
  with gr.Column():
296
  with gr.Row():
297
  width_slider = gr.Slider(
298
+ label="Target Width",
299
+ minimum=720,
300
  maximum=1536,
301
  step=8,
302
+ value=720, # Set a default value
303
  )
304
  height_slider = gr.Slider(
305
+ label="Target Height",
306
+ minimum=720,
307
  maximum=1536,
308
  step=8,
309
+ value=1280, # Set a default value
310
  )
311
+
312
+ num_inference_steps = gr.Slider(label="Steps", minimum=4, maximum=12, step=1, value=8)
313
+ with gr.Group():
314
+ overlap_percentage = gr.Slider(
315
+ label="Mask overlap (%)",
316
  minimum=1,
317
  maximum=50,
318
+ value=10,
319
  step=1
320
  )
321
+ with gr.Row():
322
+ overlap_top = gr.Checkbox(label="Overlap Top", value=True)
323
+ overlap_right = gr.Checkbox(label="Overlap Right", value=True)
324
+ with gr.Row():
325
+ overlap_left = gr.Checkbox(label="Overlap Left", value=True)
326
+ overlap_bottom = gr.Checkbox(label="Overlap Bottom", value=True)
327
  with gr.Row():
328
  resize_option = gr.Radio(
329
  label="Resize input image",
330
+ choices=["Full", "50%", "33%", "25%", "Custom"],
331
  value="Full"
332
  )
333
+ custom_resize_percentage = gr.Slider(
334
+ label="Custom resize (%)",
335
+ minimum=1,
336
+ maximum=100,
337
+ step=1,
338
+ value=50,
 
339
  visible=False
340
  )
341
+
342
+ with gr.Column():
343
+ preview_button = gr.Button("Preview alignment and mask")
344
+
345
+
346
  gr.Examples(
347
  examples=[
348
+ ["./examples/example_2.jpg", 1440, 810, "Left"],
349
+ ["./examples/example_3.jpg", 1024, 1024, "Top"],
350
  ["./examples/example_3.jpg", 1024, 1024, "Bottom"],
 
351
  ],
352
  inputs=[input_image, width_slider, height_slider, alignment_dropdown],
353
  )
354
 
355
+
356
+
357
  with gr.Column():
358
  result = ImageSlider(
359
  interactive=False,
 
361
  )
362
  use_as_input_button = gr.Button("Use as Input Image", visible=False)
363
 
364
+ history_gallery = gr.Gallery(label="History", columns=6, object_fit="contain", interactive=False)
365
+ preview_image = gr.Image(label="Preview")
366
+
367
+
368
+
369
  def use_output_as_input(output_image):
370
  """Sets the generated output as the new input image."""
371
  return gr.update(value=output_image[1])
 
400
  resize_option.change(
401
  fn=toggle_custom_resize_slider,
402
  inputs=[resize_option],
403
+ outputs=[custom_resize_percentage],
404
  queue=False
405
  )
406
 
407
+ run_button.click( # Clear the result
408
  fn=clear_result,
409
  inputs=None,
410
  outputs=result,
411
+ ).then( # Generate the new image
412
  fn=infer,
413
+ inputs=[input_image, width_slider, height_slider, overlap_percentage, num_inference_steps,
414
+ resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
415
+ overlap_left, overlap_right, overlap_top, overlap_bottom],
416
+ outputs=result,
417
+ ).then( # Update the history gallery
418
+ fn=lambda x, history: update_history(x[1], history),
419
+ inputs=[result, history_gallery],
420
+ outputs=history_gallery,
421
+ ).then( # Show the "Use as Input Image" button
422
  fn=lambda: gr.update(visible=True),
423
  inputs=None,
424
  outputs=use_as_input_button,
425
  )
426
 
427
+ prompt_input.submit( # Clear the result
428
  fn=clear_result,
429
  inputs=None,
430
  outputs=result,
431
+ ).then( # Generate the new image
432
  fn=infer,
433
+ inputs=[input_image, width_slider, height_slider, overlap_percentage, num_inference_steps,
434
+ resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
435
+ overlap_left, overlap_right, overlap_top, overlap_bottom],
436
+ outputs=result,
437
+ ).then( # Update the history gallery
438
+ fn=lambda x, history: update_history(x[1], history),
439
+ inputs=[result, history_gallery],
440
+ outputs=history_gallery,
441
+ ).then( # Show the "Use as Input Image" button
442
  fn=lambda: gr.update(visible=True),
443
  inputs=None,
444
  outputs=use_as_input_button,
445
  )
446
 
447
+ preview_button.click(
448
+ fn=preview_image_and_mask,
449
+ inputs=[input_image, width_slider, height_slider, overlap_percentage, resize_option, custom_resize_percentage, alignment_dropdown,
450
+ overlap_left, overlap_right, overlap_top, overlap_bottom],
451
+ outputs=preview_image,
452
+ queue=False
453
+ )
454
+
455
  demo.queue(max_size=12).launch(share=False)