Spaces:
Configuration error
Configuration error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,61 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import numpy as np
|
| 3 |
-
import joblib, io
|
| 4 |
import librosa
|
| 5 |
from deepface import DeepFace
|
| 6 |
|
| 7 |
-
#
|
| 8 |
-
#
|
| 9 |
-
|
|
|
|
| 10 |
|
|
|
|
| 11 |
def analyze_face(frame: np.ndarray):
|
| 12 |
# DeepFace 回傳 dict,裡面有 'dominant_emotion'
|
| 13 |
res = DeepFace.analyze(frame, actions=["emotion"], enforce_detection=False)
|
| 14 |
-
|
| 15 |
-
return frame, emo
|
| 16 |
|
| 17 |
def analyze_audio(wav_file):
|
| 18 |
wav_bytes = wav_file.read()
|
|
|
|
| 19 |
y, sr = librosa.load(io.BytesIO(wav_bytes), sr=None)
|
| 20 |
mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
|
| 21 |
mf = np.mean(mfccs.T, axis=0)
|
| 22 |
return audio_model.predict([mf])[0]
|
| 23 |
|
| 24 |
def analyze_text(txt):
|
|
|
|
| 25 |
mapping = {
|
| 26 |
-
"😊happy":["開心","快樂","愉快","喜悅","歡喜","興奮","
|
| 27 |
-
"😠angry":
|
| 28 |
-
"😢sad":
|
| 29 |
-
"😲surprise":
|
| 30 |
-
"😨fear":
|
| 31 |
}
|
| 32 |
for emo, kws in mapping.items():
|
| 33 |
if any(w in txt for w in kws):
|
| 34 |
return emo
|
| 35 |
-
return "neutral"
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
|
|
|
| 39 |
with gr.Tabs():
|
| 40 |
with gr.TabItem("📷 Live Face"):
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
-
with gr.TabItem("🎤
|
| 50 |
-
wav = gr.File(label="選擇 .wav
|
| 51 |
-
wav_btn = gr.Button("
|
| 52 |
-
wav_out = gr.
|
| 53 |
wav_btn.click(fn=analyze_audio, inputs=wav, outputs=wav_out)
|
| 54 |
|
| 55 |
with gr.TabItem("⌨️ 輸入文字"):
|
| 56 |
txt = gr.Textbox(label="在此輸入文字", lines=3)
|
| 57 |
-
txt_btn = gr.Button("
|
| 58 |
-
txt_out = gr.
|
| 59 |
txt_btn.click(fn=analyze_text, inputs=txt, outputs=txt_out)
|
| 60 |
|
|
|
|
|
|
|
|
|
|
| 61 |
demo.launch()
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
+
import os
|
| 3 |
+
# ─── 解決 DeepFace 無法寫入預設路徑的問題 ─────────────────────────────────────────
|
| 4 |
+
# 將 DeepFace 的快取目錄指向可寫入的 /tmp 之下
|
| 5 |
+
os.environ["DEEPFACE_HOME"] = "/tmp/.deepface"
|
| 6 |
+
|
| 7 |
import gradio as gr
|
| 8 |
import numpy as np
|
| 9 |
+
import joblib, io
|
| 10 |
import librosa
|
| 11 |
from deepface import DeepFace
|
| 12 |
|
| 13 |
+
# ─── 1. 載入模型 ─────────────────────────────────────────────────────────────
|
| 14 |
+
# 我們把模型檔放在跟 app.py 同一層的 voice_model.joblib
|
| 15 |
+
MODEL_PATH = os.path.join(os.path.dirname(__file__), "voice_model.joblib")
|
| 16 |
+
audio_model = joblib.load(MODEL_PATH)
|
| 17 |
|
| 18 |
+
# ─── 2. 定義各種分析函數 ────────────────────────────────────────────────────
|
| 19 |
def analyze_face(frame: np.ndarray):
|
| 20 |
# DeepFace 回傳 dict,裡面有 'dominant_emotion'
|
| 21 |
res = DeepFace.analyze(frame, actions=["emotion"], enforce_detection=False)
|
| 22 |
+
return frame, res["dominant_emotion"]
|
|
|
|
| 23 |
|
| 24 |
def analyze_audio(wav_file):
|
| 25 |
wav_bytes = wav_file.read()
|
| 26 |
+
# 用 librosa 讀入
|
| 27 |
y, sr = librosa.load(io.BytesIO(wav_bytes), sr=None)
|
| 28 |
mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
|
| 29 |
mf = np.mean(mfccs.T, axis=0)
|
| 30 |
return audio_model.predict([mf])[0]
|
| 31 |
|
| 32 |
def analyze_text(txt):
|
| 33 |
+
# 簡單關鍵字 mapping
|
| 34 |
mapping = {
|
| 35 |
+
"😊 happy": ["開心","快樂","愉快","喜悅","歡喜","興奮","高興","歡"],
|
| 36 |
+
"😠 angry": ["生氣","憤怒","不爽","發火","火大","氣憤"],
|
| 37 |
+
"😢 sad": ["傷心","難過","哭","憂","悲","心酸","哀","痛苦","慘","愁"],
|
| 38 |
+
"😲 surprise":["驚訝","意外","嚇","好奇","驚詫","詫異","訝異"],
|
| 39 |
+
"😨 fear": ["怕","恐懼","緊張","懼","膽怯","畏"],
|
| 40 |
}
|
| 41 |
for emo, kws in mapping.items():
|
| 42 |
if any(w in txt for w in kws):
|
| 43 |
return emo
|
| 44 |
+
return "😐 neutral"
|
| 45 |
|
| 46 |
+
# ─── 3. 建立 Gradio 介面 ────────────────────────────────────────────────────
|
| 47 |
+
with gr.Blocks(title="多模態即時情緒分析") as demo:
|
| 48 |
+
gr.Markdown("## 🤖 多模態即時情緒分析")
|
| 49 |
with gr.Tabs():
|
| 50 |
with gr.TabItem("📷 Live Face"):
|
| 51 |
+
# 注意要用 gr.components.Camera 或 gr.components…
|
| 52 |
+
camera = gr.components.Camera(label="請對準鏡頭 (Live)")
|
| 53 |
+
out_img = gr.Image(label="擷取畫面")
|
| 54 |
+
out_lbl = gr.Label(label="檢測到的情緒")
|
| 55 |
+
camera.change(
|
| 56 |
+
fn=analyze_face,
|
| 57 |
+
inputs=camera,
|
| 58 |
+
outputs=[out_img, out_lbl],
|
| 59 |
+
live=True
|
| 60 |
+
)
|
| 61 |
|
| 62 |
+
with gr.TabItem("🎤 上傳語音檔"):
|
| 63 |
+
wav = gr.File(label="選擇 .wav 檔案", file_types=[".wav"])
|
| 64 |
+
wav_btn = gr.Button("開始分析")
|
| 65 |
+
wav_out = gr.Textbox(label="語音偵測到的情緒")
|
| 66 |
wav_btn.click(fn=analyze_audio, inputs=wav, outputs=wav_out)
|
| 67 |
|
| 68 |
with gr.TabItem("⌨️ 輸入文字"):
|
| 69 |
txt = gr.Textbox(label="在此輸入文字", lines=3)
|
| 70 |
+
txt_btn = gr.Button("開始分析")
|
| 71 |
+
txt_out = gr.Textbox(label="文字偵測到的情緒")
|
| 72 |
txt_btn.click(fn=analyze_text, inputs=txt, outputs=txt_out)
|
| 73 |
|
| 74 |
+
# 啟動
|
| 75 |
+
if __name__ == "__main__":
|
| 76 |
+
# Hugging Face Spaces 上不需要傳 host/port,直接 .launch() 即可
|
| 77 |
demo.launch()
|