# ───────────────────────────────────────────────────────────────────────────── # src/streamlit_app.py # ───────────────────────────────────────────────────────────────────────────── import os, joblib MODEL_PATH = os.path.join(os.getcwd(), "src", "voice_model.joblib") if not os.path.exists(MODEL_PATH): # 第一次启动时训练并保存 clf = train_voice_model() os.makedirs(os.path.dirname(MODEL_PATH), exist_ok=True) joblib.dump(clf, MODEL_PATH) # 然后再加载 voice_clf = joblib.load(MODEL_PATH) os.environ["STREAMLIT_HOME"] = "/tmp/.streamlit" # 關閉 CORS、關掉使用者統計(可選) os.environ["STREAMLIT_SERVER_ENABLE_CORS"] = "false" os.environ["STREAMLIT_GATHER_USAGE_STATS"] = "false" # 建目錄 os.makedirs("/tmp/.streamlit", exist_ok=True) # ─────────────── 2. 把 DeepFace 的 HOME 指到 /tmp/.deepface ─────────────── os.environ["DEEPFACE_HOME"] = "/tmp/.deepface" # 建好 weights 子目錄,DeepFace 下載權重就不會再 mkdir / os.makedirs("/tmp/.deepface/weights", exist_ok=True) import streamlit as st import cv2, numpy as np, base64, io import librosa, joblib from deepface import DeepFace # ── 1️⃣ 載入所有模型(DeepFace + 你的語音模型)── @st.cache_resource(show_spinner=False) def load_models(): # a) DeepFace 預熱 DeepFace.analyze( img_path = np.zeros((224,224,3), dtype=np.uint8), actions = ['emotion'], enforce_detection=False ) # b) 載入你 commit 到 repo 的語音模型檔案 # 取得目前檔案 (streamlit_app.py) 的資料夾路徑 root = os.path.dirname(__file__) # 然後從 src/ 底下讀模型檔 model_path = os.path.join(root, "voice_model.joblib") audio_model = joblib.load(model_path) return audio_model audio_model = load_models() # ── 2️⃣ 文本情緒分析函式 ───────────────────────────────────────────────────── def analyze_text_fn(text: str) -> str: if any(w in text for w in ["開心","快樂","愉快","喜悦","喜悅","歡喜","興奮","高興"]): return "happy" if any(w in text for w in ["生氣","憤怒","不爽","發火","火大","氣憤"]): return "angry" if any(w in text for w in ["傷心","難過","哭","難受","心酸","憂","悲","哀","痛苦","慘","愁"]): return "sad" if any(w in text for w in ["驚訝","意外","嚇","驚詫","詫異","訝異","好奇"]): return "surprise" if any(w in text for w in ["怕","恐懼","緊張","懼","膽怯","畏"]): return "fear" return "neutral" # ── 3️⃣ 語音情緒分析函式 ───────────────────────────────────────────────────── def analyze_audio_fn(wav_bytes: bytes) -> str: # 讀 wav bytes y, sr = librosa.load(io.BytesIO(wav_bytes), sr=None) mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13) mf = np.mean(mfccs.T, axis=0) return audio_model.predict([mf])[0] # ── 4️⃣ Streamlit 介面 ─────────────────────────────────────────────────────── st.set_page_config(page_title="多模態即時情緒分析", layout="wide") st.title("📱 多模態即時情緒分析") tabs = st.tabs(["🔴 Face(本地測試)", "🎤 上傳語音檔", "⌨️ 輸入文字"]) with tabs[0]: st.header("Live Face(僅限本地瀏覽器測試)") st.info("⚠️ Hugging Face Spaces 無法直接開啟攝影機,請在本機使用 `streamlit run app.py` 測試。") # 這邊如果用 streamlit-webrtc 才能在本地呼叫攝影機 # 省略示範,或改成 gradio demo with tabs[1]: st.header("🎤 上傳 WAV 檔進行分析") # 支援 .wav 上傳 wav_file = st.file_uploader("請選擇 .wav 音檔", type=["wav"]) if wav_file is not None: # 讀 bytes,呼叫分析函式 wav_bytes = wav_file.read() emo = analyze_audio_fn(wav_bytes) st.success(f"🎤 語音偵測到的情緒:**{emo}**") with tabs[2]: st.header("輸入文字進行情緒分析") txt = st.text_area("請在此輸入文字") if st.button("開始分析"): emo = analyze_text_fn(txt) st.success(f"📝 文本偵測到的情緒:**{emo}**")