Spaces:
Configuration error
Configuration error
Update app.py
Browse files
app.py
CHANGED
@@ -1,40 +1,89 @@
|
|
1 |
# app.py
|
2 |
-
import os
|
3 |
-
# —— 把 DeepFace 缓存目录指向可写的 /tmp/.deepface
|
4 |
-
os.environ["DEEPFACE_HOME"] = "/tmp/.deepface"
|
5 |
-
os.makedirs(os.environ["DEEPFACE_HOME"], exist_ok=True)
|
6 |
-
|
7 |
import gradio as gr
|
8 |
-
import cv2
|
9 |
import numpy as np
|
|
|
|
|
10 |
from deepface import DeepFace
|
11 |
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
13 |
"""
|
14 |
-
|
15 |
-
|
16 |
"""
|
17 |
-
# RGB
|
18 |
-
bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
|
19 |
res = DeepFace.analyze(
|
20 |
-
|
21 |
actions=['emotion'],
|
22 |
enforce_detection=False
|
23 |
)
|
24 |
-
#
|
25 |
if isinstance(res, list):
|
26 |
emo = res[0].get('dominant_emotion', 'unknown')
|
27 |
else:
|
28 |
emo = res.get('dominant_emotion', 'unknown')
|
29 |
return emo
|
30 |
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# app.py
|
|
|
|
|
|
|
|
|
|
|
2 |
import gradio as gr
|
|
|
3 |
import numpy as np
|
4 |
+
import joblib, io
|
5 |
+
import librosa
|
6 |
from deepface import DeepFace
|
7 |
|
8 |
+
# —— 1. 预加载模型 ——
|
9 |
+
# DeepFace 热身 + 载入语音模型
|
10 |
+
audio_model = joblib.load("src/voice_model.joblib")
|
11 |
+
# 你也可以包一层 try/except
|
12 |
+
|
13 |
+
def analyze_face(frame: np.ndarray):
|
14 |
"""
|
15 |
+
输入:摄像头采到的 RGB numpy 数组
|
16 |
+
输出:DeepFace 分析出的 dominant_emotion(字符串)
|
17 |
"""
|
18 |
+
# DeepFace.analyze 接受 RGB np.array
|
|
|
19 |
res = DeepFace.analyze(
|
20 |
+
img_path=frame,
|
21 |
actions=['emotion'],
|
22 |
enforce_detection=False
|
23 |
)
|
24 |
+
# 兼容 dict / list 返回
|
25 |
if isinstance(res, list):
|
26 |
emo = res[0].get('dominant_emotion', 'unknown')
|
27 |
else:
|
28 |
emo = res.get('dominant_emotion', 'unknown')
|
29 |
return emo
|
30 |
|
31 |
+
def analyze_audio(wav_file):
|
32 |
+
"""
|
33 |
+
输入:上传的 wav(二进制)
|
34 |
+
输出:语音情绪分类标签
|
35 |
+
"""
|
36 |
+
data = wav_file.read()
|
37 |
+
y, sr = librosa.load(io.BytesIO(data), sr=None)
|
38 |
+
mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
|
39 |
+
mf = np.mean(mfccs.T, axis=0)
|
40 |
+
return audio_model.predict([mf])[0]
|
41 |
+
|
42 |
+
def analyze_text(txt):
|
43 |
+
"""
|
44 |
+
简单的基于关键词的中文情绪分析
|
45 |
+
"""
|
46 |
+
if any(w in text for w in ["開心","快樂","愉快","喜悅","歡喜","興奮","歡","高興"]): return "happy"
|
47 |
+
if any(w in text for w in ["生氣","憤怒","不爽","發火","火大","氣憤"]): return "angry"
|
48 |
+
if any(w in text for w in ["傷心","難過","哭","難受","心酸","憂","悲","哀","痛苦","慘","愁"]): return "sad"
|
49 |
+
if any(w in text for w in ["驚訝","意外","嚇","驚詫","詫異","訝異","好奇"]): return "surprise"
|
50 |
+
if any(w in text for w in ["怕","恐懼","緊張","懼","膽怯","畏"]): return "fear"
|
51 |
+
return "neutral"
|
52 |
+
|
53 |
+
with gr.Blocks(title="多模態即時情緒分析") as demo:
|
54 |
+
gr.Markdown("## 📱 多模態即時情緒分析")
|
55 |
+
|
56 |
+
tabs = gr.Tabs()
|
57 |
+
with tabs:
|
58 |
+
with gr.TabItem("🔴 Face(即時)"):
|
59 |
+
gr.Markdown("⚠️ Spaces 無法直接打開攝像頭,請本機 `python app.py` 測試;手機/電腦瀏覽器可用以下方式:")
|
60 |
+
camera = gr.Image(
|
61 |
+
source="webcam",
|
62 |
+
type="numpy",
|
63 |
+
label="請對準鏡頭"
|
64 |
+
)
|
65 |
+
face_out = gr.Textbox(label="偵測結果")
|
66 |
+
camera.change(fn=analyze_face, inputs=camera, outputs=face_out)
|
67 |
|
68 |
+
with gr.TabItem("🎤 上傳語音"):
|
69 |
+
wav = gr.File(
|
70 |
+
label="請選擇 .wav 音檔",
|
71 |
+
file_types=["wav"]
|
72 |
+
)
|
73 |
+
audio_out = gr.Textbox(label="偵測結果")
|
74 |
+
wav.upload(fn=analyze_audio, inputs=wav, outputs=audio_out)
|
75 |
+
|
76 |
+
with gr.TabItem("⌨️ 文本輸入"):
|
77 |
+
txt = gr.Textbox(
|
78 |
+
label="在此輸入文字",
|
79 |
+
placeholder="輸入想要分析的句子…"
|
80 |
+
)
|
81 |
+
text_btn = gr.Button("開始分析")
|
82 |
+
text_out = gr.Textbox(label="偵測結果")
|
83 |
+
text_btn.click(fn=analyze_text, inputs=txt, outputs=text_out)
|
84 |
+
|
85 |
+
demo.launch(
|
86 |
+
server_name="0.0.0.0",
|
87 |
+
server_port=7860,
|
88 |
+
share=False
|
89 |
+
)
|