haepada commited on
Commit
158bfd0
·
verified ·
1 Parent(s): 6671fa4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -65
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # Part 1/4 - Imports and Initial Setup
2
  import gradio as gr
3
  import numpy as np
4
  import librosa
@@ -81,13 +81,20 @@ WORLDVIEW_MESSAGE = """
81
  온천천 온천장역에서 장전역까지 걸으며 더 깊은 체험이 가능합니다.
82
  """
83
 
84
- def calculate_baseline_features(audio_path):
85
  """기준점 음성 특성 분석"""
86
  try:
87
- y, sr = librosa.load(audio_path, sr=16000)
 
 
 
 
 
 
 
88
  features = {
89
  "energy": float(np.mean(librosa.feature.rms(y=y))),
90
- "tempo": float(librosa.beat.tempo(y)[0]),
91
  "pitch": float(np.mean(librosa.feature.zero_crossing_rate(y))),
92
  "volume": float(np.mean(np.abs(y))),
93
  "mfcc": librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13).mean(axis=1).tolist()
@@ -158,32 +165,38 @@ def map_acoustic_to_emotion(features, baseline_features=None):
158
 
159
  return emotions
160
 
161
- def analyze_voice(audio_path, state):
162
  """통합 음성 분석"""
163
- if audio_path is None:
164
  return state, "음성을 먼저 녹음해주세요.", "", "", ""
165
-
166
  try:
167
- y, sr = librosa.load(audio_path, sr=16000)
168
-
 
 
 
 
 
 
169
  # 음향학적 특성 분석
170
  acoustic_features = {
171
  "energy": float(np.mean(librosa.feature.rms(y=y))),
172
- "tempo": float(librosa.beat.tempo(y)[0]),
173
  "pitch": float(np.mean(librosa.feature.zero_crossing_rate(y))),
174
  "volume": float(np.mean(np.abs(y)))
175
  }
176
 
177
  # 음성 감정 분석
178
  voice_emotion = map_acoustic_to_emotion(acoustic_features, state.get("baseline_features"))
179
-
180
  # 음성 인식
181
- transcription = speech_recognizer(y)
182
  text = transcription["text"]
183
-
184
  # 텍스트 감정 분석
185
  text_sentiment = text_analyzer(text)[0]
186
-
187
  # 결과 포맷팅
188
  voice_result = (
189
  f"음성 감정: {voice_emotion['primary']} "
@@ -195,12 +208,15 @@ def analyze_voice(audio_path, state):
195
  f"- 음높이 변화: {voice_emotion['details']['pitch_variation']}\n"
196
  f"- 음성 크기: {voice_emotion['details']['voice_volume']}"
197
  )
198
-
199
- text_result = f"텍스트 감정 분석 (1-5): {text_sentiment['score']}"
200
-
201
  # 프롬프트 생성
202
  prompt = generate_detailed_prompt(text, voice_emotion, text_sentiment)
203
-
 
 
 
204
  return state, text, voice_result, text_result, prompt
205
  except Exception as e:
206
  return state, f"오류 발생: {str(e)}", "", "", ""
@@ -228,8 +244,8 @@ def generate_detailed_prompt(text, emotions, text_sentiment):
228
  prompt = f"한국 전통 민화 스타일의 추상화, {emotion_colors.get(emotions['primary'], '자연스러운 색상')} 기반. "
229
  prompt += f"{visual_style}로 표현된 {emotions['primary']}의 감정. "
230
  prompt += f"음성의 특징({', '.join(emotions['characteristics'])})을 화면의 동적 요소로 표현. "
231
- prompt += f"발화 내용 '{text}'에서 느껴지는 감정(강도: {text_sentiment['score']}/5)을 은유적 이미지로 담아내기."
232
-
233
  return prompt
234
 
235
  def generate_image_from_prompt(prompt):
@@ -238,8 +254,8 @@ def generate_image_from_prompt(prompt):
238
  try:
239
  if not prompt:
240
  print("No prompt provided")
241
- return None
242
-
243
  response = requests.post(
244
  API_URL,
245
  headers=headers,
@@ -252,7 +268,7 @@ def generate_image_from_prompt(prompt):
252
  }
253
  }
254
  )
255
-
256
  if response.status_code == 200:
257
  print("Image generated successfully")
258
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -269,33 +285,27 @@ def generate_image_from_prompt(prompt):
269
  print(f"Error generating image: {str(e)}")
270
  return None, None
271
 
272
- def handle_image_timeout(result_image):
273
- """이미지 자동 사라짐을 처리하는 함수"""
274
- time.sleep(IMAGE_DISPLAY_TIME)
275
- result_image.update(value=None)
276
-
277
  def save_reflection(text, state):
278
  """감상 저장"""
279
  if not text.strip():
280
- return state, state["reflections"]
281
-
282
  try:
283
  current_time = datetime.now().strftime("%H:%M:%S")
284
  sentiment = text_analyzer(text)[0]
285
  new_reflection = [current_time, text, f"{sentiment['label']} ({sentiment['score']:.2f})"]
286
-
287
- if "reflections" not in state:
288
- state["reflections"] = []
289
-
290
- state["reflections"].append(new_reflection)
291
- return state, state["reflections"]
292
  except Exception as e:
293
  print(f"Error in save_reflection: {str(e)}")
294
  return state, []
295
 
296
  def create_interface():
297
  db = SimpleDB()
298
-
299
  with gr.Blocks(theme=gr.themes.Soft()) as app:
300
  state = gr.State({
301
  "user_name": "",
@@ -324,7 +334,8 @@ def create_interface():
324
  gr.Markdown("'당신의 건강과 행복이 늘 가득하기를'")
325
  baseline_audio = gr.Audio(
326
  label="축원 문장 녹음하기",
327
- sources=["microphone"]
 
328
  )
329
  set_baseline_btn = gr.Button("기준점 설정 완료")
330
  baseline_status = gr.Markdown("")
@@ -334,7 +345,7 @@ def create_interface():
334
  play_music_btn = gr.Button("온천천의 소리 듣기")
335
  with gr.Row():
336
  audio = gr.Audio(
337
- value=None,
338
  type="filepath",
339
  label="온천천의 소리",
340
  interactive=False,
@@ -348,7 +359,10 @@ def create_interface():
348
  save_btn = gr.Button("감상 저장하기")
349
  reflections_display = gr.Dataframe(
350
  headers=["시간", "감상", "감정 분석"],
351
- label="기록된 감상들"
 
 
 
352
  )
353
 
354
  with gr.Tab("기원"):
@@ -357,11 +371,12 @@ def create_interface():
357
  with gr.Column():
358
  voice_input = gr.Audio(
359
  label="소원을 나누고 싶은 마음을 말해주세요",
360
- sources=["microphone"]
 
361
  )
362
  clear_btn = gr.Button("녹음 지우기")
363
  analyze_btn = gr.Button("소원 분석하기")
364
-
365
  with gr.Column():
366
  transcribed_text = gr.Textbox(label="인식된 텍스트")
367
  voice_emotion = gr.Textbox(label="음성 감정 분석")
@@ -376,40 +391,28 @@ def create_interface():
376
  )
377
  generate_btn = gr.Button("마음의 그림 그리기")
378
  result_image = gr.Image(label="생성된 이미지")
379
-
380
  gr.Markdown("## 마지막 감상을 남겨주세요")
381
  final_reflection = gr.Textbox(
382
  label="마지막 감상",
383
  placeholder="한 줄로 남겨주세요..."
384
  )
385
  save_final_btn = gr.Button("감상 남기기")
386
-
387
- def delayed_image_clear():
388
- time.sleep(IMAGE_DISPLAY_TIME)
389
- return gr.update(value=None)
390
-
391
- def on_image_generated(prompt):
392
- image_content = generate_image_from_prompt(prompt)
393
- if image_content:
394
- # Start a timer to clear the image
395
- timer = threading.Timer(IMAGE_DISPLAY_TIME, lambda: result_image.update(value=None))
396
- timer.start()
397
- return image_content
398
- return None
399
 
400
  # 이벤트 연결
401
  start_btn.click(
402
- fn=lambda name: (
403
  WORLDVIEW_MESSAGE if name.strip() else "이름을 입력해주세요",
404
  gr.update(visible=True) if name.strip() else gr.update(),
405
- {"user_name": name} if name.strip() else state
406
  ),
407
- inputs=[name_input],
408
  outputs=[worldview_display, tabs, state]
409
  )
410
 
411
  set_baseline_btn.click(
412
- fn=lambda x, s: ({"baseline_features": calculate_baseline_features(x)}, "기준점이 설정되었습니다."),
413
  inputs=[baseline_audio, state],
414
  outputs=[state, baseline_status]
415
  )
@@ -432,19 +435,29 @@ def create_interface():
432
  )
433
 
434
  generate_btn.click(
435
- fn=on_image_generated,
436
  inputs=[final_prompt],
437
  outputs=[result_image]
438
  )
439
 
440
  save_final_btn.click(
441
- fn=lambda t, s: (db.save_wish(s["user_name"], t), "감상이 저장되었습니다."),
 
 
 
442
  inputs=[final_reflection, state],
443
- outputs=[gr.Markdown("")]
444
  )
445
 
446
- return app
 
 
 
 
 
447
 
 
448
  if __name__ == "__main__":
449
  demo = create_interface()
450
- demo.launch(debug=True)
 
 
1
+ # 라이브러리 임포트 초기 설정
2
  import gradio as gr
3
  import numpy as np
4
  import librosa
 
81
  온천천 온천장역에서 장전역까지 걸으며 더 깊은 체험이 가능합니다.
82
  """
83
 
84
+ def calculate_baseline_features(audio_data):
85
  """기준점 음성 특성 분석"""
86
  try:
87
+ if isinstance(audio_data, tuple):
88
+ sr, y = audio_data
89
+ elif isinstance(audio_data, str):
90
+ y, sr = librosa.load(audio_data, sr=16000)
91
+ else:
92
+ print("Unsupported audio format")
93
+ return None
94
+
95
  features = {
96
  "energy": float(np.mean(librosa.feature.rms(y=y))),
97
+ "tempo": float(librosa.beat.tempo(y, sr=sr)[0]),
98
  "pitch": float(np.mean(librosa.feature.zero_crossing_rate(y))),
99
  "volume": float(np.mean(np.abs(y))),
100
  "mfcc": librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13).mean(axis=1).tolist()
 
165
 
166
  return emotions
167
 
168
+ def analyze_voice(audio_data, state):
169
  """통합 음성 분석"""
170
+ if audio_data is None:
171
  return state, "음성을 먼저 녹음해주세요.", "", "", ""
172
+
173
  try:
174
+ if isinstance(audio_data, tuple):
175
+ sr, y = audio_data
176
+ elif isinstance(audio_data, str):
177
+ y, sr = librosa.load(audio_data, sr=16000)
178
+ else:
179
+ print("Unsupported audio format")
180
+ return state, "오디오 형식을 지원하지 않습니다.", "", "", ""
181
+
182
  # 음향학적 특성 분석
183
  acoustic_features = {
184
  "energy": float(np.mean(librosa.feature.rms(y=y))),
185
+ "tempo": float(librosa.beat.tempo(y, sr=sr)[0]),
186
  "pitch": float(np.mean(librosa.feature.zero_crossing_rate(y))),
187
  "volume": float(np.mean(np.abs(y)))
188
  }
189
 
190
  # 음성 감정 분석
191
  voice_emotion = map_acoustic_to_emotion(acoustic_features, state.get("baseline_features"))
192
+
193
  # 음성 인식
194
+ transcription = speech_recognizer(y, sampling_rate=sr)
195
  text = transcription["text"]
196
+
197
  # 텍스트 감정 분석
198
  text_sentiment = text_analyzer(text)[0]
199
+
200
  # 결과 포맷팅
201
  voice_result = (
202
  f"음성 감정: {voice_emotion['primary']} "
 
208
  f"- 음높이 변화: {voice_emotion['details']['pitch_variation']}\n"
209
  f"- 음성 크기: {voice_emotion['details']['voice_volume']}"
210
  )
211
+
212
+ text_result = f"텍스트 감정 분석 (1-5): {text_sentiment['label']} (점수: {text_sentiment['score']:.2f})"
213
+
214
  # 프롬프트 생성
215
  prompt = generate_detailed_prompt(text, voice_emotion, text_sentiment)
216
+
217
+ # 상태 업데이트
218
+ state = {**state, "final_prompt": prompt}
219
+
220
  return state, text, voice_result, text_result, prompt
221
  except Exception as e:
222
  return state, f"오류 발생: {str(e)}", "", "", ""
 
244
  prompt = f"한국 전통 민화 스타일의 추상화, {emotion_colors.get(emotions['primary'], '자연스러운 색상')} 기반. "
245
  prompt += f"{visual_style}로 표현된 {emotions['primary']}의 감정. "
246
  prompt += f"음성의 특징({', '.join(emotions['characteristics'])})을 화면의 동적 요소로 표현. "
247
+ prompt += f"발화 내용 '{text}'에서 느껴지는 감정({text_sentiment['label']} - 점수: {text_sentiment['score']:.2f})을 은유적 이미지로 담아내기."
248
+
249
  return prompt
250
 
251
  def generate_image_from_prompt(prompt):
 
254
  try:
255
  if not prompt:
256
  print("No prompt provided")
257
+ return None, None
258
+
259
  response = requests.post(
260
  API_URL,
261
  headers=headers,
 
268
  }
269
  }
270
  )
271
+
272
  if response.status_code == 200:
273
  print("Image generated successfully")
274
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
 
285
  print(f"Error generating image: {str(e)}")
286
  return None, None
287
 
 
 
 
 
 
288
  def save_reflection(text, state):
289
  """감상 저장"""
290
  if not text.strip():
291
+ return state, state.get("reflections", [])
292
+
293
  try:
294
  current_time = datetime.now().strftime("%H:%M:%S")
295
  sentiment = text_analyzer(text)[0]
296
  new_reflection = [current_time, text, f"{sentiment['label']} ({sentiment['score']:.2f})"]
297
+
298
+ reflections = state.get("reflections", [])
299
+ reflections.append(new_reflection)
300
+ state = {**state, "reflections": reflections}
301
+ return state, reflections
 
302
  except Exception as e:
303
  print(f"Error in save_reflection: {str(e)}")
304
  return state, []
305
 
306
  def create_interface():
307
  db = SimpleDB()
308
+
309
  with gr.Blocks(theme=gr.themes.Soft()) as app:
310
  state = gr.State({
311
  "user_name": "",
 
334
  gr.Markdown("'당신의 건강과 행복이 늘 가득하기를'")
335
  baseline_audio = gr.Audio(
336
  label="축원 문장 녹음하기",
337
+ source="microphone",
338
+ type="numpy"
339
  )
340
  set_baseline_btn = gr.Button("기준점 설정 완료")
341
  baseline_status = gr.Markdown("")
 
345
  play_music_btn = gr.Button("온천천의 소리 듣기")
346
  with gr.Row():
347
  audio = gr.Audio(
348
+ value="oncheoncheon_sound.wav",
349
  type="filepath",
350
  label="온천천의 소리",
351
  interactive=False,
 
359
  save_btn = gr.Button("감상 저장하기")
360
  reflections_display = gr.Dataframe(
361
  headers=["시간", "감상", "감정 분석"],
362
+ label="기록된 감상들",
363
+ datatype=["str", "str", "str"],
364
+ row_count=(0, "dynamic"),
365
+ col_count=(3, "fixed")
366
  )
367
 
368
  with gr.Tab("기원"):
 
371
  with gr.Column():
372
  voice_input = gr.Audio(
373
  label="소원을 나누고 싶은 마음을 말해주세요",
374
+ source="microphone",
375
+ type="numpy"
376
  )
377
  clear_btn = gr.Button("녹음 지우기")
378
  analyze_btn = gr.Button("소원 분석하기")
379
+
380
  with gr.Column():
381
  transcribed_text = gr.Textbox(label="인식된 텍스트")
382
  voice_emotion = gr.Textbox(label="음성 감정 분석")
 
391
  )
392
  generate_btn = gr.Button("마음의 그림 그리기")
393
  result_image = gr.Image(label="생성된 이미지")
394
+
395
  gr.Markdown("## 마지막 감상을 남겨주세요")
396
  final_reflection = gr.Textbox(
397
  label="마지막 감상",
398
  placeholder="한 줄로 남겨주세요..."
399
  )
400
  save_final_btn = gr.Button("감상 남기기")
401
+ save_final_status = gr.Markdown("")
 
 
 
 
 
 
 
 
 
 
 
 
402
 
403
  # 이벤트 연결
404
  start_btn.click(
405
+ fn=lambda name, state: (
406
  WORLDVIEW_MESSAGE if name.strip() else "이름을 입력해주세요",
407
  gr.update(visible=True) if name.strip() else gr.update(),
408
+ {**state, "user_name": name} if name.strip() else state
409
  ),
410
+ inputs=[name_input, state],
411
  outputs=[worldview_display, tabs, state]
412
  )
413
 
414
  set_baseline_btn.click(
415
+ fn=lambda x, s: ({**s, "baseline_features": calculate_baseline_features(x)}, "기준점이 설정되었습니다."),
416
  inputs=[baseline_audio, state],
417
  outputs=[state, baseline_status]
418
  )
 
435
  )
436
 
437
  generate_btn.click(
438
+ fn=lambda prompt: generate_image_from_prompt(prompt)[0],
439
  inputs=[final_prompt],
440
  outputs=[result_image]
441
  )
442
 
443
  save_final_btn.click(
444
+ fn=lambda t, s: (
445
+ db.save_wish(s.get("user_name", "익명"), t),
446
+ "감상이 저장되었습니다."
447
+ ),
448
  inputs=[final_reflection, state],
449
+ outputs=[save_final_status]
450
  )
451
 
452
+ play_music_btn.click(
453
+ fn=lambda: "oncheoncheon_sound.wav",
454
+ outputs=[audio]
455
+ )
456
+
457
+ return app
458
 
459
+ # 파일 맨 아래 부분
460
  if __name__ == "__main__":
461
  demo = create_interface()
462
+ demo.launch(debug=True, share=True)
463
+