Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
@@ -26,6 +26,8 @@ from PIL import Image
|
|
26 |
from gradio_client import Client, handle_file
|
27 |
import uuid
|
28 |
|
|
|
|
|
29 |
|
30 |
def clear_memory():
|
31 |
"""메모리 정리 함수"""
|
@@ -702,8 +704,64 @@ def update_controls(bg_prompt):
|
|
702 |
gr.update(visible=is_visible), # object_controls
|
703 |
]
|
704 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
705 |
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
706 |
-
position = gr.State(value="bottom-center")
|
707 |
|
708 |
gr.HTML("""
|
709 |
<div class="main-title">
|
@@ -712,153 +770,173 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
712 |
</div>
|
713 |
""")
|
714 |
|
715 |
-
with gr.
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
label="Object to Extract",
|
727 |
-
placeholder="Enter what you want to extract...",
|
728 |
-
interactive=True
|
729 |
-
)
|
730 |
-
with gr.Row():
|
731 |
-
bg_prompt = gr.Textbox(
|
732 |
-
label="Background Prompt (optional)",
|
733 |
-
placeholder="Describe the background...",
|
734 |
-
interactive=True,
|
735 |
-
scale=3
|
736 |
-
)
|
737 |
-
aspect_ratio = gr.Dropdown(
|
738 |
-
choices=["1:1", "16:9", "9:16", "4:3"],
|
739 |
-
value="1:1",
|
740 |
-
label="Aspect Ratio",
|
741 |
-
interactive=True,
|
742 |
-
visible=True,
|
743 |
-
scale=1
|
744 |
-
)
|
745 |
-
|
746 |
-
with gr.Group(elem_classes="controls-panel", visible=False) as object_controls:
|
747 |
-
with gr.Column(scale=1):
|
748 |
-
position = gr.State(value="bottom-center") # 초기값 설정
|
749 |
-
with gr.Row():
|
750 |
-
btn_top_left = gr.Button("↖", elem_classes="position-btn")
|
751 |
-
btn_top_center = gr.Button("↑", elem_classes="position-btn")
|
752 |
-
btn_top_right = gr.Button("↗", elem_classes="position-btn")
|
753 |
-
with gr.Row():
|
754 |
-
btn_middle_left = gr.Button("←", elem_classes="position-btn")
|
755 |
-
btn_middle_center = gr.Button("•", elem_classes="position-btn")
|
756 |
-
btn_middle_right = gr.Button("→", elem_classes="position-btn")
|
757 |
-
with gr.Row():
|
758 |
-
btn_bottom_left = gr.Button("↙", elem_classes="position-btn")
|
759 |
-
btn_bottom_center = gr.Button("↓", elem_classes="position-btn", value="selected")
|
760 |
-
btn_bottom_right = gr.Button("↘", elem_classes="position-btn")
|
761 |
-
with gr.Column(scale=1):
|
762 |
-
scale_slider = gr.Slider(
|
763 |
-
minimum=10,
|
764 |
-
maximum=200,
|
765 |
-
value=50,
|
766 |
-
step=5,
|
767 |
-
label="Object Size (%)"
|
768 |
)
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
with gr.Group():
|
791 |
-
with gr.
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
800 |
)
|
801 |
-
|
802 |
-
with gr.Row():
|
803 |
-
with gr.Column(scale=1):
|
804 |
-
font_choice = gr.Dropdown(
|
805 |
-
choices=["Default", "Korean Regular"], # "Korean Son" 제거
|
806 |
-
|
807 |
-
value="Default",
|
808 |
-
label="Font Selection",
|
809 |
-
interactive=True
|
810 |
-
)
|
811 |
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
maximum=255,
|
836 |
-
value=255,
|
837 |
-
step=1,
|
838 |
-
label="Opacity"
|
839 |
-
)
|
840 |
-
x_position = gr.Slider(
|
841 |
-
minimum=0,
|
842 |
-
maximum=100,
|
843 |
-
value=50,
|
844 |
-
step=1,
|
845 |
-
label="Left(0%)~Right(100%)"
|
846 |
)
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
step=1,
|
852 |
-
label="High(0%)~Low(100%)"
|
853 |
)
|
854 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
855 |
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
862 |
|
863 |
# CSS 클래스를 위한 스타일 추가
|
864 |
gr.HTML("""
|
@@ -870,7 +948,7 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
870 |
</style>
|
871 |
""")
|
872 |
|
873 |
-
# 버튼 클릭 이벤트 바인딩
|
874 |
position_mapping = {
|
875 |
btn_top_left: "top-left",
|
876 |
btn_top_center: "top-center",
|
@@ -885,10 +963,9 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
885 |
|
886 |
for btn, pos in position_mapping.items():
|
887 |
btn.click(
|
888 |
-
fn=lambda pos=pos: update_position(pos),
|
889 |
outputs=position
|
890 |
)
|
891 |
-
|
892 |
|
893 |
# 이벤트 바인딩
|
894 |
bg_prompt.change(
|
@@ -926,12 +1003,11 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
926 |
queue=True
|
927 |
)
|
928 |
|
929 |
-
# 이벤트 바인딩 부분에서
|
930 |
add_text_btn.click(
|
931 |
fn=add_text_to_image,
|
932 |
inputs=[
|
933 |
-
combined_image,
|
934 |
-
text_input,
|
935 |
font_size,
|
936 |
color_dropdown,
|
937 |
opacity_slider,
|
@@ -942,10 +1018,22 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
|
942 |
font_choice
|
943 |
],
|
944 |
outputs=combined_image,
|
945 |
-
api_name="add_text"
|
946 |
)
|
947 |
-
|
948 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
949 |
|
950 |
demo.queue(max_size=5)
|
951 |
demo.launch(
|
|
|
26 |
from gradio_client import Client, handle_file
|
27 |
import uuid
|
28 |
|
29 |
+
import random
|
30 |
+
from datetime import datetime
|
31 |
|
32 |
def clear_memory():
|
33 |
"""메모리 정리 함수"""
|
|
|
704 |
gr.update(visible=is_visible), # object_controls
|
705 |
]
|
706 |
|
707 |
+
|
708 |
+
# 저장 디렉토리 설정
|
709 |
+
SAVE_DIR = "saved_images"
|
710 |
+
if not os.path.exists(SAVE_DIR):
|
711 |
+
os.makedirs(SAVE_DIR, exist_ok=True)
|
712 |
+
|
713 |
+
MAX_SEED = np.iinfo(np.int32).max
|
714 |
+
MAX_IMAGE_SIZE = 1024
|
715 |
+
|
716 |
+
def save_generated_image(image, prompt):
|
717 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
718 |
+
unique_id = str(uuid.uuid4())[:8]
|
719 |
+
filename = f"{timestamp}_{unique_id}.png"
|
720 |
+
filepath = os.path.join(SAVE_DIR, filename)
|
721 |
+
|
722 |
+
image.save(filepath)
|
723 |
+
return filepath
|
724 |
+
|
725 |
+
@spaces.GPU(duration=60)
|
726 |
+
def generate_image(
|
727 |
+
prompt: str,
|
728 |
+
seed: int,
|
729 |
+
randomize_seed: bool,
|
730 |
+
width: int,
|
731 |
+
height: int,
|
732 |
+
guidance_scale: float,
|
733 |
+
num_inference_steps: int,
|
734 |
+
progress: gr.Progress = gr.Progress()
|
735 |
+
):
|
736 |
+
try:
|
737 |
+
if randomize_seed:
|
738 |
+
seed = random.randint(0, MAX_SEED)
|
739 |
+
|
740 |
+
generator = torch.Generator(device=device).manual_seed(seed)
|
741 |
+
|
742 |
+
with torch.inference_mode():
|
743 |
+
image = pipe(
|
744 |
+
prompt=prompt,
|
745 |
+
width=width,
|
746 |
+
height=height,
|
747 |
+
num_inference_steps=num_inference_steps,
|
748 |
+
guidance_scale=guidance_scale,
|
749 |
+
generator=generator,
|
750 |
+
).images[0]
|
751 |
+
|
752 |
+
filepath = save_generated_image(image, prompt)
|
753 |
+
return image, seed
|
754 |
+
|
755 |
+
except Exception as e:
|
756 |
+
raise gr.Error(f"Image generation failed: {str(e)}")
|
757 |
+
finally:
|
758 |
+
clear_memory()
|
759 |
+
|
760 |
+
|
761 |
+
|
762 |
+
|
763 |
with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
|
764 |
+
position = gr.State(value="bottom-center")
|
765 |
|
766 |
gr.HTML("""
|
767 |
<div class="main-title">
|
|
|
770 |
</div>
|
771 |
""")
|
772 |
|
773 |
+
with gr.Tabs():
|
774 |
+
with gr.Tab("Edit & Combine"):
|
775 |
+
with gr.Row(equal_height=True):
|
776 |
+
# 왼쪽 패널 (입력)
|
777 |
+
with gr.Column(scale=1):
|
778 |
+
with gr.Group(elem_classes="input-panel"):
|
779 |
+
input_image = gr.Image(
|
780 |
+
type="pil",
|
781 |
+
label="Upload Image",
|
782 |
+
interactive=True,
|
783 |
+
height=400
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
784 |
)
|
785 |
+
text_prompt = gr.Textbox(
|
786 |
+
label="Object to Extract",
|
787 |
+
placeholder="Enter what you want to extract...",
|
788 |
+
interactive=True
|
789 |
+
)
|
790 |
+
with gr.Row():
|
791 |
+
bg_prompt = gr.Textbox(
|
792 |
+
label="Background Prompt (optional)",
|
793 |
+
placeholder="Describe the background...",
|
794 |
+
interactive=True,
|
795 |
+
scale=3
|
796 |
+
)
|
797 |
+
aspect_ratio = gr.Dropdown(
|
798 |
+
choices=["1:1", "16:9", "9:16", "4:3"],
|
799 |
+
value="1:1",
|
800 |
+
label="Aspect Ratio",
|
801 |
+
interactive=True,
|
802 |
+
visible=True,
|
803 |
+
scale=1
|
804 |
+
)
|
805 |
+
|
806 |
+
with gr.Group(elem_classes="controls-panel", visible=False) as object_controls:
|
807 |
+
with gr.Column(scale=1):
|
808 |
+
position = gr.State(value="bottom-center")
|
809 |
+
with gr.Row():
|
810 |
+
btn_top_left = gr.Button("↖", elem_classes="position-btn")
|
811 |
+
btn_top_center = gr.Button("↑", elem_classes="position-btn")
|
812 |
+
btn_top_right = gr.Button("↗", elem_classes="position-btn")
|
813 |
+
with gr.Row():
|
814 |
+
btn_middle_left = gr.Button("←", elem_classes="position-btn")
|
815 |
+
btn_middle_center = gr.Button("•", elem_classes="position-btn")
|
816 |
+
btn_middle_right = gr.Button("→", elem_classes="position-btn")
|
817 |
+
with gr.Row():
|
818 |
+
btn_bottom_left = gr.Button("↙", elem_classes="position-btn")
|
819 |
+
btn_bottom_center = gr.Button("↓", elem_classes="position-btn", value="selected")
|
820 |
+
btn_bottom_right = gr.Button("↘", elem_classes="position-btn")
|
821 |
+
with gr.Column(scale=1):
|
822 |
+
scale_slider = gr.Slider(
|
823 |
+
minimum=10,
|
824 |
+
maximum=200,
|
825 |
+
value=50,
|
826 |
+
step=5,
|
827 |
+
label="Object Size (%)"
|
828 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
829 |
|
830 |
+
process_btn = gr.Button(
|
831 |
+
"Process",
|
832 |
+
variant="primary",
|
833 |
+
interactive=False,
|
834 |
+
size="lg"
|
835 |
+
)
|
836 |
+
|
837 |
+
# 오른쪽 패널 (출력)
|
838 |
+
with gr.Column(scale=1):
|
839 |
+
with gr.Group(elem_classes="output-panel"):
|
840 |
+
combined_image = gr.Image(
|
841 |
+
label="Combined Result",
|
842 |
+
show_download_button=True,
|
843 |
+
type="pil",
|
844 |
+
height=400
|
845 |
+
)
|
846 |
+
|
847 |
+
with gr.Accordion("Text Insertion Options", open=False):
|
848 |
+
with gr.Group():
|
849 |
+
with gr.Row():
|
850 |
+
text_input = gr.Textbox(
|
851 |
+
label="Text Content",
|
852 |
+
placeholder="Enter text to add..."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
853 |
)
|
854 |
+
text_position_type = gr.Radio(
|
855 |
+
choices=["Text Over Image", "Text Behind Image"],
|
856 |
+
value="Text Over Image",
|
857 |
+
label="Text Position"
|
|
|
|
|
858 |
)
|
859 |
+
|
860 |
+
with gr.Row():
|
861 |
+
with gr.Column(scale=1):
|
862 |
+
font_choice = gr.Dropdown(
|
863 |
+
choices=["Default", "Korean Regular"],
|
864 |
+
value="Default",
|
865 |
+
label="Font Selection",
|
866 |
+
interactive=True
|
867 |
+
)
|
868 |
+
font_size = gr.Slider(
|
869 |
+
minimum=10,
|
870 |
+
maximum=200,
|
871 |
+
value=40,
|
872 |
+
step=5,
|
873 |
+
label="Font Size"
|
874 |
+
)
|
875 |
+
color_dropdown = gr.Dropdown(
|
876 |
+
choices=["White", "Black", "Red", "Green", "Blue", "Yellow", "Purple"],
|
877 |
+
value="White",
|
878 |
+
label="Text Color"
|
879 |
+
)
|
880 |
+
thickness = gr.Slider(
|
881 |
+
minimum=0,
|
882 |
+
maximum=10,
|
883 |
+
value=1,
|
884 |
+
step=1,
|
885 |
+
label="Text Thickness"
|
886 |
+
)
|
887 |
+
with gr.Column(scale=1):
|
888 |
+
opacity_slider = gr.Slider(
|
889 |
+
minimum=0,
|
890 |
+
maximum=255,
|
891 |
+
value=255,
|
892 |
+
step=1,
|
893 |
+
label="Opacity"
|
894 |
+
)
|
895 |
+
x_position = gr.Slider(
|
896 |
+
minimum=0,
|
897 |
+
maximum=100,
|
898 |
+
value=50,
|
899 |
+
step=1,
|
900 |
+
label="Left(0%)~Right(100%)"
|
901 |
+
)
|
902 |
+
y_position = gr.Slider(
|
903 |
+
minimum=0,
|
904 |
+
maximum=100,
|
905 |
+
value=50,
|
906 |
+
step=1,
|
907 |
+
label="High(0%)~Low(100%)"
|
908 |
+
)
|
909 |
+
add_text_btn = gr.Button("Apply Text", variant="primary")
|
910 |
+
|
911 |
+
extracted_image = gr.Image(
|
912 |
+
label="Extracted Object",
|
913 |
+
show_download_button=True,
|
914 |
+
type="pil",
|
915 |
+
height=200
|
916 |
+
)
|
917 |
|
918 |
+
with gr.Tab("Generate Image"):
|
919 |
+
with gr.Column():
|
920 |
+
gen_prompt = gr.Textbox(
|
921 |
+
label="Generation Prompt",
|
922 |
+
placeholder="Enter your image generation prompt..."
|
923 |
+
)
|
924 |
+
with gr.Row():
|
925 |
+
gen_width = gr.Slider(512, 1024, 768, step=64, label="Width")
|
926 |
+
gen_height = gr.Slider(512, 1024, 768, step=64, label="Height")
|
927 |
+
|
928 |
+
with gr.Row():
|
929 |
+
guidance_scale = gr.Slider(1, 20, 7.5, step=0.5, label="Guidance Scale")
|
930 |
+
num_steps = gr.Slider(1, 50, 30, step=1, label="Number of Steps")
|
931 |
+
|
932 |
+
with gr.Row():
|
933 |
+
seed = gr.Number(label="Seed", value=-1)
|
934 |
+
randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
|
935 |
+
|
936 |
+
generate_btn = gr.Button("Generate Image", variant="primary")
|
937 |
+
|
938 |
+
output_image = gr.Image(label="Generated Image", type="pil")
|
939 |
+
output_seed = gr.Number(label="Used Seed", interactive=False)
|
940 |
|
941 |
# CSS 클래스를 위한 스타일 추가
|
942 |
gr.HTML("""
|
|
|
948 |
</style>
|
949 |
""")
|
950 |
|
951 |
+
# 버튼 클릭 이벤트 바인딩
|
952 |
position_mapping = {
|
953 |
btn_top_left: "top-left",
|
954 |
btn_top_center: "top-center",
|
|
|
963 |
|
964 |
for btn, pos in position_mapping.items():
|
965 |
btn.click(
|
966 |
+
fn=lambda pos=pos: update_position(pos),
|
967 |
outputs=position
|
968 |
)
|
|
|
969 |
|
970 |
# 이벤트 바인딩
|
971 |
bg_prompt.change(
|
|
|
1003 |
queue=True
|
1004 |
)
|
1005 |
|
|
|
1006 |
add_text_btn.click(
|
1007 |
fn=add_text_to_image,
|
1008 |
inputs=[
|
1009 |
+
combined_image,
|
1010 |
+
text_input,
|
1011 |
font_size,
|
1012 |
color_dropdown,
|
1013 |
opacity_slider,
|
|
|
1018 |
font_choice
|
1019 |
],
|
1020 |
outputs=combined_image,
|
1021 |
+
api_name="add_text"
|
1022 |
)
|
|
|
1023 |
|
1024 |
+
generate_btn.click(
|
1025 |
+
fn=generate_image,
|
1026 |
+
inputs=[
|
1027 |
+
gen_prompt,
|
1028 |
+
seed,
|
1029 |
+
randomize_seed,
|
1030 |
+
gen_width,
|
1031 |
+
gen_height,
|
1032 |
+
guidance_scale,
|
1033 |
+
num_steps,
|
1034 |
+
],
|
1035 |
+
outputs=[output_image, output_seed]
|
1036 |
+
)
|
1037 |
|
1038 |
demo.queue(max_size=5)
|
1039 |
demo.launch(
|