add: custom offsets

#8
by LPX55 - opened
Files changed (1) hide show
  1. app.py +104 -73
app.py CHANGED
@@ -106,13 +106,13 @@ def can_expand(source_width, source_height, target_width, target_height, alignme
106
  return False
107
  return True
108
 
109
-
110
- def prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
111
  target_size = (width, height)
112
  scale_factor = min(target_size[0] / image.width, target_size[1] / image.height)
113
  new_width = int(image.width * scale_factor)
114
  new_height = int(image.height * scale_factor)
115
  source = image.resize((new_width, new_height), Image.LANCZOS)
 
116
  if resize_option == "Full":
117
  resize_percentage = 100
118
  elif resize_option == "80%":
@@ -133,10 +133,12 @@ def prepare_image_and_mask(image, width, height, overlap_percentage, resize_opti
133
  new_width = max(new_width, 64)
134
  new_height = max(new_height, 64)
135
  source = source.resize((new_width, new_height), Image.LANCZOS)
 
136
  overlap_x = int(new_width * (overlap_percentage / 100))
137
  overlap_y = int(new_height * (overlap_percentage / 100))
138
  overlap_x = max(overlap_x, 1)
139
  overlap_y = max(overlap_y, 1)
 
140
  if alignment == "Middle":
141
  margin_x = (target_size[0] - new_width) // 2
142
  margin_y = (target_size[1] - new_height) // 2
@@ -152,39 +154,51 @@ def prepare_image_and_mask(image, width, height, overlap_percentage, resize_opti
152
  elif alignment == "Bottom":
153
  margin_x = (target_size[0] - new_width) // 2
154
  margin_y = target_size[1] - new_height
155
- margin_x = max(0, min(margin_x, target_size[0] - new_width))
156
- margin_y = max(0, min(margin_y, target_size[1] - new_height))
 
 
157
  background = Image.new('RGB', target_size, (255, 255, 255))
158
  background.paste(source, (margin_x, margin_y))
 
159
  mask = Image.new('L', target_size, 255)
160
  mask_draw = ImageDraw.Draw(mask)
161
  white_gaps_patch = 2
 
162
  left_overlap = margin_x + overlap_x if overlap_left else margin_x + white_gaps_patch
163
  right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width - white_gaps_patch
164
  top_overlap = margin_y + overlap_y if overlap_top else margin_y + white_gaps_patch
165
  bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height - white_gaps_patch
166
- if alignment == "Left":
167
- left_overlap = margin_x + overlap_x if overlap_left else margin_x
168
- elif alignment == "Right":
169
- right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width
170
- elif alignment == "Top":
171
- top_overlap = margin_y + overlap_y if overlap_top else margin_y
172
- elif alignment == "Bottom":
173
- botttom_overlap = margin = margin = margin = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height
174
  mask_draw.rectangle([
175
  (left_overlap, top_overlap),
176
  (right_overlap, bottom_overlap)
177
  ], fill=0)
 
178
  return background, mask
179
- def preview_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
180
- 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)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  preview = background.copy().convert('RGBA')
182
  red_overlay = Image.new('RGBA', background.size, (255, 0, 0, 64))
183
  red_mask = Image.new('RGBA', background.size, (0, 0, 0, 0))
184
  red_mask.paste(red_overlay, (0, 0), mask)
185
  preview = Image.alpha_composite(preview, red_mask)
186
  return preview
187
-
188
  @spaces.GPU(duration=12)
189
  def inpaint(prompt, image, inpaint_model, paste_back):
190
  global pipe
@@ -204,9 +218,9 @@ def inpaint(prompt, image, inpaint_model, paste_back):
204
  result.paste(image, (0, 0), Image.fromarray(255 - np.array(mask)))
205
  return result
206
 
207
- @spaces.GPU(duration=12)
208
- def outpaint(image, width, height, overlap_percentage, num_inference_steps, resize_option, custom_resize_percentage, prompt_input, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
209
- 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)
210
  if not can_expand(background.width, background.height, width, height, alignment):
211
  alignment = "Middle"
212
  cnet_image = background.copy()
@@ -231,10 +245,10 @@ def outpaint(image, width, height, overlap_percentage, num_inference_steps, resi
231
  image = image.convert("RGBA")
232
  cnet_image.paste(image, (0, 0), mask)
233
  yield background, cnet_image
234
-
235
- @spaces.GPU(duration=12)
236
- 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):
237
- 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)
238
  if not can_expand(background.width, background.height, width, height, alignment):
239
  alignment = "Middle"
240
  cnet_image = background.copy()
@@ -258,7 +272,7 @@ def infer(image, width, height, overlap_percentage, num_inference_steps, resize_
258
  image = image.convert("RGBA")
259
  cnet_image.paste(image, (0, 0), mask)
260
  yield background, cnet_image
261
-
262
  def use_output_as_input(output_image):
263
  return gr.update(value=output_image[1])
264
 
@@ -444,54 +458,69 @@ with gr.Blocks(css=css, fill_height=True) as demo:
444
  value="Middle",
445
  label="Alignment"
446
  )
447
- with gr.Accordion(label="Advanced settings", open=False) as settings_panel:
448
- with gr.Column():
449
- with gr.Row():
450
- width_slider = gr.Slider(
451
- label="Target Width",
452
- minimum=720,
453
- maximum=1536,
454
- step=8,
455
- value=1024,
456
- )
457
- height_slider = gr.Slider(
458
- label="Target Height",
459
- minimum=720,
460
- maximum=1536,
461
- step=8,
462
- value=1024,
463
- )
464
- num_inference_steps = gr.Slider(label="Steps", minimum=4, maximum=12, step=1, value=8)
465
- with gr.Group():
466
- overlap_percentage = gr.Slider(
467
- label="Mask overlap (%)",
468
- minimum=1,
469
- maximum=80,
470
- value=10,
471
- step=1
472
- )
473
- with gr.Row():
474
- overlap_top = gr.Checkbox(label="Overlap Top", value=True)
475
- overlap_right = gr.Checkbox(label="Overlap Right", value=True)
476
- with gr.Row():
477
- overlap_left = gr.Checkbox(label="Overlap Left", value=True)
478
- overlap_bottom = gr.Checkbox(label="Overlap Bottom", value=True)
479
- with gr.Row():
480
- resize_option = gr.Radio(
481
- label="Resize input image",
482
- choices=["Full", "80%", "66%", "50%", "33%", "25%", "Custom"],
483
- value="Full"
484
- )
485
- custom_resize_percentage = gr.Slider(
486
- label="Custom resize (%)",
487
- minimum=1,
488
- maximum=100,
489
- step=1,
490
- value=50,
491
- visible=False
492
- )
493
- with gr.Column():
494
- preview_button = gr.Button("Preview alignment and mask")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  # gr.Examples(
496
  # examples=[
497
  # ["./examples/example_1.webp", 1280, 720, "Middle"],
@@ -588,7 +617,7 @@ with gr.Blocks(css=css, fill_height=True) as demo:
588
  fn=infer,
589
  inputs=[input_image_outpaint, width_slider, height_slider, overlap_percentage, num_inference_steps,
590
  resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
591
- overlap_left, overlap_right, overlap_top, overlap_bottom],
592
  outputs=[result_outpaint],
593
  ).then(
594
  fn=lambda x, history: update_history(x[1], history),
@@ -599,10 +628,12 @@ with gr.Blocks(css=css, fill_height=True) as demo:
599
  inputs=None,
600
  outputs=[use_as_input_button_outpaint],
601
  )
 
 
602
  preview_button.click(
603
  fn=preview_image_and_mask,
604
  inputs=[input_image_outpaint, width_slider, height_slider, overlap_percentage, resize_option, custom_resize_percentage, alignment_dropdown,
605
- overlap_left, overlap_right, overlap_top, overlap_bottom],
606
  outputs=[preview_image],
607
  queue=False
608
  )
 
106
  return False
107
  return True
108
 
109
+ def prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom, x_offset, y_offset):
 
110
  target_size = (width, height)
111
  scale_factor = min(target_size[0] / image.width, target_size[1] / image.height)
112
  new_width = int(image.width * scale_factor)
113
  new_height = int(image.height * scale_factor)
114
  source = image.resize((new_width, new_height), Image.LANCZOS)
115
+
116
  if resize_option == "Full":
117
  resize_percentage = 100
118
  elif resize_option == "80%":
 
133
  new_width = max(new_width, 64)
134
  new_height = max(new_height, 64)
135
  source = source.resize((new_width, new_height), Image.LANCZOS)
136
+
137
  overlap_x = int(new_width * (overlap_percentage / 100))
138
  overlap_y = int(new_height * (overlap_percentage / 100))
139
  overlap_x = max(overlap_x, 1)
140
  overlap_y = max(overlap_y, 1)
141
+
142
  if alignment == "Middle":
143
  margin_x = (target_size[0] - new_width) // 2
144
  margin_y = (target_size[1] - new_height) // 2
 
154
  elif alignment == "Bottom":
155
  margin_x = (target_size[0] - new_width) // 2
156
  margin_y = target_size[1] - new_height
157
+
158
+ margin_x = max(0, min(margin_x + x_offset, target_size[0] - new_width))
159
+ margin_y = max(0, min(margin_y + y_offset, target_size[1] - new_height))
160
+
161
  background = Image.new('RGB', target_size, (255, 255, 255))
162
  background.paste(source, (margin_x, margin_y))
163
+
164
  mask = Image.new('L', target_size, 255)
165
  mask_draw = ImageDraw.Draw(mask)
166
  white_gaps_patch = 2
167
+
168
  left_overlap = margin_x + overlap_x if overlap_left else margin_x + white_gaps_patch
169
  right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width - white_gaps_patch
170
  top_overlap = margin_y + overlap_y if overlap_top else margin_y + white_gaps_patch
171
  bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height - white_gaps_patch
172
+
 
 
 
 
 
 
 
173
  mask_draw.rectangle([
174
  (left_overlap, top_overlap),
175
  (right_overlap, bottom_overlap)
176
  ], fill=0)
177
+
178
  return background, mask
179
+ def preview_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom, x_offset, y_offset):
180
+ background, mask = prepare_image_and_mask(
181
+ image,
182
+ width,
183
+ height,
184
+ overlap_percentage,
185
+ resize_option,
186
+ custom_resize_percentage,
187
+ alignment,
188
+ overlap_left,
189
+ overlap_right,
190
+ overlap_top,
191
+ overlap_bottom,
192
+ x_offset,
193
+ y_offset
194
+ )
195
  preview = background.copy().convert('RGBA')
196
  red_overlay = Image.new('RGBA', background.size, (255, 0, 0, 64))
197
  red_mask = Image.new('RGBA', background.size, (0, 0, 0, 0))
198
  red_mask.paste(red_overlay, (0, 0), mask)
199
  preview = Image.alpha_composite(preview, red_mask)
200
  return preview
201
+
202
  @spaces.GPU(duration=12)
203
  def inpaint(prompt, image, inpaint_model, paste_back):
204
  global pipe
 
218
  result.paste(image, (0, 0), Image.fromarray(255 - np.array(mask)))
219
  return result
220
 
221
+ @spaces.GPU(duration=7)
222
+ def outpaint(image, width, height, overlap_percentage, num_inference_steps, resize_option, custom_resize_percentage, prompt_input, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom, x_offset, y_offset):
223
+ 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, x_offset, y_offset)
224
  if not can_expand(background.width, background.height, width, height, alignment):
225
  alignment = "Middle"
226
  cnet_image = background.copy()
 
245
  image = image.convert("RGBA")
246
  cnet_image.paste(image, (0, 0), mask)
247
  yield background, cnet_image
248
+
249
+ @spaces.GPU(duration=7)
250
+ 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, x_offset, y_offset):
251
+ 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, x_offset, y_offset)
252
  if not can_expand(background.width, background.height, width, height, alignment):
253
  alignment = "Middle"
254
  cnet_image = background.copy()
 
272
  image = image.convert("RGBA")
273
  cnet_image.paste(image, (0, 0), mask)
274
  yield background, cnet_image
275
+
276
  def use_output_as_input(output_image):
277
  return gr.update(value=output_image[1])
278
 
 
458
  value="Middle",
459
  label="Alignment"
460
  )
461
+ with gr.Accordion(label="Advanced settings", open=False) as settings_panel:
462
+ with gr.Column():
463
+ with gr.Row():
464
+ width_slider = gr.Slider(
465
+ label="Target Width",
466
+ minimum=720,
467
+ maximum=1536,
468
+ step=8,
469
+ value=1024,
470
+ )
471
+ height_slider = gr.Slider(
472
+ label="Target Height",
473
+ minimum=720,
474
+ maximum=1536,
475
+ step=8,
476
+ value=1024,
477
+ )
478
+ num_inference_steps = gr.Slider(label="Steps", minimum=4, maximum=12, step=1, value=8)
479
+ with gr.Group():
480
+ overlap_percentage = gr.Slider(
481
+ label="Mask overlap (%)",
482
+ minimum=1,
483
+ maximum=80,
484
+ value=10,
485
+ step=1
486
+ )
487
+ with gr.Row():
488
+ overlap_top = gr.Checkbox(label="Overlap Top", value=True)
489
+ overlap_right = gr.Checkbox(label="Overlap Right", value=True)
490
+ with gr.Row():
491
+ overlap_left = gr.Checkbox(label="Overlap Left", value=True)
492
+ overlap_bottom = gr.Checkbox(label="Overlap Bottom", value=True)
493
+ with gr.Row():
494
+ x_offset_slider = gr.Slider(
495
+ label="X Offset",
496
+ minimum=-100,
497
+ maximum=100,
498
+ value=0,
499
+ step=1
500
+ )
501
+ y_offset_slider = gr.Slider(
502
+ label="Y Offset",
503
+ minimum=-100,
504
+ maximum=100,
505
+ value=0,
506
+ step=1
507
+ )
508
+ with gr.Row():
509
+ resize_option = gr.Radio(
510
+ label="Resize input image",
511
+ choices=["Full", "80%", "66%", "50%", "33%", "25%", "Custom"],
512
+ value="Full"
513
+ )
514
+ custom_resize_percentage = gr.Slider(
515
+ label="Custom resize (%)",
516
+ minimum=1,
517
+ maximum=100,
518
+ step=1,
519
+ value=50,
520
+ visible=False
521
+ )
522
+ with gr.Column():
523
+ preview_button = gr.Button("Preview alignment and mask")
524
  # gr.Examples(
525
  # examples=[
526
  # ["./examples/example_1.webp", 1280, 720, "Middle"],
 
617
  fn=infer,
618
  inputs=[input_image_outpaint, width_slider, height_slider, overlap_percentage, num_inference_steps,
619
  resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
620
+ overlap_left, overlap_right, overlap_top, overlap_bottom, x_offset_slider, y_offset_slider],
621
  outputs=[result_outpaint],
622
  ).then(
623
  fn=lambda x, history: update_history(x[1], history),
 
628
  inputs=None,
629
  outputs=[use_as_input_button_outpaint],
630
  )
631
+
632
+
633
  preview_button.click(
634
  fn=preview_image_and_mask,
635
  inputs=[input_image_outpaint, width_slider, height_slider, overlap_percentage, resize_option, custom_resize_percentage, alignment_dropdown,
636
+ overlap_left, overlap_right, overlap_top, overlap_bottom, x_offset_slider, y_offset_slider],
637
  outputs=[preview_image],
638
  queue=False
639
  )