ginipick commited on
Commit
ecb47d1
ยท
verified ยท
1 Parent(s): 20d0a5c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -11
app.py CHANGED
@@ -158,10 +158,76 @@ def generate_background(prompt: str, aspect_ratio: str) -> Image.Image:
158
  raise gr.Error(f"Background generation failed: {str(e)}")
159
 
160
 
161
- def combine_with_background(foreground: Image.Image, background: Image.Image) -> Image.Image:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  """์ „๊ฒฝ๊ณผ ๋ฐฐ๊ฒฝ ํ•ฉ์„ฑ ํ•จ์ˆ˜"""
163
- background = background.resize(foreground.size)
164
- return Image.alpha_composite(background.convert('RGBA'), foreground)
 
 
 
 
 
 
 
 
 
 
165
 
166
  @spaces.GPU
167
  def _gpu_process(img: Image.Image, prompt: str | BoundingBox | None) -> tuple[Image.Image, BoundingBox | None, list[str]]:
@@ -270,7 +336,7 @@ def update_box_button(img, box_input):
270
  return gr.update(interactive=False, variant="secondary")
271
 
272
 
273
- # ๋งจ ์•ž๋ถ€๋ถ„์— CSS ์ •์˜ ์ถ”๊ฐ€
274
  css = """
275
  footer {display: none}
276
  .main-title {
@@ -321,9 +387,19 @@ button.primary {
321
  button.primary:hover {
322
  background: #1976D2;
323
  }
 
 
 
 
 
 
 
 
 
 
324
  """
325
 
326
- # UI ๋ถ€๋ถ„ ์ˆ˜์ •
327
  with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
328
  gr.HTML("""
329
  <div class="main-title">
@@ -359,6 +435,21 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
359
  visible=True,
360
  scale=1
361
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  process_btn = gr.Button(
363
  "Process",
364
  variant="primary",
@@ -396,20 +487,42 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
396
  queue=False
397
  )
398
 
399
- # bg_prompt๊ฐ€ ๋น„์–ด์žˆ์„ ๋•Œ aspect_ratio๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ํ•จ์ˆ˜
400
- def update_aspect_ratio(bg_prompt):
401
- return gr.update(visible=bool(bg_prompt))
 
 
 
 
402
 
403
  bg_prompt.change(
404
- fn=update_aspect_ratio,
405
  inputs=bg_prompt,
406
- outputs=aspect_ratio,
407
  queue=False
408
  )
409
 
 
 
 
 
 
 
 
 
 
 
 
410
  process_btn.click(
411
  fn=process_prompt,
412
- inputs=[input_image, text_prompt, bg_prompt, aspect_ratio],
 
 
 
 
 
 
 
413
  outputs=[combined_image, extracted_image],
414
  queue=True
415
  )
 
158
  raise gr.Error(f"Background generation failed: {str(e)}")
159
 
160
 
161
+ def create_position_grid():
162
+ """3x3 ์œ„์น˜ ์„ ํƒ ๊ทธ๋ฆฌ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” HTML"""
163
+ return """
164
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; width: 150px; margin: auto;">
165
+ <button class="position-btn" data-pos="top-left">โ†–</button>
166
+ <button class="position-btn" data-pos="top-center">โ†‘</button>
167
+ <button class="position-btn" data-pos="top-right">โ†—</button>
168
+ <button class="position-btn" data-pos="middle-left">โ†</button>
169
+ <button class="position-btn" data-pos="middle-center">โ€ข</button>
170
+ <button class="position-btn" data-pos="middle-right">โ†’</button>
171
+ <button class="position-btn" data-pos="bottom-left">โ†™</button>
172
+ <button class="position-btn" data-pos="bottom-center" data-default="true">โ†“</button>
173
+ <button class="position-btn" data-pos="bottom-right">โ†˜</button>
174
+ </div>
175
+ <script>
176
+ const buttons = document.querySelectorAll('.position-btn');
177
+ buttons.forEach(btn => {
178
+ btn.style.width = '40px';
179
+ btn.style.height = '40px';
180
+ btn.style.border = '1px solid #ccc';
181
+ btn.style.borderRadius = '4px';
182
+ btn.style.cursor = 'pointer';
183
+ if (btn.dataset.default === 'true') {
184
+ btn.style.backgroundColor = '#2196F3';
185
+ btn.style.color = 'white';
186
+ }
187
+ });
188
+ </script>
189
+ """
190
+
191
+ def calculate_object_position(position: str, bg_size: tuple[int, int], obj_size: tuple[int, int]) -> tuple[int, int]:
192
+ """์˜ค๋ธŒ์ ํŠธ์˜ ์œ„์น˜ ๊ณ„์‚ฐ"""
193
+ bg_width, bg_height = bg_size
194
+ obj_width, obj_height = obj_size
195
+
196
+ positions = {
197
+ "top-left": (0, 0),
198
+ "top-center": ((bg_width - obj_width) // 2, 0),
199
+ "top-right": (bg_width - obj_width, 0),
200
+ "middle-left": (0, (bg_height - obj_height) // 2),
201
+ "middle-center": ((bg_width - obj_width) // 2, (bg_height - obj_height) // 2),
202
+ "middle-right": (bg_width - obj_width, (bg_height - obj_height) // 2),
203
+ "bottom-left": (0, bg_height - obj_height),
204
+ "bottom-center": ((bg_width - obj_width) // 2, bg_height - obj_height),
205
+ "bottom-right": (bg_width - obj_width, bg_height - obj_height)
206
+ }
207
+
208
+ return positions.get(position, positions["bottom-center"])
209
+
210
+ def resize_object(image: Image.Image, scale_percent: float) -> Image.Image:
211
+ """์˜ค๋ธŒ์ ํŠธ ํฌ๊ธฐ ์กฐ์ •"""
212
+ width = int(image.width * scale_percent / 100)
213
+ height = int(image.height * scale_percent / 100)
214
+ return image.resize((width, height), Image.Resampling.LANCZOS)
215
+
216
+ def combine_with_background(foreground: Image.Image, background: Image.Image,
217
+ position: str = "bottom-center", scale_percent: float = 100) -> Image.Image:
218
  """์ „๊ฒฝ๊ณผ ๋ฐฐ๊ฒฝ ํ•ฉ์„ฑ ํ•จ์ˆ˜"""
219
+ # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ค€๋น„
220
+ result = background.convert('RGBA')
221
+
222
+ # ์˜ค๋ธŒ์ ํŠธ ํฌ๊ธฐ ์กฐ์ •
223
+ scaled_foreground = resize_object(foreground, scale_percent)
224
+
225
+ # ์˜ค๋ธŒ์ ํŠธ ์œ„์น˜ ๊ณ„์‚ฐ
226
+ x, y = calculate_object_position(position, result.size, scaled_foreground.size)
227
+
228
+ # ํ•ฉ์„ฑ
229
+ result.paste(scaled_foreground, (x, y), scaled_foreground)
230
+ return result
231
 
232
  @spaces.GPU
233
  def _gpu_process(img: Image.Image, prompt: str | BoundingBox | None) -> tuple[Image.Image, BoundingBox | None, list[str]]:
 
336
  return gr.update(interactive=False, variant="secondary")
337
 
338
 
339
+ # CSS ์ •์˜
340
  css = """
341
  footer {display: none}
342
  .main-title {
 
387
  button.primary:hover {
388
  background: #1976D2;
389
  }
390
+ .position-btn {
391
+ transition: all 0.3s ease;
392
+ }
393
+ .position-btn:hover {
394
+ background-color: #e3f2fd;
395
+ }
396
+ .position-btn.selected {
397
+ background-color: #2196F3;
398
+ color: white;
399
+ }
400
  """
401
 
402
+ # UI ๊ตฌ์„ฑ
403
  with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
404
  gr.HTML("""
405
  <div class="main-title">
 
435
  visible=True,
436
  scale=1
437
  )
438
+
439
+ # ์˜ค๋ธŒ์ ํŠธ ์œ„์น˜์™€ ํฌ๊ธฐ ์กฐ์ • ์ปจํŠธ๋กค
440
+ with gr.Row(visible=False) as object_controls:
441
+ with gr.Column(scale=1):
442
+ gr.HTML(create_position_grid())
443
+ position = gr.State(value="bottom-center")
444
+ with gr.Column(scale=1):
445
+ scale_slider = gr.Slider(
446
+ minimum=10,
447
+ maximum=200,
448
+ value=100,
449
+ step=10,
450
+ label="Object Size (%)"
451
+ )
452
+
453
  process_btn = gr.Button(
454
  "Process",
455
  variant="primary",
 
487
  queue=False
488
  )
489
 
490
+ def update_controls(bg_prompt):
491
+ """๋ฐฐ๊ฒฝ ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์ปจํŠธ๋กค ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ"""
492
+ is_visible = bool(bg_prompt)
493
+ return [
494
+ gr.update(visible=is_visible), # aspect_ratio
495
+ gr.update(visible=is_visible), # object_controls
496
+ ]
497
 
498
  bg_prompt.change(
499
+ fn=update_controls,
500
  inputs=bg_prompt,
501
+ outputs=[aspect_ratio, object_controls],
502
  queue=False
503
  )
504
 
505
+ # ์œ„์น˜ ์„ ํƒ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
506
+ def update_position(evt: gr.SelectData) -> str:
507
+ """์œ„์น˜ ์„ ํƒ ์—…๋ฐ์ดํŠธ"""
508
+ return evt.value
509
+
510
+ position.change(
511
+ fn=lambda x: gr.update(value=x),
512
+ inputs=position,
513
+ outputs=position
514
+ )
515
+
516
  process_btn.click(
517
  fn=process_prompt,
518
+ inputs=[
519
+ input_image,
520
+ text_prompt,
521
+ bg_prompt,
522
+ aspect_ratio,
523
+ position,
524
+ scale_slider
525
+ ],
526
  outputs=[combined_image, extracted_image],
527
  queue=True
528
  )