Spaces:
Sleeping
Sleeping
""" | |
이미지 생성 UI 컴포넌트 및 기능 | |
""" | |
import gradio as gr | |
import time | |
from config import ( | |
HIGH_PRICE_OPTION, | |
LOW_PRICE_OPTION, | |
CUSTOM, | |
GEN_IMAGE_DESC, | |
WORLDCUPABLE_IMAGE_CNT, | |
imageStyleMap, | |
) | |
from image_generator import genPromptAndImage | |
def create_image_tab(): | |
"""이미지 생성 탭 UI 생성 함수""" | |
with gr.Tab("이미지 생성"): | |
current_img_url = gr.State("") | |
selectedImageIndex = gr.State(0) | |
entryImageUrls = gr.State([]) # 저장된 URL 목록 | |
with gr.Column(): # Column을 사용하여 수직 배열 | |
with gr.Row(): | |
with gr.Column(): # Column을 사용하여 수직 배열 | |
userPrompt = gr.TextArea( | |
label="교재 지문 (필수)", | |
value=GEN_IMAGE_DESC, | |
max_lines=5, | |
) | |
additionalComment = gr.Textbox( | |
label="추가 희망사항 (옵셔널)", lines=2, value="" | |
) | |
with gr.Column(): | |
with gr.Accordion("상세 설정", open=False): | |
with gr.Column(): # Column을 사용하여 수직 배열 | |
with gr.Row(): | |
highImgCnt = gr.Textbox( | |
label="비싼 이미지", | |
value="0", | |
interactive=False, | |
) | |
lowImgCnt = gr.Textbox( | |
label="싼 이미지", value="0", interactive=False | |
) | |
totalPrice = gr.Textbox( | |
label="총 이미지 생성 비용(원)", | |
value="0", | |
interactive=False, | |
) | |
genModelKind = gr.Radio( | |
[LOW_PRICE_OPTION, HIGH_PRICE_OPTION], | |
label="이미지 생성 모델", | |
value=LOW_PRICE_OPTION, | |
) | |
with gr.Row(): | |
aspectRatio = gr.Radio( | |
["1:1", "4:3", "16:9", CUSTOM], | |
label="이미지 비율", | |
value="16:9", | |
scale=4, | |
) | |
imgWidth = gr.Number( | |
label="너비 (32의배수)", | |
value=1440, | |
scale=1, | |
visible=False, | |
maximum=1440, | |
minimum=256, | |
step=32, | |
) | |
imgHeight = gr.Number( | |
label="높이 (32의배수)", | |
value=768, | |
scale=1, | |
visible=False, | |
maximum=1440, | |
minimum=256, | |
step=32, | |
) | |
def custom_aspect_ratio(ratio): | |
if ratio == CUSTOM: | |
return ( | |
gr.update( | |
value=HIGH_PRICE_OPTION, | |
interactive=False, | |
), | |
gr.update(visible=True), | |
gr.update(visible=True), | |
) | |
else: | |
return ( | |
gr.update(interactive=True), | |
gr.update(visible=False), | |
gr.update(visible=False), | |
) | |
aspectRatio.change( | |
fn=custom_aspect_ratio, | |
inputs=aspectRatio, | |
outputs=[genModelKind, imgWidth, imgHeight], | |
) | |
imageFormatList = ["png", "jpg", "webp"] | |
imageFormatRadio = gr.Radio( | |
imageFormatList, label="이미지 포멧", value="webp" | |
) | |
styleList = list(imageStyleMap.keys()) | |
imageStyleRadio = gr.Radio( | |
styleList, label="이미지 스타일", value="알아서" | |
) | |
with gr.Accordion("Prompt info", open=False): | |
fluxPrompt = gr.Textbox( | |
label="Flux Prompt", value="", lines=10 | |
) | |
with gr.Row(): | |
genSmartImage = gr.Button("교재 지문 삽화 이미지 생성!") | |
removeEntryWorldcupBtn = gr.Button("현재 이미지 제거") | |
with gr.Column(): | |
progressBar = gr.Image( | |
label=None, interactive=False, visible=False, height=28 | |
) | |
entryListGallery = gr.Gallery( | |
label="이미지 월드컵 진출 이미지", | |
preview=True, | |
allow_preview=True, | |
interactive=False, | |
) | |
with gr.Row(): | |
entryCount = gr.Markdown("## 이미지 월드컵 진출 이미지 수: 0") | |
goImageWorldCupBtn = gr.Button( | |
"이미지 월드컵 하러가기", visible=False | |
) | |
def setSelectedImageIndex(evt: gr.SelectData, state=None): | |
try: | |
if evt is None: | |
return 0 | |
return evt.index | |
except: | |
return 0 | |
def removeSelectedEntry(entryList, selectedIndex): | |
if selectedIndex is None or len(entryList) == 0: | |
return gr.update(value=entryList), entryList | |
del entryList[selectedIndex] | |
return ( | |
entryList, | |
entryList, | |
f"## 이미지 월드컵 진출 이미지 수: {len(entryList)}", | |
) | |
# 왠지 모르게 마지막 요소를 지우면 preview=True 가 안되서 이렇게 함. | |
def reSelectedEntry(entryList): | |
return gr.update(selected_index=len(entryList) - 1, preview=True) | |
removeEntryWorldcupBtn.click( | |
fn=removeSelectedEntry, | |
inputs=[entryImageUrls, selectedImageIndex], | |
outputs=[entryListGallery, entryImageUrls, entryCount], | |
).then( | |
fn=reSelectedEntry, | |
inputs=entryImageUrls, | |
outputs=entryListGallery, | |
) | |
# 이벤트 처리. | |
# 입력값이 변경될 때마다 Flux Prompt 초기화 | |
for component in [ | |
userPrompt, | |
aspectRatio, | |
imageStyleRadio, | |
additionalComment, | |
]: | |
component.change(fn=lambda x: "", inputs=fluxPrompt, outputs=fluxPrompt) | |
genImageInputs = [ | |
userPrompt, | |
additionalComment, | |
fluxPrompt, | |
aspectRatio, | |
imageStyleRadio, | |
imageFormatRadio, | |
genModelKind, | |
highImgCnt, | |
lowImgCnt, | |
imgWidth, | |
imgHeight, | |
] | |
def show_progress(): | |
return gr.update(label="이미지 생성 중", visible=True) | |
def hide_progress(): | |
return gr.update(visible=False) | |
genSmartImage.click(fn=show_progress, outputs=progressBar).then( | |
fn=genPromptAndImage, | |
inputs=genImageInputs, | |
outputs=[ | |
fluxPrompt, | |
highImgCnt, | |
lowImgCnt, | |
totalPrice, | |
current_img_url, | |
progressBar, | |
], | |
).then( | |
fn=lambda entryList, imgUrl: ( | |
gr.update( | |
value=[*entryList, imgUrl], | |
preview=True, | |
selected_index=len(entryList), | |
), | |
entryList + [imgUrl], | |
len(entryList), | |
gr.update(visible=False), | |
), | |
inputs=[entryImageUrls, current_img_url], | |
outputs=[ | |
entryListGallery, | |
entryImageUrls, | |
selectedImageIndex, | |
progressBar, | |
], | |
).then( | |
fn=lambda entryImageUrls: f"## 이미지 월드컵 진출 이미지 수: {len(entryImageUrls)}", | |
inputs=entryImageUrls, | |
outputs=entryCount, | |
).then( | |
fn=lambda entryCountTxt, entryList: ( | |
[ | |
entryCountTxt + " (이미지 월드컵 가능!)", | |
gr.update(visible=False), | |
gr.Info( | |
f"{len(entryList)}개의 이미지가 만들어졌습니다. '이미지 월드컵'을 시작하실 수 있습니다!", | |
duration=5, | |
), | |
] | |
if len(entryList) in WORLDCUPABLE_IMAGE_CNT | |
else [entryCountTxt, gr.update(visible=False)] | |
), | |
inputs=[entryCount, entryImageUrls], | |
outputs=[entryCount, goImageWorldCupBtn], | |
) | |
return entryImageUrls | |