haepada commited on
Commit
7469d14
·
verified ·
1 Parent(s): 638a6b2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -185
app.py CHANGED
@@ -7,7 +7,7 @@ import os
7
  import requests
8
 
9
  # 환경변수에서 토큰 가져오기
10
- HF_API_TOKEN = os.getenv("roots") # 변경된 부분
11
  if not HF_API_TOKEN:
12
  raise ValueError("roots token not found in environment variables")
13
 
@@ -20,77 +20,80 @@ speech_recognizer = pipeline(
20
  "automatic-speech-recognition",
21
  model="kresnik/wav2vec2-large-xlsr-korean"
22
  )
23
- emotion_classifier = pipeline(
24
- "audio-classification",
25
- model="MIT/ast-finetuned-speech-commands-v2"
26
- )
27
  text_analyzer = pipeline(
28
  "sentiment-analysis",
29
  model="nlptown/bert-base-multilingual-uncased-sentiment"
30
  )
31
- korean_sentiment = pipeline(
32
- "text-classification",
33
- model="searle-j/korean_sentiment_analysis"
34
- )
35
 
36
- # 유틸리티 함수들
37
  def map_acoustic_to_emotion(features):
38
  """음향학적 특성을 감정으로 매핑"""
39
- intensity = features["energy"] * 100
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- if features["energy"] > 0.7:
42
- if features["tempo"] > 120:
43
- emotion = "기쁨/흥분"
 
 
44
  else:
45
- emotion = "분노/강조"
46
- elif features["pitch"] > 0.6:
47
- emotion = "놀람/관심"
48
- elif features["energy"] < 0.3:
49
- emotion = "슬픔/우울"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  else:
51
- emotion = "평온/중립"
52
-
53
- return {
54
- "emotion": emotion,
55
- "intensity": intensity,
56
- "features": features
57
- }
58
-
59
- def generate_detailed_prompt(text, voice_emotion, text_sentiment, acoustic_features):
60
- """프롬프트 생성"""
61
- emotion_colors = {
62
- "기쁨/흥분": "밝은 노랑과 주황색",
63
- "분노/강조": "강렬한 빨강과 검정",
64
- "놀람/관심": "선명한 파랑과 보라",
65
- "슬픔/우울": "어두운 파랑과 회색",
66
- "평온/중립": "부드러운 초록과 베이지"
67
- }
68
 
69
- visual_elements = {
70
- "high_energy": "역동적인 붓질과 강한 대비",
71
- "medium_energy": "균형잡힌 구도와 자연스러운 흐름",
72
- "low_energy": "부드러운 그라데이션과 차분한 "
 
 
73
  }
74
 
75
- energy_level = "medium_energy"
76
- if acoustic_features["energy"] > 0.7:
77
- energy_level = "high_energy"
78
- elif acoustic_features["energy"] < 0.3:
79
- energy_level = "low_energy"
80
-
81
- prompt = f"한국 전통 민화 스타일의 추상화, {emotion_colors.get(voice_emotion['emotion'], '자연스러운 색상')} 기반. "
82
- prompt += f"{visual_elements[energy_level]}를 통해 감정의 깊이를 표현. "
83
- prompt += f"음성의 {voice_emotion['emotion']} 감정과 텍스트의 {text_sentiment['label']} 감정이 조화를 이루며, "
84
- prompt += f"목소리의 특징(강도:{voice_emotion['intensity']:.1f})을 화면의 동적인 요소로 표현. "
85
- prompt += f"발화 내용 '{text}'의 의미를 은유적 이미지로 담아내기."
86
-
87
- return prompt
88
 
89
  def generate_image_from_prompt(prompt):
90
- """이미지 생성"""
91
  print(f"Generating image with prompt: {prompt}")
92
  try:
93
  if not prompt:
 
94
  return None
95
 
96
  response = requests.post(
@@ -107,18 +110,48 @@ def generate_image_from_prompt(prompt):
107
  )
108
 
109
  if response.status_code == 200:
 
110
  return response.content
111
  else:
112
  print(f"Error: {response.status_code}")
113
  print(f"Response: {response.text}")
114
  return None
 
115
  except Exception as e:
116
  print(f"Error generating image: {str(e)}")
117
  return None
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  def create_interface():
120
  with gr.Blocks(theme=gr.themes.Soft()) as app:
121
- # 상태 관리
122
  state = gr.State({
123
  "user_name": "",
124
  "reflections": [],
@@ -130,86 +163,8 @@ def create_interface():
130
  header = gr.Markdown("# 디지털 굿판")
131
  user_display = gr.Markdown("")
132
 
133
- with gr.Tabs() as tabs:
134
- # 입장
135
- with gr.Tab("입장"):
136
- gr.Markdown("""# 디지털 굿판에 오신 것을 환영합니다""")
137
- name_input = gr.Textbox(label="이름을 알려주세요")
138
- start_btn = gr.Button("여정 시작하기")
139
-
140
- # 청신
141
- with gr.Tab("청신"):
142
- with gr.Row():
143
- audio_path = os.path.abspath(os.path.join("assets", "main_music.mp3"))
144
- audio = gr.Audio(
145
- value=audio_path,
146
- type="filepath",
147
- label="온천천의 소리",
148
- interactive=False,
149
- autoplay=True
150
- )
151
- with gr.Column():
152
- reflection_input = gr.Textbox(
153
- label="현재 순간의 감상을 적어주세요",
154
- lines=3
155
- )
156
- save_btn = gr.Button("감상 저장하기")
157
- reflections_display = gr.Dataframe(
158
- headers=["시간", "감상", "감정 분석"],
159
- label="기록된 감상들"
160
- )
161
-
162
- # 기원
163
- with gr.Tab("기원"):
164
- gr.Markdown("## 기원 - 목소리로 전하기")
165
- with gr.Row():
166
- with gr.Column():
167
- voice_input = gr.Audio(
168
- label="나누고 싶은 이야기를 들려주세요",
169
- sources=["microphone"],
170
- type="filepath",
171
- interactive=True
172
- )
173
- clear_btn = gr.Button("녹음 지우기")
174
-
175
- with gr.Column():
176
- transcribed_text = gr.Textbox(
177
- label="인식된 텍스트",
178
- interactive=False
179
- )
180
- voice_emotion = gr.Textbox(
181
- label="음성 감정 분석",
182
- interactive=False
183
- )
184
- text_emotion = gr.Textbox(
185
- label="텍스트 감정 분석",
186
- interactive=False
187
- )
188
- analyze_btn = gr.Button("분석하기")
189
-
190
- # 송신
191
- with gr.Tab("송신"):
192
- gr.Markdown("## 송신 - 시각화 결과")
193
- with gr.Column():
194
- final_prompt = gr.Textbox(
195
- label="생성된 프롬프트",
196
- interactive=False,
197
- lines=3
198
- )
199
- generate_btn = gr.Button("이미지 생성하기")
200
- result_image = gr.Image(
201
- label="생성된 이미지",
202
- type="pil"
203
- )
204
-
205
- # 인터페이스 함수들
206
- def start_journey(name):
207
- """여정 시작"""
208
- return f"# 환영합니다, {name}님의 디지털 굿판", gr.update(selected="청신")
209
-
210
- def clear_voice_input():
211
- """음성 입력 초기화"""
212
- return None
213
 
214
  def analyze_voice(audio_path, state):
215
  """음성 분석"""
@@ -219,6 +174,7 @@ def create_interface():
219
  try:
220
  y, sr = librosa.load(audio_path, sr=16000)
221
 
 
222
  acoustic_features = {
223
  "energy": float(np.mean(librosa.feature.rms(y=y))),
224
  "tempo": float(librosa.beat.tempo(y)[0]),
@@ -226,65 +182,39 @@ def create_interface():
226
  "volume": float(np.mean(np.abs(y)))
227
  }
228
 
229
- voice_emotion = map_acoustic_to_emotion(acoustic_features)
 
 
 
230
  transcription = speech_recognizer(y)
231
  text = transcription["text"]
232
- text_sentiment = korean_sentiment(text)[0]
233
 
234
- voice_result = f"음성 감정: {voice_emotion['emotion']} (강도: {voice_emotion['intensity']:.2f})"
235
- text_result = f"텍스트 감정: {text_sentiment['label']} ({text_sentiment['score']:.2f})"
236
 
237
- prompt = generate_detailed_prompt(text, voice_emotion, text_sentiment, acoustic_features)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
  return state, text, voice_result, text_result, prompt
240
  except Exception as e:
241
  return state, f"오류 발생: {str(e)}", "", "", ""
242
 
243
- def save_reflection(text, state):
244
- """감상 저장"""
245
- if not text.strip():
246
- return state, state["reflections"]
247
-
248
- current_time = datetime.now().strftime("%H:%M:%S")
249
- sentiment = text_analyzer(text)[0]
250
- new_reflection = [current_time, text, f"{sentiment['label']} ({sentiment['score']:.2f})"]
251
-
252
- if "reflections" not in state:
253
- state["reflections"] = []
254
-
255
- state["reflections"].append(new_reflection)
256
- return state, state["reflections"]
257
-
258
- # 이벤트 연결
259
- start_btn.click(
260
- fn=lambda name: (f"# 환영합니다, {name}님의 디지털 굿판", gr.update(selected="청신")),
261
- inputs=[name_input],
262
- outputs=[user_display, tabs]
263
- )
264
-
265
- save_btn.click(
266
- fn=save_reflection,
267
- inputs=[reflection_input, state],
268
- outputs=[state, reflections_display]
269
- )
270
-
271
- clear_btn.click(
272
- fn=clear_voice_input,
273
- inputs=[],
274
- outputs=[voice_input]
275
- )
276
-
277
- analyze_btn.click(
278
- fn=analyze_voice,
279
- inputs=[voice_input, state],
280
- outputs=[state, transcribed_text, voice_emotion, text_emotion, final_prompt]
281
- )
282
-
283
- generate_btn.click(
284
- fn=generate_image_from_prompt,
285
- inputs=[final_prompt],
286
- outputs=[result_image]
287
- )
288
 
289
  return app
290
 
 
7
  import requests
8
 
9
  # 환경변수에서 토큰 가져오기
10
+ HF_API_TOKEN = os.getenv("roots")
11
  if not HF_API_TOKEN:
12
  raise ValueError("roots token not found in environment variables")
13
 
 
20
  "automatic-speech-recognition",
21
  model="kresnik/wav2vec2-large-xlsr-korean"
22
  )
 
 
 
 
23
  text_analyzer = pipeline(
24
  "sentiment-analysis",
25
  model="nlptown/bert-base-multilingual-uncased-sentiment"
26
  )
 
 
 
 
27
 
 
28
  def map_acoustic_to_emotion(features):
29
  """음향학적 특성을 감정으로 매핑"""
30
+ # 음성 특성 정규화
31
+ energy_norm = min(features["energy"] * 100, 100) # 에너지 레벨 (0-100)
32
+ tempo_norm = min(features["tempo"] / 200, 1) # 템포 정규화 (0-1)
33
+ pitch_norm = min(features["pitch"] * 2, 1) # 피치 정규화 (0-1)
34
+
35
+ # 상세 감정 분석
36
+ emotions = {
37
+ "primary": "",
38
+ "intensity": energy_norm,
39
+ "confidence": 0.0,
40
+ "secondary": "",
41
+ "characteristics": []
42
+ }
43
 
44
+ # 주요 감정 결정
45
+ if energy_norm > 70:
46
+ if tempo_norm > 0.6:
47
+ emotions["primary"] = "기쁨/열정"
48
+ emotions["characteristics"].append("빠르고 활기찬 말하기 패턴")
49
  else:
50
+ emotions["primary"] = "분노/강조"
51
+ emotions["characteristics"].append("강한 음성 강도")
52
+ emotions["confidence"] = energy_norm / 100
53
+
54
+ elif pitch_norm > 0.6:
55
+ if energy_norm > 50:
56
+ emotions["primary"] = "놀람/흥분"
57
+ emotions["characteristics"].append("높은 음고와 강한 강세")
58
+ else:
59
+ emotions["primary"] = "관심/호기심"
60
+ emotions["characteristics"].append("음고 변화가 큼")
61
+ emotions["confidence"] = pitch_norm
62
+
63
+ elif energy_norm < 30:
64
+ if tempo_norm < 0.4:
65
+ emotions["primary"] = "슬픔/우울"
66
+ emotions["characteristics"].append("느리고 약한 음성")
67
+ else:
68
+ emotions["primary"] = "피로/무기력"
69
+ emotions["characteristics"].append("낮은 에너지 레벨")
70
+ emotions["confidence"] = (30 - energy_norm) / 30
71
+
72
  else:
73
+ if tempo_norm > 0.5:
74
+ emotions["primary"] = "평온/안정"
75
+ emotions["characteristics"].append("균형잡힌 말하기 패턴")
76
+ else:
77
+ emotions["primary"] = "차분/진지"
78
+ emotions["characteristics"].append("안정적인 음성 특성")
79
+ emotions["confidence"] = 0.5
 
 
 
 
 
 
 
 
 
 
80
 
81
+ # 음성 특성 상세 분석
82
+ emotions["details"] = {
83
+ "energy_level": f"{energy_norm:.1f}%",
84
+ "speech_rate": f"{'빠름' if tempo_norm > 0.6 else '보통' if tempo_norm > 0.4 else '느림'}",
85
+ "pitch_variation": f"{'높음' if pitch_norm > 0.6 else '보통' if pitch_norm > 0.3 else '낮음'}",
86
+ "voice_volume": f"{'큼' if features['volume'] > 0.7 else '보통' if features['volume'] > 0.3 else '작음'}"
87
  }
88
 
89
+ return emotions
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
  def generate_image_from_prompt(prompt):
92
+ """이미지 생성 함수"""
93
  print(f"Generating image with prompt: {prompt}")
94
  try:
95
  if not prompt:
96
+ print("No prompt provided")
97
  return None
98
 
99
  response = requests.post(
 
110
  )
111
 
112
  if response.status_code == 200:
113
+ print("Image generated successfully")
114
  return response.content
115
  else:
116
  print(f"Error: {response.status_code}")
117
  print(f"Response: {response.text}")
118
  return None
119
+
120
  except Exception as e:
121
  print(f"Error generating image: {str(e)}")
122
  return None
123
 
124
+ def generate_detailed_prompt(text, emotions, text_sentiment):
125
+ """감정 기반 상세 프롬프트 생성"""
126
+ emotion_colors = {
127
+ "기쁨/열정": "밝은 노랑과 따뜻한 주황색",
128
+ "분노/강조": "강렬한 빨강과 짙은 검정",
129
+ "놀람/흥분": "선명한 파랑과 밝은 보라",
130
+ "관심/호기심": "연한 하늘색과 민트색",
131
+ "슬픔/우울": "어두운 파랑과 회색",
132
+ "피로/무기력": "탁한 갈색과 짙은 회색",
133
+ "평온/안정": "부드러운 초록과 베이지",
134
+ "차분/진지": "차분한 남색과 깊은 보라"
135
+ }
136
+
137
+ # 감정 강도에 따른 시각적 표현
138
+ if emotions["intensity"] > 70:
139
+ visual_style = "역동적인 붓질과 강한 대비"
140
+ elif emotions["intensity"] > 40:
141
+ visual_style = "균형잡힌 구도와 중간 톤의 조화"
142
+ else:
143
+ visual_style = "부드러운 그라데이션과 차분한 톤"
144
+
145
+ # 프롬프트 구성
146
+ prompt = f"한국 전통 민화 스타일의 추상화, {emotion_colors.get(emotions['primary'], '자연스러운 색상')} 기반. "
147
+ prompt += f"{visual_style}로 표현된 {emotions['primary']}의 감정. "
148
+ prompt += f"음성의 특징({', '.join(emotions['characteristics'])})을 화면의 동적 요소로 표현. "
149
+ prompt += f"발화 내용 '{text}'에서 느껴지는 감정(강도: {text_sentiment['score']}/5)을 은유적 이미지로 담아내기."
150
+
151
+ return prompt
152
+
153
  def create_interface():
154
  with gr.Blocks(theme=gr.themes.Soft()) as app:
 
155
  state = gr.State({
156
  "user_name": "",
157
  "reflections": [],
 
163
  header = gr.Markdown("# 디지털 굿판")
164
  user_display = gr.Markdown("")
165
 
166
+ # 나머지 인터페이스 코드는 동일하게 유지...
167
+ [이전 코드와 동일한 부분 생략]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
  def analyze_voice(audio_path, state):
170
  """음성 분석"""
 
174
  try:
175
  y, sr = librosa.load(audio_path, sr=16000)
176
 
177
+ # 음향학적 특성 분석
178
  acoustic_features = {
179
  "energy": float(np.mean(librosa.feature.rms(y=y))),
180
  "tempo": float(librosa.beat.tempo(y)[0]),
 
182
  "volume": float(np.mean(np.abs(y)))
183
  }
184
 
185
+ # 감정 분석
186
+ emotions = map_acoustic_to_emotion(acoustic_features)
187
+
188
+ # 음성 인식
189
  transcription = speech_recognizer(y)
190
  text = transcription["text"]
 
191
 
192
+ # 텍스트 감정 분석
193
+ text_sentiment = text_analyzer(text)[0]
194
 
195
+ # 결과 포맷팅
196
+ voice_result = (
197
+ f"음성 감정: {emotions['primary']} "
198
+ f"(강도: {emotions['intensity']:.1f}%, 신뢰도: {emotions['confidence']:.2f})\n"
199
+ f"특징: {', '.join(emotions['characteristics'])}\n"
200
+ f"상세 분석:\n"
201
+ f"- 에너지 레벨: {emotions['details']['energy_level']}\n"
202
+ f"- 말하기 속도: {emotions['details']['speech_rate']}\n"
203
+ f"- 음높이 변화: {emotions['details']['pitch_variation']}\n"
204
+ f"- 음성 크기: {emotions['details']['voice_volume']}"
205
+ )
206
+
207
+ text_result = f"텍스트 감정 분석 (1-5): {text_sentiment['score']}"
208
+
209
+ # 프롬프트 생성
210
+ prompt = generate_detailed_prompt(text, emotions, text_sentiment)
211
 
212
  return state, text, voice_result, text_result, prompt
213
  except Exception as e:
214
  return state, f"오류 발생: {str(e)}", "", "", ""
215
 
216
+ # 이벤트 연결도 동일하게 유지...
217
+ [이전 코드와 동일한 부분 생략]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
  return app
220