Update app.py
Browse files
app.py
CHANGED
|
@@ -6,6 +6,7 @@ import traceback
|
|
| 6 |
import numpy as np
|
| 7 |
import librosa
|
| 8 |
import soundfile as sf
|
|
|
|
| 9 |
from fastapi import FastAPI, UploadFile, File, Form, HTTPException, Depends, Security
|
| 10 |
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
| 11 |
from fastapi.responses import JSONResponse, PlainTextResponse
|
|
@@ -28,13 +29,13 @@ def verify_token(credentials: HTTPAuthorizationCredentials = Security(security))
|
|
| 28 |
)
|
| 29 |
return True
|
| 30 |
|
| 31 |
-
# 亲昵口语热词配置
|
| 32 |
CUSTOM_HOTWORDS = os.getenv(
|
| 33 |
"HOTWORDS",
|
| 34 |
"爹地 Daddy 妈咪 学长 学姐 欧巴 欧尼 宝贝 亲爱的 哥哥 姐姐 弟弟 妹妹 师傅 老公 老婆 爹咪 小路 小路哥哥 因因 岚岚 甜甜 芝芝瓜 小选 小蛇 宝宝 叶瑄 艾因 路辰 罗夏 司岚 星星嘟嘟 牧首 教宗 虎先生 小叶哥哥"
|
| 35 |
)
|
| 36 |
|
| 37 |
-
# 口语错别字规则纠错映射
|
| 38 |
COLLOQUIAL_CORRECTIONS = {
|
| 39 |
r"跌地|跌弟|爹底|迭地|嗲地": "爹地",
|
| 40 |
r"学掌|鞋长|薛长|雪长": "学长",
|
|
@@ -79,20 +80,20 @@ emo_model = AutoModel(
|
|
| 79 |
disable_update=True
|
| 80 |
)
|
| 81 |
|
| 82 |
-
# ================= 3. 音频前处理 (
|
| 83 |
def preprocess_audio(input_path: str, target_sr: int = 16000):
|
|
|
|
| 84 |
y, sr = librosa.load(input_path, sr=target_sr, mono=True)
|
| 85 |
if len(y) == 0:
|
| 86 |
return input_path, 0.0
|
| 87 |
|
| 88 |
duration = float(len(y) / sr)
|
| 89 |
|
| 90 |
-
#
|
| 91 |
try:
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
pass
|
| 96 |
|
| 97 |
# 峰值归一化至 -0.5 dB
|
| 98 |
max_val = float(np.max(np.abs(y)))
|
|
@@ -147,7 +148,7 @@ def parse_emotion_and_timeline(emo_res, total_duration: float, window_size: floa
|
|
| 147 |
|
| 148 |
top_emotion = max(emotion_scores, key=emotion_scores.get)
|
| 149 |
|
| 150 |
-
# 10 秒及以下
|
| 151 |
if total_duration <= 10.0:
|
| 152 |
timeline.append({
|
| 153 |
"start": 0.0,
|
|
@@ -269,7 +270,7 @@ async def root():
|
|
| 269 |
async def transcribe_audio(
|
| 270 |
file: UploadFile = File(...),
|
| 271 |
model_name: str = Form(default="whisper-1", alias="model"),
|
| 272 |
-
language: str = Form(default="auto"),
|
| 273 |
response_format: str = Form(default="json"),
|
| 274 |
authenticated: bool = Depends(verify_token)
|
| 275 |
):
|
|
@@ -280,10 +281,10 @@ async def transcribe_audio(
|
|
| 280 |
|
| 281 |
processed_path = None
|
| 282 |
try:
|
| 283 |
-
# 1.
|
| 284 |
processed_path, duration = preprocess_audio(raw_path)
|
| 285 |
|
| 286 |
-
# 2. SenseVoice 识别文本
|
| 287 |
asr_lang = language if language in ["zh", "en", "yue", "ja", "ko", "auto"] else "auto"
|
| 288 |
asr_res = asr_model.generate(
|
| 289 |
input=processed_path,
|
|
@@ -294,7 +295,7 @@ async def transcribe_audio(
|
|
| 294 |
raw_text = asr_res[0]["text"] if asr_res and len(asr_res) > 0 else ""
|
| 295 |
clean_text = re.sub(r"<\|.*?\|>", "", raw_text).strip()
|
| 296 |
|
| 297 |
-
# 3.
|
| 298 |
clean_text = correct_colloquial_words(clean_text)
|
| 299 |
clean_text = format_cjk_spacing(clean_text)
|
| 300 |
|
|
@@ -309,7 +310,7 @@ async def transcribe_audio(
|
|
| 309 |
window_size=5
|
| 310 |
)
|
| 311 |
|
| 312 |
-
# 5. 多模态语气裁决
|
| 313 |
final_emotion, confidence = evaluate_tone_and_emotion(
|
| 314 |
clean_text,
|
| 315 |
emotion_scores,
|
|
|
|
| 6 |
import numpy as np
|
| 7 |
import librosa
|
| 8 |
import soundfile as sf
|
| 9 |
+
import noisereduce as nr
|
| 10 |
from fastapi import FastAPI, UploadFile, File, Form, HTTPException, Depends, Security
|
| 11 |
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
| 12 |
from fastapi.responses import JSONResponse, PlainTextResponse
|
|
|
|
| 29 |
)
|
| 30 |
return True
|
| 31 |
|
| 32 |
+
# 亲昵口语热词配置
|
| 33 |
CUSTOM_HOTWORDS = os.getenv(
|
| 34 |
"HOTWORDS",
|
| 35 |
"爹地 Daddy 妈咪 学长 学姐 欧巴 欧尼 宝贝 亲爱的 哥哥 姐姐 弟弟 妹妹 师傅 老公 老婆 爹咪 小路 小路哥哥 因因 岚岚 甜甜 芝芝瓜 小选 小蛇 宝宝 叶瑄 艾因 路辰 罗夏 司岚 星星嘟嘟 牧首 教宗 虎先生 小叶哥哥"
|
| 36 |
)
|
| 37 |
|
| 38 |
+
# 口语错别字规则纠错映射
|
| 39 |
COLLOQUIAL_CORRECTIONS = {
|
| 40 |
r"跌地|跌弟|爹底|迭地|嗲地": "爹地",
|
| 41 |
r"学掌|鞋长|薛长|雪长": "学长",
|
|
|
|
| 80 |
disable_update=True
|
| 81 |
)
|
| 82 |
|
| 83 |
+
# ================= 3. 音频前处理 (改回初始 noisereduce 降噪) =================
|
| 84 |
def preprocess_audio(input_path: str, target_sr: int = 16000):
|
| 85 |
+
"""强制 16kHz 单声道 -> noisereduce 频域降噪 (0.6 强度) -> 峰值归一化"""
|
| 86 |
y, sr = librosa.load(input_path, sr=target_sr, mono=True)
|
| 87 |
if len(y) == 0:
|
| 88 |
return input_path, 0.0
|
| 89 |
|
| 90 |
duration = float(len(y) / sr)
|
| 91 |
|
| 92 |
+
# 初始降噪方案:适度频谱降噪 (prop_decrease=0.6)
|
| 93 |
try:
|
| 94 |
+
y = nr.reduce_noise(y=y, sr=sr, stationary=True, prop_decrease=0.6)
|
| 95 |
+
except Exception as e:
|
| 96 |
+
print(f"[Warn] 降噪跳过: {e}")
|
|
|
|
| 97 |
|
| 98 |
# 峰值归一化至 -0.5 dB
|
| 99 |
max_val = float(np.max(np.abs(y)))
|
|
|
|
| 148 |
|
| 149 |
top_emotion = max(emotion_scores, key=emotion_scores.get)
|
| 150 |
|
| 151 |
+
# 10 秒及以下不切分
|
| 152 |
if total_duration <= 10.0:
|
| 153 |
timeline.append({
|
| 154 |
"start": 0.0,
|
|
|
|
| 270 |
async def transcribe_audio(
|
| 271 |
file: UploadFile = File(...),
|
| 272 |
model_name: str = Form(default="whisper-1", alias="model"),
|
| 273 |
+
language: str = Form(default="auto"),
|
| 274 |
response_format: str = Form(default="json"),
|
| 275 |
authenticated: bool = Depends(verify_token)
|
| 276 |
):
|
|
|
|
| 281 |
|
| 282 |
processed_path = None
|
| 283 |
try:
|
| 284 |
+
# 1. 降噪与标准化前处理
|
| 285 |
processed_path, duration = preprocess_audio(raw_path)
|
| 286 |
|
| 287 |
+
# 2. SenseVoice 识别文本
|
| 288 |
asr_lang = language if language in ["zh", "en", "yue", "ja", "ko", "auto"] else "auto"
|
| 289 |
asr_res = asr_model.generate(
|
| 290 |
input=processed_path,
|
|
|
|
| 295 |
raw_text = asr_res[0]["text"] if asr_res and len(asr_res) > 0 else ""
|
| 296 |
clean_text = re.sub(r"<\|.*?\|>", "", raw_text).strip()
|
| 297 |
|
| 298 |
+
# 3. 中文口语纠错与中英空格排版
|
| 299 |
clean_text = correct_colloquial_words(clean_text)
|
| 300 |
clean_text = format_cjk_spacing(clean_text)
|
| 301 |
|
|
|
|
| 310 |
window_size=5
|
| 311 |
)
|
| 312 |
|
| 313 |
+
# 5. 多模态综合语气裁决
|
| 314 |
final_emotion, confidence = evaluate_tone_and_emotion(
|
| 315 |
clean_text,
|
| 316 |
emotion_scores,
|