Update app.py
Browse files
app.py
CHANGED
@@ -427,65 +427,102 @@ def update_box_button(img, box_input):
|
|
427 |
return gr.update(interactive=False, variant="secondary")
|
428 |
|
429 |
|
430 |
-
# CSS 정의
|
431 |
css = """
|
432 |
footer {display: none}
|
433 |
.main-title {
|
434 |
text-align: center;
|
435 |
-
margin:
|
436 |
-
padding:
|
437 |
-
background: #
|
438 |
-
border-radius:
|
|
|
439 |
}
|
440 |
.main-title h1 {
|
441 |
color: #2196F3;
|
442 |
-
font-size: 2.
|
443 |
-
margin-bottom: 0.
|
|
|
444 |
}
|
445 |
.main-title p {
|
446 |
-
color: #
|
447 |
-
font-size: 1.
|
|
|
448 |
}
|
449 |
.container {
|
450 |
max-width: 1200px;
|
451 |
margin: auto;
|
452 |
padding: 20px;
|
453 |
}
|
454 |
-
.
|
455 |
-
margin-top: 1em;
|
456 |
-
}
|
457 |
-
.input-group {
|
458 |
background: white;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
459 |
padding: 1em;
|
460 |
border-radius: 8px;
|
461 |
-
|
462 |
}
|
463 |
-
.
|
464 |
-
|
465 |
-
|
|
|
|
|
|
|
466 |
border-radius: 8px;
|
467 |
-
|
468 |
}
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
475 |
cursor: pointer;
|
476 |
-
transition: background 0.3s ease;
|
477 |
}
|
478 |
-
|
479 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
480 |
}
|
481 |
.position-btn {
|
|
|
|
|
|
|
|
|
|
|
482 |
transition: all 0.3s ease;
|
483 |
}
|
484 |
.position-btn:hover {
|
485 |
-
background
|
486 |
}
|
487 |
.position-btn.selected {
|
488 |
-
background
|
489 |
color: white;
|
490 |
}
|
491 |
"""
|
@@ -631,168 +668,165 @@ def add_text_to_image(
|
|
631 |
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
632 |
gr.HTML("""
|
633 |
<div class="main-title">
|
634 |
-
<h1>🎨GiniGen Canvas-o3</h1>
|
635 |
-
<p>
|
636 |
</div>
|
637 |
""")
|
638 |
|
639 |
-
with gr.Row():
|
|
|
640 |
with gr.Column(scale=1):
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
)
|
646 |
-
text_prompt = gr.Textbox(
|
647 |
-
label="Object to Extract",
|
648 |
-
placeholder="Enter what you want to extract...",
|
649 |
-
interactive=True
|
650 |
-
)
|
651 |
-
with gr.Row():
|
652 |
-
bg_prompt = gr.Textbox(
|
653 |
-
label="Background Prompt (optional)",
|
654 |
-
placeholder="Describe the background...",
|
655 |
interactive=True,
|
656 |
-
|
657 |
)
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
interactive=True,
|
663 |
-
visible=True,
|
664 |
-
scale=1
|
665 |
)
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
btn_bottom_center = gr.Button("↓")
|
681 |
-
btn_bottom_right = gr.Button("↘")
|
682 |
-
with gr.Column(scale=1):
|
683 |
-
scale_slider = gr.Slider(
|
684 |
-
minimum=10,
|
685 |
-
maximum=200,
|
686 |
-
value=50,
|
687 |
-
step=5,
|
688 |
-
label="Object Size (%)"
|
689 |
)
|
690 |
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
696 |
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
type="pil",
|
703 |
-
height=512
|
704 |
)
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
value="Text Over Image",
|
717 |
-
label="Text Position Type",
|
718 |
-
interactive=True
|
719 |
-
)
|
720 |
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
772 |
|
773 |
-
with gr.Row():
|
774 |
extracted_image = gr.Image(
|
775 |
label="Extracted Object",
|
776 |
show_download_button=True,
|
777 |
type="pil",
|
778 |
-
height=
|
779 |
)
|
780 |
|
781 |
-
#
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
btn_middle_right.click(fn=lambda: update_position("middle-right"), outputs=position)
|
791 |
-
btn_bottom_left.click(fn=lambda: update_position("bottom-left"), outputs=position)
|
792 |
-
btn_bottom_center.click(fn=lambda: update_position("bottom-center"), outputs=position)
|
793 |
-
btn_bottom_right.click(fn=lambda: update_position("bottom-right"), outputs=position)
|
794 |
-
|
795 |
-
# Event bindings
|
796 |
input_image.change(
|
797 |
fn=update_process_button,
|
798 |
inputs=[input_image, text_prompt],
|
@@ -807,14 +841,6 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
807 |
queue=False
|
808 |
)
|
809 |
|
810 |
-
def update_controls(bg_prompt):
|
811 |
-
"""배경 프롬프트 입력 여부에 따라 컨트롤 표시 업데이트"""
|
812 |
-
is_visible = bool(bg_prompt)
|
813 |
-
return [
|
814 |
-
gr.update(visible=is_visible), # aspect_ratio
|
815 |
-
gr.update(visible=is_visible), # object_controls
|
816 |
-
]
|
817 |
-
|
818 |
bg_prompt.change(
|
819 |
fn=update_controls,
|
820 |
inputs=bg_prompt,
|
@@ -822,6 +848,20 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
822 |
queue=False
|
823 |
)
|
824 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
825 |
process_btn.click(
|
826 |
fn=process_prompt,
|
827 |
inputs=[
|
@@ -836,7 +876,6 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
836 |
queue=True
|
837 |
)
|
838 |
|
839 |
-
# 텍스트 추가 버튼 이벤트 연결 수정
|
840 |
add_text_btn.click(
|
841 |
fn=add_text_to_image,
|
842 |
inputs=[
|
@@ -849,15 +888,15 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
849 |
y_position,
|
850 |
thickness,
|
851 |
text_position_type,
|
852 |
-
font_choice
|
853 |
],
|
854 |
outputs=combined_image
|
855 |
)
|
856 |
|
857 |
-
demo.queue(max_size=5)
|
858 |
demo.launch(
|
859 |
server_name="0.0.0.0",
|
860 |
server_port=7860,
|
861 |
share=False,
|
862 |
-
max_threads=2
|
863 |
)
|
|
|
427 |
return gr.update(interactive=False, variant="secondary")
|
428 |
|
429 |
|
|
|
430 |
css = """
|
431 |
footer {display: none}
|
432 |
.main-title {
|
433 |
text-align: center;
|
434 |
+
margin: 1em 0;
|
435 |
+
padding: 1.5em;
|
436 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
437 |
+
border-radius: 15px;
|
438 |
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
439 |
}
|
440 |
.main-title h1 {
|
441 |
color: #2196F3;
|
442 |
+
font-size: 2.8em;
|
443 |
+
margin-bottom: 0.3em;
|
444 |
+
font-weight: 700;
|
445 |
}
|
446 |
.main-title p {
|
447 |
+
color: #555;
|
448 |
+
font-size: 1.3em;
|
449 |
+
line-height: 1.4;
|
450 |
}
|
451 |
.container {
|
452 |
max-width: 1200px;
|
453 |
margin: auto;
|
454 |
padding: 20px;
|
455 |
}
|
456 |
+
.input-panel, .output-panel {
|
|
|
|
|
|
|
457 |
background: white;
|
458 |
+
padding: 1.5em;
|
459 |
+
border-radius: 12px;
|
460 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
461 |
+
margin-bottom: 1em;
|
462 |
+
}
|
463 |
+
.controls-panel {
|
464 |
+
background: #f8f9fa;
|
465 |
padding: 1em;
|
466 |
border-radius: 8px;
|
467 |
+
margin: 1em 0;
|
468 |
}
|
469 |
+
.image-display {
|
470 |
+
min-height: 512px;
|
471 |
+
display: flex;
|
472 |
+
align-items: center;
|
473 |
+
justify-content: center;
|
474 |
+
background: #fafafa;
|
475 |
border-radius: 8px;
|
476 |
+
margin: 1em 0;
|
477 |
}
|
478 |
+
.example-section {
|
479 |
+
text-align: center;
|
480 |
+
padding: 2em;
|
481 |
+
background: #f5f5f5;
|
482 |
+
border-radius: 12px;
|
483 |
+
margin-top: 2em;
|
484 |
+
}
|
485 |
+
.example-section img {
|
486 |
+
max-width: 100%;
|
487 |
+
border-radius: 8px;
|
488 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
489 |
+
}
|
490 |
+
.accordion {
|
491 |
+
border: 1px solid #e0e0e0;
|
492 |
+
border-radius: 8px;
|
493 |
+
margin: 1em 0;
|
494 |
+
}
|
495 |
+
.accordion-header {
|
496 |
+
padding: 1em;
|
497 |
+
background: #f5f5f5;
|
498 |
cursor: pointer;
|
|
|
499 |
}
|
500 |
+
.accordion-content {
|
501 |
+
padding: 1em;
|
502 |
+
display: none;
|
503 |
+
}
|
504 |
+
.accordion.open .accordion-content {
|
505 |
+
display: block;
|
506 |
+
}
|
507 |
+
.position-grid {
|
508 |
+
display: grid;
|
509 |
+
grid-template-columns: repeat(3, 1fr);
|
510 |
+
gap: 8px;
|
511 |
+
margin: 1em 0;
|
512 |
}
|
513 |
.position-btn {
|
514 |
+
padding: 10px;
|
515 |
+
border: 1px solid #ddd;
|
516 |
+
border-radius: 4px;
|
517 |
+
background: white;
|
518 |
+
cursor: pointer;
|
519 |
transition: all 0.3s ease;
|
520 |
}
|
521 |
.position-btn:hover {
|
522 |
+
background: #e3f2fd;
|
523 |
}
|
524 |
.position-btn.selected {
|
525 |
+
background: #2196F3;
|
526 |
color: white;
|
527 |
}
|
528 |
"""
|
|
|
668 |
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
669 |
gr.HTML("""
|
670 |
<div class="main-title">
|
671 |
+
<h1>🎨 GiniGen Canvas-o3</h1>
|
672 |
+
<p>Advanced Image Processing Tool: Remove backgrounds, generate new ones, and add customized text to your images.</p>
|
673 |
</div>
|
674 |
""")
|
675 |
|
676 |
+
with gr.Row(equal_height=True):
|
677 |
+
# 왼쪽 패널 (입력)
|
678 |
with gr.Column(scale=1):
|
679 |
+
with gr.Box(elem_classes="input-panel"):
|
680 |
+
input_image = gr.Image(
|
681 |
+
type="pil",
|
682 |
+
label="Upload Image",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
683 |
interactive=True,
|
684 |
+
height=400
|
685 |
)
|
686 |
+
text_prompt = gr.Textbox(
|
687 |
+
label="Object to Extract",
|
688 |
+
placeholder="Enter what you want to extract...",
|
689 |
+
interactive=True
|
|
|
|
|
|
|
690 |
)
|
691 |
+
with gr.Row():
|
692 |
+
bg_prompt = gr.Textbox(
|
693 |
+
label="Background Prompt (optional)",
|
694 |
+
placeholder="Describe the background...",
|
695 |
+
interactive=True,
|
696 |
+
scale=3
|
697 |
+
)
|
698 |
+
aspect_ratio = gr.Dropdown(
|
699 |
+
choices=["1:1", "16:9", "9:16", "4:3"],
|
700 |
+
value="1:1",
|
701 |
+
label="Aspect Ratio",
|
702 |
+
interactive=True,
|
703 |
+
visible=True,
|
704 |
+
scale=1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
705 |
)
|
706 |
|
707 |
+
with gr.Box(elem_classes="controls-panel", visible=False) as object_controls:
|
708 |
+
with gr.Column(scale=1):
|
709 |
+
with gr.Row():
|
710 |
+
position = gr.State(value="bottom-center")
|
711 |
+
btn_top_left = gr.Button("↖")
|
712 |
+
btn_top_center = gr.Button("↑")
|
713 |
+
btn_top_right = gr.Button("↗")
|
714 |
+
with gr.Row():
|
715 |
+
btn_middle_left = gr.Button("←")
|
716 |
+
btn_middle_center = gr.Button("•")
|
717 |
+
btn_middle_right = gr.Button("→")
|
718 |
+
with gr.Row():
|
719 |
+
btn_bottom_left = gr.Button("↙")
|
720 |
+
btn_bottom_center = gr.Button("↓")
|
721 |
+
btn_bottom_right = gr.Button("↘")
|
722 |
+
with gr.Column(scale=1):
|
723 |
+
scale_slider = gr.Slider(
|
724 |
+
minimum=10,
|
725 |
+
maximum=200,
|
726 |
+
value=50,
|
727 |
+
step=5,
|
728 |
+
label="Object Size (%)"
|
729 |
+
)
|
730 |
|
731 |
+
process_btn = gr.Button(
|
732 |
+
"Process",
|
733 |
+
variant="primary",
|
734 |
+
interactive=False,
|
735 |
+
size="lg"
|
|
|
|
|
736 |
)
|
737 |
+
|
738 |
+
# 오른쪽 패널 (출력)
|
739 |
+
with gr.Column(scale=1):
|
740 |
+
with gr.Box(elem_classes="output-panel"):
|
741 |
+
with gr.Tab("Result"):
|
742 |
+
combined_image = gr.Image(
|
743 |
+
label="Combined Result",
|
744 |
+
show_download_button=True,
|
745 |
+
type="pil",
|
746 |
+
height=400
|
747 |
+
)
|
|
|
|
|
|
|
|
|
748 |
|
749 |
+
# 텍스트 삽입 옵션을 Accordion으로 변경
|
750 |
+
with gr.Accordion("Text Insertion Options", open=False):
|
751 |
+
with gr.Group():
|
752 |
+
with gr.Row():
|
753 |
+
text_input = gr.Textbox(
|
754 |
+
label="Text Content",
|
755 |
+
placeholder="Enter text to add..."
|
756 |
+
)
|
757 |
+
text_position_type = gr.Radio(
|
758 |
+
choices=["Text Over Image", "Text Behind Image"],
|
759 |
+
value="Text Over Image",
|
760 |
+
label="Text Position"
|
761 |
+
)
|
762 |
+
|
763 |
+
with gr.Row():
|
764 |
+
with gr.Column(scale=1):
|
765 |
+
font_choice = gr.Dropdown(
|
766 |
+
choices=["Default", "Korean Regular", "Korean Son"],
|
767 |
+
value="Default",
|
768 |
+
label="Font Selection",
|
769 |
+
interactive=True
|
770 |
+
)
|
771 |
+
font_size = gr.Slider(
|
772 |
+
minimum=10,
|
773 |
+
maximum=200,
|
774 |
+
value=40,
|
775 |
+
step=5,
|
776 |
+
label="Font Size"
|
777 |
+
)
|
778 |
+
color_dropdown = gr.Dropdown(
|
779 |
+
choices=["White", "Black", "Red", "Green", "Blue", "Yellow", "Purple"],
|
780 |
+
value="White",
|
781 |
+
label="Text Color"
|
782 |
+
)
|
783 |
+
thickness = gr.Slider(
|
784 |
+
minimum=0,
|
785 |
+
maximum=10,
|
786 |
+
value=1,
|
787 |
+
step=1,
|
788 |
+
label="Text Thickness"
|
789 |
+
)
|
790 |
+
with gr.Column(scale=1):
|
791 |
+
opacity_slider = gr.Slider(
|
792 |
+
minimum=0,
|
793 |
+
maximum=255,
|
794 |
+
value=255,
|
795 |
+
step=1,
|
796 |
+
label="Opacity"
|
797 |
+
)
|
798 |
+
x_position = gr.Slider(
|
799 |
+
minimum=0,
|
800 |
+
maximum=100,
|
801 |
+
value=50,
|
802 |
+
step=1,
|
803 |
+
label="X Position (%)"
|
804 |
+
)
|
805 |
+
y_position = gr.Slider(
|
806 |
+
minimum=0,
|
807 |
+
maximum=100,
|
808 |
+
value=50,
|
809 |
+
step=1,
|
810 |
+
label="Y Position (%)"
|
811 |
+
)
|
812 |
+
add_text_btn = gr.Button("Apply Text", variant="primary")
|
813 |
|
|
|
814 |
extracted_image = gr.Image(
|
815 |
label="Extracted Object",
|
816 |
show_download_button=True,
|
817 |
type="pil",
|
818 |
+
height=200
|
819 |
)
|
820 |
|
821 |
+
# 예제 이미지 섹션 추가
|
822 |
+
gr.HTML("""
|
823 |
+
<div class="example-section">
|
824 |
+
<h3>Example Results</h3>
|
825 |
+
<img src="example.png" alt="Example results" />
|
826 |
+
</div>
|
827 |
+
""")
|
828 |
+
|
829 |
+
# 이벤트 바인딩
|
|
|
|
|
|
|
|
|
|
|
|
|
830 |
input_image.change(
|
831 |
fn=update_process_button,
|
832 |
inputs=[input_image, text_prompt],
|
|
|
841 |
queue=False
|
842 |
)
|
843 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
844 |
bg_prompt.change(
|
845 |
fn=update_controls,
|
846 |
inputs=bg_prompt,
|
|
|
848 |
queue=False
|
849 |
)
|
850 |
|
851 |
+
# 위치 버튼 이벤트
|
852 |
+
for btn, pos in [
|
853 |
+
(btn_top_left, "top-left"), (btn_top_center, "top-center"),
|
854 |
+
(btn_top_right, "top-right"), (btn_middle_left, "middle-left"),
|
855 |
+
(btn_middle_center, "middle-center"), (btn_middle_right, "middle-right"),
|
856 |
+
(btn_bottom_left, "bottom-left"), (btn_bottom_center, "bottom-center"),
|
857 |
+
(btn_bottom_right, "bottom-right")
|
858 |
+
]:
|
859 |
+
btn.click(
|
860 |
+
fn=lambda p=pos: p,
|
861 |
+
outputs=position,
|
862 |
+
queue=False
|
863 |
+
)
|
864 |
+
|
865 |
process_btn.click(
|
866 |
fn=process_prompt,
|
867 |
inputs=[
|
|
|
876 |
queue=True
|
877 |
)
|
878 |
|
|
|
879 |
add_text_btn.click(
|
880 |
fn=add_text_to_image,
|
881 |
inputs=[
|
|
|
888 |
y_position,
|
889 |
thickness,
|
890 |
text_position_type,
|
891 |
+
font_choice
|
892 |
],
|
893 |
outputs=combined_image
|
894 |
)
|
895 |
|
896 |
+
demo.queue(max_size=5)
|
897 |
demo.launch(
|
898 |
server_name="0.0.0.0",
|
899 |
server_port=7860,
|
900 |
share=False,
|
901 |
+
max_threads=2
|
902 |
)
|