Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
@@ -588,112 +588,196 @@ def update_box_button(img, box_input):
|
|
588 |
|
589 |
|
590 |
css = """
|
591 |
-
|
|
|
|
|
|
|
|
|
592 |
.main-title {
|
593 |
text-align: center;
|
594 |
-
margin:
|
595 |
-
padding:
|
596 |
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
597 |
border-radius: 15px;
|
598 |
-
box-shadow: 0
|
|
|
599 |
}
|
|
|
600 |
.main-title h1 {
|
601 |
color: #2196F3;
|
602 |
-
font-size:
|
603 |
-
margin-bottom: 0.
|
604 |
font-weight: 700;
|
|
|
605 |
}
|
|
|
606 |
.main-title p {
|
607 |
color: #555;
|
608 |
-
font-size: 1.
|
609 |
-
line-height: 1.
|
610 |
-
|
611 |
-
|
612 |
-
max-width: 1200px;
|
613 |
-
margin: auto;
|
614 |
-
padding: 20px;
|
615 |
}
|
|
|
|
|
616 |
.input-panel, .output-panel {
|
617 |
background: white;
|
618 |
-
padding:
|
619 |
-
border-radius:
|
620 |
-
box-shadow: 0
|
621 |
-
margin-bottom:
|
|
|
|
|
|
|
|
|
|
|
622 |
}
|
|
|
|
|
623 |
.controls-panel {
|
624 |
background: #f8f9fa;
|
625 |
-
padding:
|
626 |
-
border-radius:
|
627 |
-
margin:
|
|
|
628 |
}
|
|
|
|
|
629 |
.image-display {
|
630 |
min-height: 512px;
|
631 |
display: flex;
|
632 |
align-items: center;
|
633 |
justify-content: center;
|
634 |
background: #fafafa;
|
635 |
-
border-radius: 8px;
|
636 |
-
margin: 1em 0;
|
637 |
-
}
|
638 |
-
.example-section {
|
639 |
-
text-align: center;
|
640 |
-
padding: 2em;
|
641 |
-
background: #f5f5f5;
|
642 |
border-radius: 12px;
|
643 |
-
margin
|
644 |
-
|
645 |
-
.example-section img {
|
646 |
-
max-width: 100%;
|
647 |
-
border-radius: 8px;
|
648 |
-
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
649 |
-
}
|
650 |
-
.accordion {
|
651 |
-
border: 1px solid #e0e0e0;
|
652 |
-
border-radius: 8px;
|
653 |
-
margin: 1em 0;
|
654 |
-
}
|
655 |
-
.accordion-header {
|
656 |
-
padding: 1em;
|
657 |
-
background: #f5f5f5;
|
658 |
-
cursor: pointer;
|
659 |
-
}
|
660 |
-
.accordion-content {
|
661 |
-
padding: 1em;
|
662 |
-
display: none;
|
663 |
-
}
|
664 |
-
.accordion.open .accordion-content {
|
665 |
-
display: block;
|
666 |
-
}
|
667 |
-
.position-grid {
|
668 |
-
display: grid;
|
669 |
-
grid-template-columns: repeat(3, 1fr);
|
670 |
-
gap: 8px;
|
671 |
-
margin: 1em 0;
|
672 |
}
|
673 |
|
674 |
-
|
675 |
.position-btn {
|
676 |
-
padding:
|
677 |
-
border:
|
678 |
-
border-radius:
|
679 |
background: white;
|
680 |
cursor: pointer;
|
681 |
-
transition: all 0.
|
682 |
-
width:
|
683 |
-
height:
|
684 |
display: flex;
|
685 |
align-items: center;
|
686 |
justify-content: center;
|
|
|
|
|
687 |
}
|
688 |
|
689 |
.position-btn:hover {
|
690 |
background: #e3f2fd;
|
|
|
|
|
691 |
}
|
692 |
|
693 |
.position-btn.selected {
|
694 |
background-color: #2196F3;
|
695 |
color: white;
|
696 |
border-color: #1976D2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
697 |
}
|
698 |
"""
|
699 |
|
@@ -862,14 +946,21 @@ def update_controls(bg_prompt):
|
|
862 |
|
863 |
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
864 |
position = gr.State(value="bottom-center")
|
865 |
-
|
|
|
866 |
gr.HTML("""
|
867 |
<div class="main-title">
|
868 |
<h1>๐จ GiniGen Canvas-o3</h1>
|
869 |
<p>Remove background of specified objects, generate new backgrounds, and insert text over or behind images with prompts.</p>
|
870 |
</div>
|
871 |
""")
|
|
|
|
|
|
|
|
|
|
|
872 |
|
|
|
873 |
with gr.Row(equal_height=True):
|
874 |
# ์ผ์ชฝ ํจ๋ (์
๋ ฅ)
|
875 |
with gr.Column(scale=1):
|
@@ -881,8 +972,10 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
881 |
type="pil",
|
882 |
label="Upload Image",
|
883 |
interactive=True,
|
884 |
-
height=400
|
|
|
885 |
)
|
|
|
886 |
with gr.Group():
|
887 |
inpaint_prompt = gr.Textbox(
|
888 |
label="Inpainting Prompt",
|
@@ -899,8 +992,10 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
899 |
text_prompt = gr.Textbox(
|
900 |
label="Object to Extract",
|
901 |
placeholder="Enter what you want to extract...",
|
902 |
-
interactive=True
|
|
|
903 |
)
|
|
|
904 |
with gr.Row():
|
905 |
bg_prompt = gr.Textbox(
|
906 |
label="Background Prompt (optional)",
|
@@ -1052,18 +1147,64 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
1052 |
btn_bottom_right: "bottom-right"
|
1053 |
}
|
1054 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1055 |
for btn, pos in position_mapping.items():
|
1056 |
btn.click(
|
1057 |
-
fn=lambda
|
1058 |
-
|
|
|
|
|
|
|
|
|
|
|
1059 |
)
|
1060 |
|
|
|
1061 |
inpaint_btn.click(
|
1062 |
-
fn=
|
|
|
|
|
|
|
|
|
1063 |
inputs=[input_image, mask_input, inpaint_prompt],
|
1064 |
-
outputs=input_image
|
1065 |
)
|
1066 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1067 |
bg_prompt.change(
|
1068 |
fn=update_controls,
|
1069 |
inputs=bg_prompt,
|
@@ -1085,19 +1226,7 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
1085 |
queue=False
|
1086 |
)
|
1087 |
|
1088 |
-
|
1089 |
-
fn=process_prompt,
|
1090 |
-
inputs=[
|
1091 |
-
input_image,
|
1092 |
-
text_prompt,
|
1093 |
-
bg_prompt,
|
1094 |
-
aspect_ratio,
|
1095 |
-
position,
|
1096 |
-
scale_slider
|
1097 |
-
],
|
1098 |
-
outputs=[combined_image, extracted_image],
|
1099 |
-
queue=True
|
1100 |
-
)
|
1101 |
|
1102 |
add_text_btn.click(
|
1103 |
fn=add_text_to_image,
|
|
|
588 |
|
589 |
|
590 |
css = """
|
591 |
+
/* ๊ธฐ๋ณธ ๋ ์ด์์ */
|
592 |
+
footer {display: none !important}
|
593 |
+
body {background: #f5f7fa !important}
|
594 |
+
|
595 |
+
/* ๋ฉ์ธ ํ์ดํ */
|
596 |
.main-title {
|
597 |
text-align: center;
|
598 |
+
margin: 1.5em auto;
|
599 |
+
padding: 2em;
|
600 |
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
601 |
border-radius: 15px;
|
602 |
+
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
603 |
+
max-width: 1200px;
|
604 |
}
|
605 |
+
|
606 |
.main-title h1 {
|
607 |
color: #2196F3;
|
608 |
+
font-size: 3em;
|
609 |
+
margin-bottom: 0.5em;
|
610 |
font-weight: 700;
|
611 |
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
612 |
}
|
613 |
+
|
614 |
.main-title p {
|
615 |
color: #555;
|
616 |
+
font-size: 1.4em;
|
617 |
+
line-height: 1.6;
|
618 |
+
max-width: 800px;
|
619 |
+
margin: 0 auto;
|
|
|
|
|
|
|
620 |
}
|
621 |
+
|
622 |
+
/* ํจ๋ ์คํ์ผ๋ง */
|
623 |
.input-panel, .output-panel {
|
624 |
background: white;
|
625 |
+
padding: 2em;
|
626 |
+
border-radius: 15px;
|
627 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
628 |
+
margin-bottom: 1.5em;
|
629 |
+
transition: all 0.3s ease;
|
630 |
+
}
|
631 |
+
|
632 |
+
.input-panel:hover, .output-panel:hover {
|
633 |
+
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
|
634 |
}
|
635 |
+
|
636 |
+
/* ์ปจํธ๋กค ํจ๋ */
|
637 |
.controls-panel {
|
638 |
background: #f8f9fa;
|
639 |
+
padding: 1.5em;
|
640 |
+
border-radius: 12px;
|
641 |
+
margin: 1.5em 0;
|
642 |
+
border: 1px solid #e9ecef;
|
643 |
}
|
644 |
+
|
645 |
+
/* ์ด๋ฏธ์ง ๋์คํ๋ ์ด */
|
646 |
.image-display {
|
647 |
min-height: 512px;
|
648 |
display: flex;
|
649 |
align-items: center;
|
650 |
justify-content: center;
|
651 |
background: #fafafa;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
652 |
border-radius: 12px;
|
653 |
+
margin: 1.5em 0;
|
654 |
+
border: 2px dashed #e0e0e0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
655 |
}
|
656 |
|
657 |
+
/* ๋ฒํผ ์คํ์ผ๋ง */
|
658 |
.position-btn {
|
659 |
+
padding: 12px;
|
660 |
+
border: 2px solid #ddd;
|
661 |
+
border-radius: 8px;
|
662 |
background: white;
|
663 |
cursor: pointer;
|
664 |
+
transition: all 0.2s ease;
|
665 |
+
width: 48px;
|
666 |
+
height: 48px;
|
667 |
display: flex;
|
668 |
align-items: center;
|
669 |
justify-content: center;
|
670 |
+
font-size: 1.2em;
|
671 |
+
margin: 4px;
|
672 |
}
|
673 |
|
674 |
.position-btn:hover {
|
675 |
background: #e3f2fd;
|
676 |
+
transform: translateY(-2px);
|
677 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
678 |
}
|
679 |
|
680 |
.position-btn.selected {
|
681 |
background-color: #2196F3;
|
682 |
color: white;
|
683 |
border-color: #1976D2;
|
684 |
+
box-shadow: 0 4px 12px rgba(33,150,243,0.3);
|
685 |
+
}
|
686 |
+
|
687 |
+
/* ๊ทธ๋ฆฌ๋ ๋ ์ด์์ */
|
688 |
+
.position-grid {
|
689 |
+
display: grid;
|
690 |
+
grid-template-columns: repeat(3, 1fr);
|
691 |
+
gap: 10px;
|
692 |
+
margin: 1.5em 0;
|
693 |
+
padding: 10px;
|
694 |
+
background: #f5f5f5;
|
695 |
+
border-radius: 12px;
|
696 |
+
}
|
697 |
+
|
698 |
+
/* ์
๋ ฅ ํ๋ ์คํ์ผ๋ง */
|
699 |
+
input[type="text"], textarea {
|
700 |
+
border: 2px solid #e0e0e0;
|
701 |
+
border-radius: 8px;
|
702 |
+
padding: 12px;
|
703 |
+
font-size: 1.1em;
|
704 |
+
transition: all 0.3s ease;
|
705 |
+
}
|
706 |
+
|
707 |
+
input[type="text"]:focus, textarea:focus {
|
708 |
+
border-color: #2196F3;
|
709 |
+
box-shadow: 0 0 0 3px rgba(33,150,243,0.2);
|
710 |
+
}
|
711 |
+
|
712 |
+
/* ์ฌ๋ผ์ด๋ ์คํ์ผ๋ง */
|
713 |
+
.slider-container {
|
714 |
+
margin: 1.5em 0;
|
715 |
+
}
|
716 |
+
|
717 |
+
.slider {
|
718 |
+
height: 6px;
|
719 |
+
background: #e0e0e0;
|
720 |
+
border-radius: 3px;
|
721 |
+
}
|
722 |
+
|
723 |
+
.slider-handle {
|
724 |
+
width: 20px;
|
725 |
+
height: 20px;
|
726 |
+
background: #2196F3;
|
727 |
+
border: 2px solid white;
|
728 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
729 |
+
}
|
730 |
+
|
731 |
+
/* ์ํ ๋ฉ์์ง */
|
732 |
+
.status-message {
|
733 |
+
padding: 10px;
|
734 |
+
border-radius: 8px;
|
735 |
+
margin: 10px 0;
|
736 |
+
font-size: 0.9em;
|
737 |
+
transition: all 0.3s ease;
|
738 |
+
}
|
739 |
+
|
740 |
+
.status-success {
|
741 |
+
background: #e8f5e9;
|
742 |
+
color: #2e7d32;
|
743 |
+
border: 1px solid #a5d6a7;
|
744 |
+
}
|
745 |
+
|
746 |
+
.status-error {
|
747 |
+
background: #ffebee;
|
748 |
+
color: #c62828;
|
749 |
+
border: 1px solid #ef9a9a;
|
750 |
+
}
|
751 |
+
|
752 |
+
/* ๋ฐ์ํ ๋์์ธ */
|
753 |
+
@media (max-width: 768px) {
|
754 |
+
.main-title h1 {
|
755 |
+
font-size: 2em;
|
756 |
+
}
|
757 |
+
|
758 |
+
.main-title p {
|
759 |
+
font-size: 1.1em;
|
760 |
+
}
|
761 |
+
|
762 |
+
.input-panel, .output-panel {
|
763 |
+
padding: 1em;
|
764 |
+
}
|
765 |
+
|
766 |
+
.position-btn {
|
767 |
+
width: 40px;
|
768 |
+
height: 40px;
|
769 |
+
font-size: 1em;
|
770 |
+
}
|
771 |
+
}
|
772 |
+
|
773 |
+
/* ์ ๋๋ฉ์ด์
ํจ๊ณผ */
|
774 |
+
@keyframes fadeIn {
|
775 |
+
from {opacity: 0; transform: translateY(10px);}
|
776 |
+
to {opacity: 1; transform: translateY(0);}
|
777 |
+
}
|
778 |
+
|
779 |
+
.fade-in {
|
780 |
+
animation: fadeIn 0.3s ease-out;
|
781 |
}
|
782 |
"""
|
783 |
|
|
|
946 |
|
947 |
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
948 |
position = gr.State(value="bottom-center")
|
949 |
+
processing_status = gr.State(value="idle")
|
950 |
+
|
951 |
gr.HTML("""
|
952 |
<div class="main-title">
|
953 |
<h1>๐จ GiniGen Canvas-o3</h1>
|
954 |
<p>Remove background of specified objects, generate new backgrounds, and insert text over or behind images with prompts.</p>
|
955 |
</div>
|
956 |
""")
|
957 |
+
|
958 |
+
status_message = gr.HTML(
|
959 |
+
value='<div class="status-message"></div>',
|
960 |
+
visible=False
|
961 |
+
)
|
962 |
|
963 |
+
|
964 |
with gr.Row(equal_height=True):
|
965 |
# ์ผ์ชฝ ํจ๋ (์
๋ ฅ)
|
966 |
with gr.Column(scale=1):
|
|
|
972 |
type="pil",
|
973 |
label="Upload Image",
|
974 |
interactive=True,
|
975 |
+
height=400,
|
976 |
+
elem_classes="fade-in"
|
977 |
)
|
978 |
+
|
979 |
with gr.Group():
|
980 |
inpaint_prompt = gr.Textbox(
|
981 |
label="Inpainting Prompt",
|
|
|
992 |
text_prompt = gr.Textbox(
|
993 |
label="Object to Extract",
|
994 |
placeholder="Enter what you want to extract...",
|
995 |
+
interactive=True,
|
996 |
+
elem_classes="fade-in"
|
997 |
)
|
998 |
+
|
999 |
with gr.Row():
|
1000 |
bg_prompt = gr.Textbox(
|
1001 |
label="Background Prompt (optional)",
|
|
|
1147 |
btn_bottom_right: "bottom-right"
|
1148 |
}
|
1149 |
|
1150 |
+
def update_ui_state(component_id, value, is_error=False):
|
1151 |
+
"""UI ์ํ ์
๋ฐ์ดํธ ์ ํธ๋ฆฌํฐ"""
|
1152 |
+
class_name = "status-error" if is_error else "status-success"
|
1153 |
+
return gr.update(
|
1154 |
+
value=f'<div class="status-message {class_name}">{value}</div>',
|
1155 |
+
visible=True
|
1156 |
+
)
|
1157 |
+
|
1158 |
+
# ๋ฒํผ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ
|
1159 |
for btn, pos in position_mapping.items():
|
1160 |
btn.click(
|
1161 |
+
fn=lambda p=pos: debug_event("position_update", p),
|
1162 |
+
inputs=[],
|
1163 |
+
outputs=[status_message]
|
1164 |
+
).then(
|
1165 |
+
fn=update_position_and_ui,
|
1166 |
+
inputs=[gr.State(pos)],
|
1167 |
+
outputs=[position] + list(position_mapping.keys())
|
1168 |
)
|
1169 |
|
1170 |
+
# ์ธํ์ธํ
ํ๋ก์ธ์ค
|
1171 |
inpaint_btn.click(
|
1172 |
+
fn=lambda: debug_event("inpainting_start"),
|
1173 |
+
inputs=[],
|
1174 |
+
outputs=[status_message]
|
1175 |
+
).then(
|
1176 |
+
fn=process_inpainting_with_feedback,
|
1177 |
inputs=[input_image, mask_input, inpaint_prompt],
|
1178 |
+
outputs=[input_image, status_message]
|
1179 |
)
|
1180 |
|
1181 |
+
# ํ๋ก์ธ์ค ๋ฒํผ
|
1182 |
+
process_btn.click(
|
1183 |
+
fn=lambda: debug_event("process_start"),
|
1184 |
+
inputs=[],
|
1185 |
+
outputs=[status_message]
|
1186 |
+
).then(
|
1187 |
+
fn=process_prompt,
|
1188 |
+
inputs=[
|
1189 |
+
input_image,
|
1190 |
+
text_prompt,
|
1191 |
+
bg_prompt,
|
1192 |
+
aspect_ratio,
|
1193 |
+
position,
|
1194 |
+
scale_slider
|
1195 |
+
],
|
1196 |
+
outputs=[combined_image, extracted_image, status_message]
|
1197 |
+
)
|
1198 |
+
|
1199 |
+
|
1200 |
+
|
1201 |
+
for btn, pos in position_mapping.items():
|
1202 |
+
btn.click(
|
1203 |
+
fn=lambda pos=pos: update_position(pos),
|
1204 |
+
outputs=position
|
1205 |
+
)
|
1206 |
+
|
1207 |
+
|
1208 |
bg_prompt.change(
|
1209 |
fn=update_controls,
|
1210 |
inputs=bg_prompt,
|
|
|
1226 |
queue=False
|
1227 |
)
|
1228 |
|
1229 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1230 |
|
1231 |
add_text_btn.click(
|
1232 |
fn=add_text_to_image,
|