aiqtech commited on
Commit
df8be8a
ยท
verified ยท
1 Parent(s): 76ce631

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +183 -400
app.py CHANGED
@@ -20,83 +20,20 @@ from transformers import GroundingDinoForObjectDetection, GroundingDinoProcessor
20
  from diffusers import FluxPipeline
21
  from transformers import pipeline, AutoTokenizer, AutoModelForSeq2SeqLM
22
  import gc
23
- from PIL import Image, ImageDraw, ImageFont
24
 
25
  def clear_memory():
26
  """๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ ํ•จ์ˆ˜"""
27
  gc.collect()
28
- if torch.cuda.is_available():
29
- try:
30
- with torch.cuda.device('cuda'):
31
- torch.cuda.empty_cache()
32
- except Exception as e:
33
- print(f"GPU memory management warning: {e}")
34
-
35
- def initialize_models():
36
- """๋ชจ๋ธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜"""
37
- global segmenter, gd_model, gd_processor, pipe, translator
38
-
39
  try:
40
- # ๋ฒˆ์—ญ ๋ชจ๋ธ - ๊ฐ€๋ฒผ์šด ๋ฒ„์ „ ์‚ฌ์šฉ
41
- model = AutoModelForSeq2SeqLM.from_pretrained( # ์ˆ˜์ •๋œ ๋ถ€๋ถ„
42
- model_name,
43
- low_cpu_mem_usage=True,
44
- torch_dtype=torch.float16
45
- ).to('cpu')
46
- tokenizer = AutoTokenizer.from_pretrained(model_name)
47
- translator = pipeline("translation", model=model, tokenizer=tokenizer, device=-1)
48
- del model # ๋ช…์‹œ์  ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
49
-
50
- # GroundingDINO - ๋” ์ž‘์€ ๋ชจ๋ธ ์‚ฌ์šฉ
51
- gd_processor = GroundingDinoProcessor.from_pretrained(
52
- "IDEA-Research/grounding-dino-base", # ๋” ์ž‘์€ base ๋ชจ๋ธ
53
- torch_dtype=torch.float16
54
- )
55
- gd_model = None # ํ•„์š”ํ•  ๋•Œ ๋กœ๋“œ
56
-
57
- # Segmenter - ๊ธฐ๋ณธ ์„ค์ •
58
- segmenter = None # ํ•„์š”ํ•  ๋•Œ ๋กœ๋“œ
59
-
60
- # FLUX ํŒŒ์ดํ”„๋ผ์ธ - ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์  ์„ค์ •
61
- pipe = FluxPipeline.from_pretrained(
62
- "black-forest-labs/FLUX.1-dev",
63
- torch_dtype=torch.float16,
64
- low_cpu_mem_usage=True,
65
- use_safetensors=True
66
- )
67
- pipe.enable_attention_slicing(slice_size=1)
68
- pipe.enable_sequential_cpu_offload()
69
-
70
- except Exception as e:
71
- print(f"Model initialization error: {str(e)}")
72
- raise
73
 
74
- def load_model_on_demand(model_type: str):
75
- """ํ•„์š”ํ•  ๋•Œ๋งŒ ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜๋Š” ํ•จ์ˆ˜"""
76
- global gd_model, segmenter
77
-
78
- if model_type == "gd" and gd_model is None:
79
- gd_model = GroundingDinoForObjectDetection.from_pretrained(
80
- "IDEA-Research/grounding-dino-base",
81
- torch_dtype=torch.float16,
82
- low_cpu_mem_usage=True
83
- )
84
- elif model_type == "segmenter" and segmenter is None:
85
- segmenter = BoxSegmenter(device='cpu')
86
-
87
  # GPU ์„ค์ •
88
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # ๋ช…์‹œ์ ์œผ๋กœ cuda:0 ์ง€์ •
89
 
90
- # ์ „์—ญ ์„ค์ •
91
- torch.backends.cudnn.benchmark = False
92
- torch.backends.cuda.matmul.allow_tf32 = True
93
- torch.set_float32_matmul_precision('medium')
94
-
95
- # ์บ์‹œ ํฌ๊ธฐ ์ œํ•œ
96
- os.environ['TRANSFORMERS_CACHE'] = '/tmp/transformers_cache'
97
- os.environ['HF_HOME'] = '/tmp/hf_home'
98
- os.environ['TORCH_HOME'] = '/tmp/torch_home'
99
-
100
  # GPU ์„ค์ •์„ try-except๋กœ ๊ฐ์‹ธ๊ธฐ
101
  if torch.cuda.is_available():
102
  try:
@@ -248,29 +185,32 @@ def calculate_dimensions(aspect_ratio: str, base_size: int = 512) -> tuple[int,
248
  return base_size * 4 // 3, base_size
249
  return base_size, base_size
250
 
251
- @spaces.GPU(duration=20)
252
  def generate_background(prompt: str, aspect_ratio: str) -> Image.Image:
253
  try:
254
  width, height = calculate_dimensions(aspect_ratio)
255
  width, height = adjust_size_to_multiple_of_8(width, height)
256
 
257
- # ์ตœ๋Œ€ ํฌ๊ธฐ ์ œํ•œ
258
- max_size = 512 # 768์—์„œ 512๋กœ ๊ฐ์†Œ
259
  if width > max_size or height > max_size:
260
  ratio = max_size / max(width, height)
261
  width = int(width * ratio)
262
  height = int(height * ratio)
263
  width, height = adjust_size_to_multiple_of_8(width, height)
264
 
265
- with torch.inference_mode():
266
- image = pipe(
267
- prompt=prompt,
268
- width=width,
269
- height=height,
270
- num_inference_steps=4, # 8์—์„œ 4๋กœ ๊ฐ์†Œ
271
- guidance_scale=4.0,
272
- batch_size=1
273
- ).images[0]
 
 
 
 
274
 
275
  return image
276
  except Exception as e:
@@ -399,50 +339,47 @@ def on_change_bbox(prompts: dict[str, Any] | None):
399
  def on_change_prompt(img: Image.Image | None, prompt: str | None, bg_prompt: str | None = None):
400
  return gr.update(interactive=bool(img and prompt))
401
 
402
- def process_image(img: Image.Image) -> Image.Image:
403
- """์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ ์ตœ์ ํ™”"""
404
- # ์ตœ๋Œ€ ํฌ๊ธฐ ์ œํ•œ
405
- max_size = 512 # ๋” ์ž‘์€ ํฌ๊ธฐ๋กœ ์ œํ•œ
406
- if img.width > max_size or img.height > max_size:
407
- ratio = max_size / max(img.width, img.height)
408
- new_size = (int(img.width * ratio), int(img.height * ratio))
409
- img = img.resize(new_size, Image.LANCZOS)
410
-
411
- # ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ ์œ„ํ•œ ์ด๋ฏธ์ง€ ๋ชจ๋“œ ๋ณ€ํ™˜
412
- if img.mode in ['RGBA', 'LA']:
413
- background = Image.new('RGB', img.size, (255, 255, 255))
414
- background.paste(img, mask=img.split()[-1])
415
- img = background
416
-
417
- return img
418
-
419
- @spaces.GPU(duration=15) # ๋” ์งง์€ ์‹œ๊ฐ„ ์ œํ•œ
420
  def process_prompt(img: Image.Image, prompt: str, bg_prompt: str | None = None,
421
  aspect_ratio: str = "1:1", position: str = "bottom-center",
422
- scale_percent: float = 100, text_params: dict | None = None):
423
  try:
424
- # ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
425
- img = process_image(img)
426
 
427
- # ํ•„์š”ํ•œ ๋ชจ๋ธ๋งŒ ๋กœ๋“œ
428
- load_model_on_demand("gd")
429
- load_model_on_demand("segmenter")
430
 
431
- with torch.cuda.amp.autocast(): # ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ ์œ„ํ•œ mixed precision
432
- # ์ฒ˜๋ฆฌ ๋กœ์ง...
433
- pass
434
-
435
- finally:
436
- # ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ
437
- clear_memory()
438
- if torch.cuda.is_available():
 
 
439
  try:
440
- with torch.cuda.device('cuda'):
441
- torch.cuda.empty_cache()
 
 
 
 
 
 
442
  except Exception as e:
443
- print(f"GPU cleanup warning: {e}")
444
-
445
-
 
 
 
 
 
 
 
446
  def process_bbox(img: Image.Image, box_input: str) -> tuple[Image.Image, Image.Image]:
447
  try:
448
  if img is None or box_input.strip() == "":
@@ -482,7 +419,6 @@ def update_box_button(img, box_input):
482
  return gr.update(interactive=False, variant="secondary")
483
 
484
 
485
-
486
  # CSS ์ •์˜
487
  css = """
488
  footer {display: none}
@@ -546,234 +482,99 @@ button.primary:hover {
546
  }
547
  """
548
 
549
- def add_text_with_stroke(draw, text, x, y, font, text_color, stroke_width):
550
- """ํ…์ŠคํŠธ์— ์™ธ๊ณฝ์„ ์„ ์ถ”๊ฐ€ํ•˜๋Š” ํ—ฌํผ ํ•จ์ˆ˜"""
551
- for adj_x in range(-stroke_width, stroke_width + 1):
552
- for adj_y in range(-stroke_width, stroke_width + 1):
553
- draw.text((x + adj_x, y + adj_y), text, font=font, fill=text_color)
554
-
555
- def add_text_to_image(image, text_params):
556
- """์ด๋ฏธ์ง€์— ํ…์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜"""
557
- if not text_params.get('text'):
558
- return image
559
-
560
- if image.mode != 'RGBA':
561
- image = image.convert('RGBA')
562
-
563
- txt_overlay = Image.new('RGBA', image.size, (255, 255, 255, 0))
564
- draw = ImageDraw.Draw(txt_overlay)
565
-
566
- try:
567
- font = ImageFont.truetype("DejaVuSans.ttf", text_params['font_size'])
568
- except:
569
- try:
570
- font = ImageFont.truetype("arial.ttf", text_params['font_size'])
571
- except:
572
- font = ImageFont.load_default()
573
-
574
- color_map = {
575
- 'White': (255, 255, 255),
576
- 'Black': (0, 0, 0),
577
- 'Red': (255, 0, 0),
578
- 'Green': (0, 255, 0),
579
- 'Blue': (0, 0, 255),
580
- 'Yellow': (255, 255, 0),
581
- 'Purple': (128, 0, 128)
582
- }
583
-
584
- rgb_color = color_map.get(text_params['color'], (255, 255, 255))
585
- text_color = (*rgb_color, text_params['opacity'])
586
-
587
- text_bbox = draw.textbbox((0, 0), text_params['text'], font=font)
588
- text_width = text_bbox[2] - text_bbox[0]
589
- text_height = text_bbox[3] - text_bbox[1]
590
-
591
- x = int((image.width - text_width) * (text_params['x_position'] / 100))
592
- y = int((image.height - text_height) * (text_params['y_position'] / 100))
593
-
594
- add_text_with_stroke(
595
- draw,
596
- text_params['text'],
597
- x,
598
- y,
599
- font,
600
- text_color,
601
- text_params['thickness']
602
- )
603
-
604
- return Image.alpha_composite(image, txt_overlay)
605
-
606
-
607
- def update_controls(bg_prompt):
608
- """๋ฐฐ๊ฒฝ ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์ปจํŠธ๋กค ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ"""
609
- is_visible = bool(bg_prompt)
610
- return [
611
- gr.update(visible=True), # aspect_ratio๋Š” ํ•ญ์ƒ ํ‘œ์‹œ
612
- gr.update(visible=is_visible) # object_controls
613
- ]
614
-
615
- def update_process_button(img, prompt):
616
- """ํ”„๋กœ์„ธ์Šค ๋ฒ„ํŠผ ์ƒํƒœ ์—…๋ฐ์ดํŠธ"""
617
- return gr.update(
618
- interactive=bool(img and prompt),
619
- variant="primary" if bool(img and prompt) else "secondary"
620
- )
621
- if __name__ == "__main__":
622
- # CUDA ์„ค์ •
623
- if torch.cuda.is_available():
624
- try:
625
- torch.cuda.set_device('cuda:0') # ๋ช…์‹œ์ ์œผ๋กœ cuda:0 ์„ค์ •
626
- torch.backends.cudnn.benchmark = True
627
- torch.backends.cuda.matmul.allow_tf32 = True
628
- except Exception as e:
629
- print(f"CUDA setup warning: {e}")
630
-
631
- if HF_TOKEN:
632
- login(token=HF_TOKEN, add_to_git_credential=False)
633
-
634
- # ๋ชจ๋ธ ์ดˆ๊ธฐํ™”
635
- initialize_models()
636
-
637
- # Gradio UI ์ •์˜
638
- with gr.Blocks(
639
- theme=gr.themes.Soft(),
640
- css=css,
641
- analytics_enabled=False,
642
- cache_examples=False
643
- ) as demo:
644
-
645
- # HTML ํ—ค๋”
646
- gr.HTML("""
647
- <div class="main-title">
648
- <h1>๐ŸŽจGiniGen Canvas</h1>
649
- <p>AI Integrated Image Creator: Extract objects, generate backgrounds, and adjust ratios and positions to create complete images with AI.</p>
650
- </div>
651
- """)
652
-
653
- with gr.Row():
654
- # ์ž…๋ ฅ ์ปฌ๋Ÿผ
655
- with gr.Column(scale=1):
656
- input_image = gr.Image(
657
- type="pil",
658
- label="Upload Image",
659
- interactive=True
660
  )
661
- text_prompt = gr.Textbox(
662
- label="Object to Extract",
663
- placeholder="Enter what you want to extract...",
664
- interactive=True
 
 
 
665
  )
666
-
667
- # ๋ฐฐ๊ฒฝ ๋ฐ ๋น„์œจ ์„ค์ •
668
- with gr.Row():
669
- bg_prompt = gr.Textbox(
670
- label="Background Prompt (optional)",
671
- placeholder="Describe the background...",
672
- interactive=True,
673
- scale=3
674
- )
675
- aspect_ratio = gr.Dropdown(
676
- choices=["1:1", "16:9", "9:16", "4:3"],
677
- value="1:1",
678
- label="Aspect Ratio",
679
- interactive=True,
680
- visible=True,
681
- scale=1
682
- )
683
 
684
- # ์˜ค๋ธŒ์ ํŠธ ์ปจํŠธ๋กค
685
- with gr.Row(visible=False) as object_controls:
686
- # ์œ„์น˜ ์ปจํŠธ๋กค
687
- with gr.Column(scale=1):
688
  position = gr.State(value="bottom-center")
689
- with gr.Row():
690
- btn_top_left = gr.Button("โ†–")
691
- btn_top_center = gr.Button("โ†‘")
692
- btn_top_right = gr.Button("โ†—")
693
- with gr.Row():
694
- btn_middle_left = gr.Button("โ†")
695
- btn_middle_center = gr.Button("โ€ข")
696
- btn_middle_right = gr.Button("โ†’")
697
- with gr.Row():
698
- btn_bottom_left = gr.Button("โ†™")
699
- btn_bottom_center = gr.Button("โ†“")
700
- btn_bottom_right = gr.Button("โ†˜")
701
-
702
- # ํฌ๊ธฐ ์ปจํŠธ๋กค
703
- with gr.Column(scale=1):
704
- scale_slider = gr.Slider(
705
- minimum=10,
706
- maximum=200,
707
- value=50,
708
- step=5,
709
- label="Object Size (%)"
710
- )
711
-
712
- # ํ…์ŠคํŠธ ์ž…๋ ฅ ์„น์…˜
713
- with gr.Group() as text_group:
714
- text_input = gr.Textbox(
715
- label="Text to Add",
716
- placeholder="Enter text..."
717
- )
718
  with gr.Row():
719
- with gr.Column(scale=1):
720
- font_size = gr.Slider(
721
- minimum=10,
722
- maximum=800,
723
- value=400,
724
- step=10,
725
- label="Font Size"
726
- )
727
- thickness = gr.Slider(
728
- minimum=0,
729
- maximum=20,
730
- value=0,
731
- step=1,
732
- label="Text Thickness"
733
- )
734
- color_dropdown = gr.Dropdown(
735
- choices=["White", "Black", "Red", "Green", "Blue", "Yellow", "Purple"],
736
- value="White",
737
- label="Text Color"
738
- )
739
- with gr.Column(scale=1):
740
- opacity_slider = gr.Slider(
741
- minimum=0,
742
- maximum=255,
743
- value=255,
744
- step=1,
745
- label="Opacity"
746
- )
747
- text_x_position = gr.Slider(
748
- minimum=0,
749
- maximum=100,
750
- value=50,
751
- step=1,
752
- label="Text X Position (%)"
753
- )
754
- text_y_position = gr.Slider(
755
- minimum=0,
756
- maximum=100,
757
- value=50,
758
- step=1,
759
- label="Text Y Position (%)"
760
- )
761
-
762
- # ์ฒ˜๋ฆฌ ๋ฒ„ํŠผ
763
- process_btn = gr.Button(
764
- "Process",
765
- variant="primary",
766
- interactive=False
767
- )
768
 
769
- # ์ถœ๋ ฅ ์ปฌ๋Ÿผ
770
- with gr.Column(scale=1):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
771
  combined_image = gr.Image(
772
  label="Combined Result",
773
  show_download_button=True,
774
  type="pil",
775
  height=512
776
  )
 
777
  extracted_image = gr.Image(
778
  label="Extracted Object",
779
  show_download_button=True,
@@ -781,73 +582,55 @@ if __name__ == "__main__":
781
  height=256
782
  )
783
 
784
- # ํ…์ŠคํŠธ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ํ•จ์ˆ˜
785
- def get_text_params():
786
- return {
787
- 'text': text_input.value,
788
- 'font_size': font_size.value,
789
- 'thickness': thickness.value,
790
- 'color': color_dropdown.value,
791
- 'opacity': opacity_slider.value,
792
- 'x_position': text_x_position.value,
793
- 'y_position': text_y_position.value
794
- }
795
-
796
- # ์ด๋ฒคํŠธ ๋ฐ”์ธ๋”ฉ
797
- input_image.change(
798
- fn=update_process_button,
799
- inputs=[input_image, text_prompt],
800
- outputs=process_btn,
801
- queue=False
802
- )
803
-
804
- text_prompt.change(
805
- fn=update_process_button,
806
- inputs=[input_image, text_prompt],
807
- outputs=process_btn,
808
- queue=False
809
- )
810
-
811
- bg_prompt.change(
812
- fn=update_controls,
813
- inputs=[bg_prompt],
814
- outputs=[aspect_ratio, object_controls],
815
- queue=False
816
- )
817
-
818
- # ์œ„์น˜ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ
819
- for btn, pos in [
820
- (btn_top_left, "top-left"), (btn_top_center, "top-center"), (btn_top_right, "top-right"),
821
- (btn_middle_left, "middle-left"), (btn_middle_center, "middle-center"), (btn_middle_right, "middle-right"),
822
- (btn_bottom_left, "bottom-left"), (btn_bottom_center, "bottom-center"), (btn_bottom_right, "bottom-right")
823
- ]:
824
- btn.click(fn=lambda p=pos: p, outputs=position)
825
-
826
- # ๋ฉ”์ธ ํ”„๋กœ์„ธ์Šค ์ด๋ฒคํŠธ
827
- process_btn.click(
828
- fn=process_prompt,
829
- inputs=[
830
- input_image,
831
- text_prompt,
832
- bg_prompt,
833
- aspect_ratio,
834
- position,
835
- scale_slider,
836
- gr.State(get_text_params)
837
- ],
838
- outputs=[combined_image, extracted_image],
839
- queue=True
840
- )
841
-
842
- demo.queue(max_size=1) # ํ ํฌ๊ธฐ ์ œํ•œ
843
  demo.launch(
844
  server_name="0.0.0.0",
845
  server_port=7860,
846
  share=False,
847
- max_threads=1, # ์Šค๋ ˆ๋“œ ์ˆ˜ ์ œํ•œ
848
- enable_queue=True,
849
- cache_examples=False,
850
- show_error=True,
851
- show_tips=False,
852
- quiet=True
853
- )
 
20
  from diffusers import FluxPipeline
21
  from transformers import pipeline, AutoTokenizer, AutoModelForSeq2SeqLM
22
  import gc
 
23
 
24
  def clear_memory():
25
  """๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ ํ•จ์ˆ˜"""
26
  gc.collect()
 
 
 
 
 
 
 
 
 
 
 
27
  try:
28
+ if torch.cuda.is_available():
29
+ with torch.cuda.device(0): # ๋ช…์‹œ์ ์œผ๋กœ device 0 ์‚ฌ์šฉ
30
+ torch.cuda.empty_cache()
31
+ except:
32
+ pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  # GPU ์„ค์ •
35
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # ๋ช…์‹œ์ ์œผ๋กœ cuda:0 ์ง€์ •
36
 
 
 
 
 
 
 
 
 
 
 
37
  # GPU ์„ค์ •์„ try-except๋กœ ๊ฐ์‹ธ๊ธฐ
38
  if torch.cuda.is_available():
39
  try:
 
185
  return base_size * 4 // 3, base_size
186
  return base_size, base_size
187
 
188
+ @spaces.GPU(duration=20) # 40์ดˆ์—์„œ 20์ดˆ๋กœ ๊ฐ์†Œ
189
  def generate_background(prompt: str, aspect_ratio: str) -> Image.Image:
190
  try:
191
  width, height = calculate_dimensions(aspect_ratio)
192
  width, height = adjust_size_to_multiple_of_8(width, height)
193
 
194
+ max_size = 768
 
195
  if width > max_size or height > max_size:
196
  ratio = max_size / max(width, height)
197
  width = int(width * ratio)
198
  height = int(height * ratio)
199
  width, height = adjust_size_to_multiple_of_8(width, height)
200
 
201
+ with timer("Background generation"):
202
+ try:
203
+ with torch.inference_mode():
204
+ image = pipe(
205
+ prompt=prompt,
206
+ width=width,
207
+ height=height,
208
+ num_inference_steps=8,
209
+ guidance_scale=4.0
210
+ ).images[0]
211
+ except Exception as e:
212
+ print(f"Pipeline error: {str(e)}")
213
+ return Image.new('RGB', (width, height), 'white')
214
 
215
  return image
216
  except Exception as e:
 
339
  def on_change_prompt(img: Image.Image | None, prompt: str | None, bg_prompt: str | None = None):
340
  return gr.update(interactive=bool(img and prompt))
341
 
342
+
343
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  def process_prompt(img: Image.Image, prompt: str, bg_prompt: str | None = None,
345
  aspect_ratio: str = "1:1", position: str = "bottom-center",
346
+ scale_percent: float = 100) -> tuple[Image.Image, Image.Image]:
347
  try:
348
+ if img is None or prompt.strip() == "":
349
+ raise gr.Error("Please provide both image and prompt")
350
 
351
+ print(f"Processing with position: {position}, scale: {scale_percent}")
 
 
352
 
353
+ try:
354
+ prompt = translate_to_english(prompt)
355
+ if bg_prompt:
356
+ bg_prompt = translate_to_english(bg_prompt)
357
+ except Exception as e:
358
+ print(f"Translation error (continuing with original text): {str(e)}")
359
+
360
+ results, _ = _process(img, prompt, bg_prompt, aspect_ratio)
361
+
362
+ if bg_prompt:
363
  try:
364
+ combined = combine_with_background(
365
+ foreground=results[2],
366
+ background=results[1],
367
+ position=position,
368
+ scale_percent=scale_percent
369
+ )
370
+ print(f"Combined image created with position: {position}")
371
+ return combined, results[2]
372
  except Exception as e:
373
+ print(f"Combination error: {str(e)}")
374
+ return results[1], results[2]
375
+
376
+ return results[1], results[2]
377
+ except Exception as e:
378
+ print(f"Error in process_prompt: {str(e)}")
379
+ raise gr.Error(str(e))
380
+ finally:
381
+ clear_memory()
382
+
383
  def process_bbox(img: Image.Image, box_input: str) -> tuple[Image.Image, Image.Image]:
384
  try:
385
  if img is None or box_input.strip() == "":
 
419
  return gr.update(interactive=False, variant="secondary")
420
 
421
 
 
422
  # CSS ์ •์˜
423
  css = """
424
  footer {display: none}
 
482
  }
483
  """
484
 
485
+ # UI ๊ตฌ์„ฑ
486
+ # UI ๊ตฌ์„ฑ ๋ถ€๋ถ„์—์„œ process_btn์„ ์œ„๋กœ ์ด๋™ํ•˜๊ณ  position_grid.click ๋ถ€๋ถ„ ์ œ๊ฑฐ
487
+
488
+ # UI ๊ตฌ์„ฑ
489
+ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
490
+ gr.HTML("""
491
+ <div class="main-title">
492
+ <h1>๐ŸŽจGiniGen Canvas</h1>
493
+ <p>AI Integrated Image Creator: Extract objects, generate backgrounds, and adjust ratios and positions to create complete images with AI.</p>
494
+ </div>
495
+ """)
496
+
497
+ with gr.Row():
498
+ with gr.Column(scale=1):
499
+ input_image = gr.Image(
500
+ type="pil",
501
+ label="Upload Image",
502
+ interactive=True
503
+ )
504
+ text_prompt = gr.Textbox(
505
+ label="Object to Extract",
506
+ placeholder="Enter what you want to extract...",
507
+ interactive=True
508
+ )
509
+ with gr.Row():
510
+ bg_prompt = gr.Textbox(
511
+ label="Background Prompt (optional)",
512
+ placeholder="Describe the background...",
513
+ interactive=True,
514
+ scale=3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  )
516
+ aspect_ratio = gr.Dropdown(
517
+ choices=["1:1", "16:9", "9:16", "4:3"],
518
+ value="1:1",
519
+ label="Aspect Ratio",
520
+ interactive=True,
521
+ visible=True,
522
+ scale=1
523
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
 
525
+ with gr.Row(visible=False) as object_controls:
526
+ with gr.Column(scale=1):
527
+ with gr.Row():
 
528
  position = gr.State(value="bottom-center")
529
+ btn_top_left = gr.Button("โ†–")
530
+ btn_top_center = gr.Button("โ†‘")
531
+ btn_top_right = gr.Button("โ†—")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
  with gr.Row():
533
+ btn_middle_left = gr.Button("โ†")
534
+ btn_middle_center = gr.Button("โ€ข")
535
+ btn_middle_right = gr.Button("โ†’")
536
+ with gr.Row():
537
+ btn_bottom_left = gr.Button("โ†™")
538
+ btn_bottom_center = gr.Button("โ†“")
539
+ btn_bottom_right = gr.Button("โ†˜")
540
+ with gr.Column(scale=1):
541
+ scale_slider = gr.Slider(
542
+ minimum=10,
543
+ maximum=200,
544
+ value=50,
545
+ step=5,
546
+ label="Object Size (%)"
547
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
 
549
+ process_btn = gr.Button(
550
+ "Process",
551
+ variant="primary",
552
+ interactive=False
553
+ )
554
+
555
+ # ๊ฐ ๋ฒ„ํŠผ์— ๋Œ€ํ•œ ํด๋ฆญ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
556
+ def update_position(new_position):
557
+ return new_position
558
+
559
+ btn_top_left.click(fn=lambda: update_position("top-left"), outputs=position)
560
+ btn_top_center.click(fn=lambda: update_position("top-center"), outputs=position)
561
+ btn_top_right.click(fn=lambda: update_position("top-right"), outputs=position)
562
+ btn_middle_left.click(fn=lambda: update_position("middle-left"), outputs=position)
563
+ btn_middle_center.click(fn=lambda: update_position("middle-center"), outputs=position)
564
+ btn_middle_right.click(fn=lambda: update_position("middle-right"), outputs=position)
565
+ btn_bottom_left.click(fn=lambda: update_position("bottom-left"), outputs=position)
566
+ btn_bottom_center.click(fn=lambda: update_position("bottom-center"), outputs=position)
567
+ btn_bottom_right.click(fn=lambda: update_position("bottom-right"), outputs=position)
568
+
569
+ with gr.Column(scale=1):
570
+ with gr.Row():
571
  combined_image = gr.Image(
572
  label="Combined Result",
573
  show_download_button=True,
574
  type="pil",
575
  height=512
576
  )
577
+ with gr.Row():
578
  extracted_image = gr.Image(
579
  label="Extracted Object",
580
  show_download_button=True,
 
582
  height=256
583
  )
584
 
585
+ # Event bindings
586
+ input_image.change(
587
+ fn=update_process_button,
588
+ inputs=[input_image, text_prompt],
589
+ outputs=process_btn,
590
+ queue=False
591
+ )
592
+
593
+ text_prompt.change(
594
+ fn=update_process_button,
595
+ inputs=[input_image, text_prompt],
596
+ outputs=process_btn,
597
+ queue=False
598
+ )
599
+
600
+ def update_controls(bg_prompt):
601
+ """๋ฐฐ๊ฒฝ ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์ปจํŠธ๋กค ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ"""
602
+ is_visible = bool(bg_prompt)
603
+ return [
604
+ gr.update(visible=is_visible), # aspect_ratio
605
+ gr.update(visible=is_visible), # object_controls
606
+ ]
607
+
608
+ bg_prompt.change(
609
+ fn=update_controls,
610
+ inputs=bg_prompt,
611
+ outputs=[aspect_ratio, object_controls],
612
+ queue=False
613
+ )
614
+
615
+ process_btn.click(
616
+ fn=process_prompt,
617
+ inputs=[
618
+ input_image,
619
+ text_prompt,
620
+ bg_prompt,
621
+ aspect_ratio,
622
+ position,
623
+ scale_slider
624
+ ],
625
+ outputs=[combined_image, extracted_image],
626
+ queue=True
627
+ )
628
+
629
+
630
+ demo.queue(max_size=5) # ํ ํฌ๊ธฐ ์ œํ•œ
 
 
 
 
 
 
 
 
 
 
 
 
 
631
  demo.launch(
632
  server_name="0.0.0.0",
633
  server_port=7860,
634
  share=False,
635
+ max_threads=2 # ์Šค๋ ˆ๋“œ ์ˆ˜ ์ œํ•œ
636
+ )