Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
|
| 2 |
import gradio as gr
|
| 3 |
import numpy as np
|
| 4 |
import librosa
|
|
@@ -49,7 +49,6 @@ def map_acoustic_to_emotion(features, baseline_features=None):
|
|
| 49 |
tempo_norm = min(features["tempo"] / 200, 1)
|
| 50 |
pitch_norm = min(features["pitch"] * 2, 1)
|
| 51 |
|
| 52 |
-
# 기준점이 있는 경우 상대적 변화 계산
|
| 53 |
if baseline_features:
|
| 54 |
energy_norm = (features["energy"] / baseline_features["energy"]) * 50
|
| 55 |
tempo_norm = (features["tempo"] / baseline_features["tempo"])
|
|
@@ -106,6 +105,7 @@ def map_acoustic_to_emotion(features, baseline_features=None):
|
|
| 106 |
|
| 107 |
return emotions
|
| 108 |
|
|
|
|
| 109 |
def generate_image_from_prompt(prompt):
|
| 110 |
"""이미지 생성 함수"""
|
| 111 |
print(f"Generating image with prompt: {prompt}")
|
|
@@ -165,17 +165,59 @@ def generate_detailed_prompt(text, emotions, text_sentiment):
|
|
| 165 |
|
| 166 |
return prompt
|
| 167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
def create_interface():
|
| 169 |
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
| 170 |
state = gr.State({
|
| 171 |
"user_name": "",
|
| 172 |
-
"baseline_features": None,
|
| 173 |
"reflections": [],
|
| 174 |
"voice_analysis": None,
|
| 175 |
"final_prompt": ""
|
| 176 |
})
|
| 177 |
|
| 178 |
-
# 헤더
|
| 179 |
header = gr.Markdown("# 디지털 굿판")
|
| 180 |
user_display = gr.Markdown("")
|
| 181 |
|
|
@@ -183,13 +225,13 @@ def create_interface():
|
|
| 183 |
# 입장
|
| 184 |
with gr.Tab("입장"):
|
| 185 |
gr.Markdown("### 디지털 굿판에 오신 것을 환영합니다")
|
| 186 |
-
name_input = gr.Textbox(label="이름을 알려주세요")
|
| 187 |
start_btn = gr.Button("여정 시작하기")
|
| 188 |
|
| 189 |
# 기준 설정
|
| 190 |
-
with gr.Tab("기준 설정"):
|
| 191 |
-
gr.Markdown("### 축원의 문장을 평온한 마음으로 읽어주세요
|
| 192 |
-
|
| 193 |
baseline_audio = gr.Audio(
|
| 194 |
label="축원 문장 녹음하기",
|
| 195 |
sources=["microphone"],
|
|
@@ -199,15 +241,21 @@ def create_interface():
|
|
| 199 |
baseline_status = gr.Markdown("")
|
| 200 |
|
| 201 |
# 청신
|
| 202 |
-
with gr.Tab("청신"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
with gr.Row():
|
| 204 |
-
audio_path = os.path.abspath(os.path.join("assets", "main_music.mp3"))
|
| 205 |
audio = gr.Audio(
|
| 206 |
-
value=
|
| 207 |
type="filepath",
|
| 208 |
label="온천천의 소리",
|
| 209 |
interactive=False,
|
| 210 |
-
autoplay=
|
| 211 |
)
|
| 212 |
with gr.Column():
|
| 213 |
reflection_input = gr.Textbox(
|
|
@@ -219,187 +267,131 @@ def create_interface():
|
|
| 219 |
headers=["시간", "감상", "감정 분석"],
|
| 220 |
label="기록된 감상들"
|
| 221 |
)
|
|
|
|
| 222 |
|
| 223 |
# 기원
|
| 224 |
-
with gr.Tab("기원"):
|
| 225 |
-
gr.Markdown("## 기원 - 목소리로 전하기")
|
| 226 |
with gr.Row():
|
| 227 |
with gr.Column():
|
| 228 |
voice_input = gr.Audio(
|
| 229 |
label="나누고 싶은 이야기를 들려주세요",
|
| 230 |
sources=["microphone"],
|
| 231 |
-
type="filepath"
|
| 232 |
-
interactive=True
|
| 233 |
)
|
| 234 |
clear_btn = gr.Button("녹음 지우기")
|
|
|
|
| 235 |
|
| 236 |
with gr.Column():
|
| 237 |
-
transcribed_text = gr.Textbox(
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
)
|
| 241 |
-
voice_emotion = gr.Textbox(
|
| 242 |
-
label="음성 감정 분석",
|
| 243 |
-
interactive=False
|
| 244 |
-
)
|
| 245 |
-
text_emotion = gr.Textbox(
|
| 246 |
-
label="텍스트 감정 분석",
|
| 247 |
-
interactive=False
|
| 248 |
-
)
|
| 249 |
-
analyze_btn = gr.Button("분석하기")
|
| 250 |
|
| 251 |
# 송신
|
| 252 |
-
with gr.Tab("송신"):
|
| 253 |
gr.Markdown("## 송신 - 시각화 결과")
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
result_image = gr.Image(
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
|
| 266 |
def start_journey(name):
|
| 267 |
-
|
|
|
|
|
|
|
| 268 |
welcome_text = f"""
|
| 269 |
# 환영합니다, {name}님의 디지털 굿판
|
| 270 |
|
| 271 |
## 굿판의 세계관 🌌
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
voice_result += "\n\n[기준점 대비 분석]\n"
|
| 354 |
-
voice_result += f"기준 상태와 비교한 감정 강도 변화: {emotions['intensity']-50:.1f}%"
|
| 355 |
-
|
| 356 |
-
text_result = f"텍스트 감정 분석 (1-5): {text_sentiment['score']}"
|
| 357 |
-
prompt = generate_detailed_prompt(text, emotions, text_sentiment)
|
| 358 |
-
|
| 359 |
-
return state, text, voice_result, text_result, prompt
|
| 360 |
-
except Exception as e:
|
| 361 |
-
return state, f"오류 발생: {str(e)}", "", "", ""
|
| 362 |
-
|
| 363 |
-
# 이벤트 연결 부분
|
| 364 |
-
analyze_btn.click(
|
| 365 |
-
fn=analyze_voice,
|
| 366 |
-
inputs=[voice_input, state],
|
| 367 |
-
outputs=[state, transcribed_text, voice_emotion, text_emotion, final_prompt]
|
| 368 |
-
)
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
set_baseline_btn.click(
|
| 372 |
-
fn=set_baseline,
|
| 373 |
-
inputs=[baseline_audio, state],
|
| 374 |
-
outputs=[state, baseline_status, tabs]
|
| 375 |
-
)
|
| 376 |
-
|
| 377 |
-
save_btn.click(
|
| 378 |
-
fn=save_reflection,
|
| 379 |
-
inputs=[reflection_input, state],
|
| 380 |
-
outputs=[state, reflections_display]
|
| 381 |
-
)
|
| 382 |
-
|
| 383 |
-
clear_btn.click(
|
| 384 |
-
fn=clear_voice_input,
|
| 385 |
-
inputs=[],
|
| 386 |
-
outputs=[voice_input]
|
| 387 |
-
)
|
| 388 |
-
|
| 389 |
-
analyze_btn.click(
|
| 390 |
-
fn=analyze_voice,
|
| 391 |
-
inputs=[voice_input, state],
|
| 392 |
-
outputs=[state, transcribed_text, voice_emotion, text_emotion, final_prompt]
|
| 393 |
-
)
|
| 394 |
-
|
| 395 |
-
generate_btn.click(
|
| 396 |
-
fn=generate_image_from_prompt,
|
| 397 |
-
inputs=[final_prompt],
|
| 398 |
-
outputs=[result_image]
|
| 399 |
-
)
|
| 400 |
-
|
| 401 |
-
return app
|
| 402 |
|
| 403 |
if __name__ == "__main__":
|
| 404 |
-
|
| 405 |
-
|
|
|
|
| 1 |
+
# Part 1/3 - Setup and Utilities
|
| 2 |
import gradio as gr
|
| 3 |
import numpy as np
|
| 4 |
import librosa
|
|
|
|
| 49 |
tempo_norm = min(features["tempo"] / 200, 1)
|
| 50 |
pitch_norm = min(features["pitch"] * 2, 1)
|
| 51 |
|
|
|
|
| 52 |
if baseline_features:
|
| 53 |
energy_norm = (features["energy"] / baseline_features["energy"]) * 50
|
| 54 |
tempo_norm = (features["tempo"] / baseline_features["tempo"])
|
|
|
|
| 105 |
|
| 106 |
return emotions
|
| 107 |
|
| 108 |
+
# Part 2/3 - Core Functions
|
| 109 |
def generate_image_from_prompt(prompt):
|
| 110 |
"""이미지 생성 함수"""
|
| 111 |
print(f"Generating image with prompt: {prompt}")
|
|
|
|
| 165 |
|
| 166 |
return prompt
|
| 167 |
|
| 168 |
+
def update_final_prompt(state):
|
| 169 |
+
"""청신의 감상들을 종합하여 최종 프롬프트 업데이트"""
|
| 170 |
+
if not state["reflections"]:
|
| 171 |
+
return ""
|
| 172 |
+
|
| 173 |
+
combined_prompt = "한국 전통 민화 스타일의 추상화, 온천천에서의 다음 감상들을 담아내기:\n"
|
| 174 |
+
for time, text, sentiment in state["reflections"]:
|
| 175 |
+
combined_prompt += f"- {time}: {text} ({sentiment})\n"
|
| 176 |
+
|
| 177 |
+
return combined_prompt
|
| 178 |
+
|
| 179 |
+
def analyze_voice_with_retry(audio_path, state, max_retries=3):
|
| 180 |
+
"""음성 분석 함수 (재시도 로직 포함)"""
|
| 181 |
+
for attempt in range(max_retries):
|
| 182 |
+
try:
|
| 183 |
+
y, sr = librosa.load(audio_path, sr=16000)
|
| 184 |
+
|
| 185 |
+
acoustic_features = {
|
| 186 |
+
"energy": float(np.mean(librosa.feature.rms(y=y))),
|
| 187 |
+
"tempo": float(librosa.beat.tempo(y)[0]),
|
| 188 |
+
"pitch": float(np.mean(librosa.feature.zero_crossing_rate(y))),
|
| 189 |
+
"volume": float(np.mean(np.abs(y)))
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
transcription = speech_recognizer(y)
|
| 193 |
+
text = transcription["text"]
|
| 194 |
+
|
| 195 |
+
emotions = map_acoustic_to_emotion(acoustic_features, state.get("baseline_features"))
|
| 196 |
+
text_sentiment = text_analyzer(text)[0]
|
| 197 |
+
|
| 198 |
+
return {
|
| 199 |
+
"text": text,
|
| 200 |
+
"emotions": emotions,
|
| 201 |
+
"sentiment": text_sentiment
|
| 202 |
+
}
|
| 203 |
+
except Exception as e:
|
| 204 |
+
if attempt == max_retries - 1:
|
| 205 |
+
raise e
|
| 206 |
+
print(f"Attempt {attempt + 1} failed, retrying...")
|
| 207 |
+
continue
|
| 208 |
+
|
| 209 |
+
# Part 3/3 - Interface and Main
|
| 210 |
def create_interface():
|
| 211 |
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
| 212 |
state = gr.State({
|
| 213 |
"user_name": "",
|
| 214 |
+
"baseline_features": None,
|
| 215 |
"reflections": [],
|
| 216 |
"voice_analysis": None,
|
| 217 |
"final_prompt": ""
|
| 218 |
})
|
| 219 |
|
| 220 |
+
# 헤더 및 네비게이션
|
| 221 |
header = gr.Markdown("# 디지털 굿판")
|
| 222 |
user_display = gr.Markdown("")
|
| 223 |
|
|
|
|
| 225 |
# 입장
|
| 226 |
with gr.Tab("입장"):
|
| 227 |
gr.Markdown("### 디지털 굿판에 오신 것을 환영합니다")
|
| 228 |
+
name_input = gr.Textbox(label="이름을 알려주세요", placeholder="이름을 입력해주세요")
|
| 229 |
start_btn = gr.Button("여정 시작하기")
|
| 230 |
|
| 231 |
# 기준 설정
|
| 232 |
+
with gr.Tab("기준 설정", visible=False) as baseline_tab:
|
| 233 |
+
gr.Markdown("""### 축원의 문장을 평온한 마음으로 읽어주세요
|
| 234 |
+
'당신의 건강과 행복이 늘 가득하기를'""")
|
| 235 |
baseline_audio = gr.Audio(
|
| 236 |
label="축원 문장 녹음하기",
|
| 237 |
sources=["microphone"],
|
|
|
|
| 241 |
baseline_status = gr.Markdown("")
|
| 242 |
|
| 243 |
# 청신
|
| 244 |
+
with gr.Tab("청신", visible=False) as cleansing_tab:
|
| 245 |
+
gr.Markdown("""## 청신 - 소리로 정화하기
|
| 246 |
+
|
| 247 |
+
이 앱은 어디서나 실행 가능하지만, 온천천의 사운드스케이프를 기반으로 제작되었습니다.
|
| 248 |
+
온천장역에서 장전역까지의 구간을 걸으며 경험하면,
|
| 249 |
+
보다 자연과 하나 된 온천천의 신화를 느낄 수 있습니다.
|
| 250 |
+
""")
|
| 251 |
+
play_music_btn = gr.Button("온천천의 소리 듣기")
|
| 252 |
with gr.Row():
|
|
|
|
| 253 |
audio = gr.Audio(
|
| 254 |
+
value=None,
|
| 255 |
type="filepath",
|
| 256 |
label="온천천의 소리",
|
| 257 |
interactive=False,
|
| 258 |
+
autoplay=False
|
| 259 |
)
|
| 260 |
with gr.Column():
|
| 261 |
reflection_input = gr.Textbox(
|
|
|
|
| 267 |
headers=["시간", "감상", "감정 분석"],
|
| 268 |
label="기록된 감상들"
|
| 269 |
)
|
| 270 |
+
continue_to_prayer_btn = gr.Button("기원으로 이동하기")
|
| 271 |
|
| 272 |
# 기원
|
| 273 |
+
with gr.Tab("기원", visible=False) as prayer_tab:
|
|
|
|
| 274 |
with gr.Row():
|
| 275 |
with gr.Column():
|
| 276 |
voice_input = gr.Audio(
|
| 277 |
label="나누고 싶은 이야기를 들려주세요",
|
| 278 |
sources=["microphone"],
|
| 279 |
+
type="filepath"
|
|
|
|
| 280 |
)
|
| 281 |
clear_btn = gr.Button("녹음 지우기")
|
| 282 |
+
analyze_btn = gr.Button("분석하기")
|
| 283 |
|
| 284 |
with gr.Column():
|
| 285 |
+
transcribed_text = gr.Textbox(label="인식된 텍스트")
|
| 286 |
+
voice_emotion = gr.Textbox(label="음성 감정 분석")
|
| 287 |
+
text_emotion = gr.Textbox(label="텍스트 감정 분석")
|
| 288 |
+
final_prompt = gr.Textbox(label="생성된 프롬프트", lines=3)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
|
| 290 |
# 송신
|
| 291 |
+
with gr.Tab("송신", visible=False) as sending_tab:
|
| 292 |
gr.Markdown("## 송신 - 시각화 결과")
|
| 293 |
+
combined_prompt = gr.Textbox(
|
| 294 |
+
label="종합 프롬프트",
|
| 295 |
+
interactive=False,
|
| 296 |
+
lines=3
|
| 297 |
+
)
|
| 298 |
+
generate_btn = gr.Button("이미지 생성하기")
|
| 299 |
+
with gr.Row():
|
| 300 |
+
result_image = gr.Image(label="생성된 이미지", type="pil")
|
| 301 |
+
with gr.Column():
|
| 302 |
+
share_btn = gr.Button("이미지 공유하기")
|
| 303 |
+
download_btn = gr.Button("이미지 저장하기")
|
| 304 |
|
| 305 |
def start_journey(name):
|
| 306 |
+
if not name.strip():
|
| 307 |
+
return "이름을 입력해주세요", gr.update()
|
| 308 |
+
|
| 309 |
welcome_text = f"""
|
| 310 |
# 환영합니다, {name}님의 디지털 굿판
|
| 311 |
|
| 312 |
## 굿판의 세계관 🌌
|
| 313 |
+
디지털 굿판은 현대 도시 속에서 잊혀진 전통 굿의 정수를 담아낸 **디지털 의례의 공간**입니다.
|
| 314 |
+
이곳에서는 사람들의 목소리와 감정을 통해 **영적 교감**을 나누고, **자연과 도시의 에너지**를 연결하며,
|
| 315 |
+
평온함과 치유를 경험하게 됩니다.
|
| 316 |
+
|
| 317 |
+
## 위치 안내 📍
|
| 318 |
+
이 앱은 어디서나 실행 가능하지만, 온천천의 ���운드스케이프를 녹음하고 이를 기반으로 제작되어,
|
| 319 |
+
온천천 온천장역에서 장전역까지의 구간을 걸으며 경험하면
|
| 320 |
+
보다 자연과 하나 된 온천천의 신화를 느낄 수 있습니다.
|
| 321 |
+
|
| 322 |
+
## 여정을 시작하며 🚀
|
| 323 |
+
먼저, 평온한 마음으로 축원의 문장을 읽어주세요.
|
| 324 |
+
이는 당신의 감정을 더 정확하게 이해하기 위한 기준점이 될 것입니다.
|
| 325 |
+
"""
|
| 326 |
+
return welcome_text, gr.update(selected="기준 설정")
|
| 327 |
+
|
| 328 |
+
def play_music():
|
| 329 |
+
audio_path = os.path.abspath(os.path.join("assets", "main_music.mp3"))
|
| 330 |
+
return gr.update(value=audio_path, autoplay=True)
|
| 331 |
+
|
| 332 |
+
def save_reflection(text, state):
|
| 333 |
+
if not text.strip():
|
| 334 |
+
return state, state["reflections"]
|
| 335 |
+
|
| 336 |
+
try:
|
| 337 |
+
current_time = datetime.now().strftime("%H:%M:%S")
|
| 338 |
+
sentiment = text_analyzer(text)[0]
|
| 339 |
+
new_reflection = [current_time, text, f"{sentiment['label']} ({sentiment['score']:.2f})"]
|
| 340 |
+
|
| 341 |
+
state = state.copy()
|
| 342 |
+
if "reflections" not in state:
|
| 343 |
+
state["reflections"] = []
|
| 344 |
+
|
| 345 |
+
state["reflections"].append(new_reflection)
|
| 346 |
+
return state, state["reflections"]
|
| 347 |
+
except Exception as e:
|
| 348 |
+
print(f"Error in save_reflection: {str(e)}")
|
| 349 |
+
return state, []
|
| 350 |
+
|
| 351 |
+
def analyze_voice(audio_path, state):
|
| 352 |
+
if audio_path is None:
|
| 353 |
+
return state, "음성을 먼저 녹음해주세요.", "", "", ""
|
| 354 |
+
|
| 355 |
+
try:
|
| 356 |
+
result = analyze_voice_with_retry(audio_path, state)
|
| 357 |
+
|
| 358 |
+
voice_result = (
|
| 359 |
+
f"음성 감정: {result['emotions']['primary']} "
|
| 360 |
+
f"(강도: {result['emotions']['intensity']:.1f}%, "
|
| 361 |
+
f"신뢰도: {result['emotions']['confidence']:.2f})\n"
|
| 362 |
+
f"특징: {', '.join(result['emotions']['characteristics'])}"
|
| 363 |
+
)
|
| 364 |
+
|
| 365 |
+
text_result = f"텍스트 감정: {result['sentiment']['label']} "
|
| 366 |
+
f"(강도: {result['sentiment']['score']}/5)"
|
| 367 |
+
|
| 368 |
+
prompt = generate_detailed_prompt(
|
| 369 |
+
result['text'],
|
| 370 |
+
result['emotions'],
|
| 371 |
+
result['sentiment']
|
| 372 |
+
)
|
| 373 |
+
|
| 374 |
+
return state, result['text'], voice_result, text_result, prompt
|
| 375 |
+
except Exception as e:
|
| 376 |
+
return state, f"오류 발생: {str(e)}", "", "", ""
|
| 377 |
+
|
| 378 |
+
def update_sending_tab(state):
|
| 379 |
+
"""청신의 감상과 기원의 음성을 종합하여 송신 탭 업데이트"""
|
| 380 |
+
combined = "청신과 기원의 여정을 담은 이미지:\n\n"
|
| 381 |
+
combined += update_final_prompt(state)
|
| 382 |
+
return combined
|
| 383 |
+
|
| 384 |
+
# 이벤트 연결
|
| 385 |
+
start_btn.click(fn=start_journey, inputs=[name_input], outputs=[user_display, tabs])
|
| 386 |
+
play_music_btn.click(fn=play_music, outputs=[audio])
|
| 387 |
+
save_btn.click(fn=save_reflection, inputs=[reflection_input, state], outputs=[state, reflections_display])
|
| 388 |
+
analyze_btn.click(fn=analyze_voice, inputs=[voice_input, state], outputs=[state, transcribed_text, voice_emotion, text_emotion, final_prompt])
|
| 389 |
+
continue_to_prayer_btn.click(fn=lambda: gr.update(selected="기원"), outputs=[tabs])
|
| 390 |
+
tabs.change(fn=update_sending_tab, inputs=[state], outputs=[combined_prompt])
|
| 391 |
+
generate_btn.click(fn=generate_image_from_prompt, inputs=[combined_prompt], outputs=[result_image])
|
| 392 |
+
|
| 393 |
+
return app
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
|
| 395 |
if __name__ == "__main__":
|
| 396 |
+
demo = create_interface()
|
| 397 |
+
demo.launch(debug=True)
|