Likhita-17/SoundStrike
GitHub: Likhita-17/SoundStrike
基于经典机器学习的枪声检测与到达方向(DOA)估计系统,提供 FastAPI 后端和 Streamlit 前端的完整端到端音频分析方案。
Stars: 0 | Forks: 0
# 🔊 枪声检测 + 到达方向估计系统
这是一个实时音频分析系统,能够检测上传的 WAV 文件中的枪声,并使用两阶段的机器学习 pipeline 评估其**方位到达方向 (DOA)**,通过 FastAPI 后端和 Streamlit Web 界面提供服务。
## 📌 概述
| 属性 | 值 |
|---|---|
| 任务 | 二元音频分类 + 圆周回归 |
| 检测模型 | 支持向量机 (SVM,RBF 核) |
| DOA 模型 | 双重 Random Forest 回归器 (sin/cos 目标) |
| 检测数据集 | UrbanSound8K — 类别 6 (枪声),748 个片段 |
| DOA 数据集 | STARSS23 (DCASE 2023 挑战赛),600 个场景,35.4 万个标注帧 |
| DOA 准确度 | MAE ≈ 17° · 中值误差 ≈ 6° · 68.7% 的帧误差在 10° 以内 |
| 界面 | Streamlit + FastAPI |
## 🎯 动机
枪声检测和定位在以下领域具有关键应用:
- **公共安全** — 城市环境中的快速事件响应
- **野生动物保护** — 检测非法偷猎活动
- **军事/边境安全** — 实时定位威胁
- **法医学** — 事件发生后的射击方向重建
大多数已部署的系统依赖于昂贵的多麦克风硬件阵列。本项目旨在探索一种**基于学习的单设备方法**,利用双耳线索和频谱特征,是否可以从标准的 FOA 录音中得出有用的方向估计。
## ✨ 功能
- 🎙️ 支持 WAV 文件上传,并在浏览器内进行音频预览
- 🔫 枪声检测,提供概率置信度得分
- 🧭 方位角估计(−180° 到 +180°),带有可靠性标签
- 📊 交互式极坐标罗盘可视化
- ⚡ 通过滑动窗口特征提取实现快速批量推理
- 🗂️ 特征缓存,避免在重新训练时重复提取
- 🌐 提供 REST API (`/predict`, `/health`),便于集成到其他系统
## 🏗️ 架构
```
┌─────────────────────────────────────────────────────┐
│ Streamlit UI (app.py) │
│ Upload WAV → Audio Preview → Analyse → Compass Plot │
└─────────────────────┬───────────────────────────────┘
│ HTTP POST /predict (multipart WAV)
▼
┌─────────────────────────────────────────────────────┐
│ FastAPI Backend (main.py) │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Stage 1 — Gunshot Detection │ │
│ │ librosa.load (sr=22050, stereo) │ │
│ │ → MFCC(40) mean+std [80 dims] │ │
│ │ → ITD + ILD + GCC [3 dims] │ │
│ │ → StandardScaler → SVM (RBF) │ │
│ │ → {detected: bool, confidence: float} │ │
│ └──────────────┬───────────────────────────────┘ │
│ │ if detected == True │
│ ┌──────────────▼───────────────────────────────┐ │
│ │ Stage 2 — DOA Estimation │ │
│ │ librosa.load (sr=24000, stereo) │ │
│ │ → Sliding window (1 s frame, 0.1 s hop) │ │
│ │ → MFCC + ITD + ILD + GCC per frame │ │
│ │ → StandardScaler → RF_sin + RF_cos │ │
│ │ → normalize → arctan2 → azimuth per frame │ │
│ │ → Energy filter (top 15% frames) │ │
│ │ → Confidence² weighted circular mean │ │
│ │ → {azimuth_deg, reliability, frames_used} │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
│ JSON response
▼
┌─────────────────────────────────────────────────────┐
│ Results Display (Streamlit) │
│ Detection badge · Confidence metric │
│ Polar compass plot · Direction interpretation │
└─────────────────────────────────────────────────────┘
```
## 📂 仓库结构
```
metadata_dev1/
├── app.py # Streamlit frontend
├── main.py # FastAPI backend
├── requirements.txt # Pinned dependencies
├── test.py # Quick audio format sanity check
│
├── scripts/
│ ├── train_svm.py # Train gunshot SVM classifier
│ ├── train_angle_model.py # Train RF DOA regressors
│ ├── predict_angle.py # DOA inference (used by main.py)
│ └── evaluate_angle.py # Offline DOA evaluation
│
├── models/
│ ├── svm_gunshot_model.pkl # Trained SVM (112 KB)
│ ├── svm_scaler.pkl # SVM StandardScaler
│ └── models_angle/
│ ├── rf_sin.pkl # RF → sin(azimuth) (~127 MB)
│ ├── rf_cos.pkl # RF → cos(azimuth) (~129 MB)
│ └── angle_scaler.pkl # DOA StandardScaler
│
├── data/
│ ├── master_metadata.csv # Merged STARSS23 labels (354 K rows)
│ ├── foa_dev1/ # 600 First-Order Ambisonics WAV scenes
│ ├── gunshots/ # 374 UrbanSound8K gunshot clips
│ ├── non_gunshots/ # 374 non-gunshot clips (balanced)
│ └── metadata_dev2/ # Per-scene CSV label files
│
├── cache/ # Cached numpy feature arrays
├── outputs/ # Evaluation CSVs and prediction logs
└── notebooks/
├── angle_evaluation_debug.ipynb # Evaluation results with plots
└── s.ipynb # Dataset exploration / CSV merge
```
## 📊 数据集
### STARSS23 (DOA 训练)
- **全称:** Sony-TAu Realistic Spatial Soundscapes 2023
- **来源:** [DCASE 2023 挑战赛任务 3](https://dcase.community/challenge2023/task3)
- **内容:** 跨越多个房间的 600 个一阶 Ambisonics (FOA) 场景录音,包含最多 3 个同时发生的声音事件的逐帧方位角 + 仰角真实标签
- **使用的标签:** 帧索引、类别 ID、方位角(−180° 到 +180°)
- **使用的训练样本:** 从 354,298 个可用帧中随机抽取的 10,000 个帧
### UrbanSound8K (检测训练)
- **来源:** [UrbanSound8K](https://urbansounddataset.weebly.com/urbansound8k.html)
- **内容:** 10 种城市声音类别;类别 6 = 枪声
- **使用的片段:** 374 个枪声 + 374 个非枪声(完美平衡,总计 748 个)
## 🧠 机器学习 Pipeline
### 特征工程(83 维)
| 特征 | 维度 | 原理 |
|---|---|---|
| MFCC 均值(40 个系数) | 40 | 捕捉声音事件的频谱包络/音色 |
| MFCC 标准差(40 个系数) | 40 | 捕捉帧内频谱的时间变化 |
| ITD — 双耳时间差 | 1 | 物理线索:左右声道之间的时间延迟编码了水平方向 |
| ILD — 双耳声级差 | 1 | 物理线索:双耳之间的声级不对称性用于定位声音的左右方位 |
| GCC — 广义互相关峰值 | 1 | 衡量声道间相关性峰值的尖锐度;作为 SNR 的代理指标 |
**为什么使用 sin/cos 回归?**
方位角是一个圆周数值 — 直接对角度(以度为单位)进行回归会在 ±180° 处引入不连续性。分别预测 `sin(θ)` 和 `cos(θ)` 可以避免这个问题;最终的角度可以通过 `arctan2(sin_pred, cos_pred)` 恢复。这是圆周回归中的一种标准技术。
### 模型
**阶段 1 — 枪声检测器**
- 算法:`SVC(kernel='rbf', probability=True)`
- 训练:80/20 分层划分;`StandardScaler` 仅在训练集上拟合
- 推理:`predict_proba` → 类别 1 的概率作为置信度
**阶段 2 — DOA 估计器**
- 算法:两个 `RandomForestRegressor(n_estimators=200)` 模型
- 目标:`sin(azimuth_rad)` 和 `cos(azimuth_rad)`
- 后处理:能量过滤(按 RMS 取前 15% 的帧)+ 加权置信度²的圆周平均值聚合
- 置信度:将 (sin, cos) 预测向量的长度归一化到单位圆;长度越大 = 预测越确定
## 📈 结果
### 枪声检测
| 指标 | 值 |
|---|---|
| 数据集 | 748 个片段(374 个枪声 / 374 个非枪声) |
| 测试集划分 | 20% (150 个片段) |
| 模型 | SVM RBF |
| 训练 | 分层 80/20 划分 |
*详细的分类报告会在训练期间打印 (`scripts/train_svm.py`)。*
### DOA 估计(5,000 帧评估 — `notebooks/angle_evaluation_debug.ipynb`)
| 指标 | 值 |
|---|---|
| MAE | 17.00° |
| 中值误差 | 6.03° |
| 误差在 10° 以内的帧 | 68.66% |
| 误差在 20° 以内的帧 | 83.08% |
| 误差在 30° 以内的帧 | 86.26% |
| 180° 翻转误差 | 1.32% |
| 平均置信度 | 0.636 |
## 🚀 安装说明
### 1. 克隆/下载仓库
```
cd metadata_dev1
```
### 2. 创建虚拟环境
```
python -m venv venv
venv\Scripts\activate # Windows
# source venv/bin/activate # macOS / Linux
```
### 3. 安装依赖项
```
pip install -r requirements.txt
```
## ▶️ 使用说明
### 启动 FastAPI 后端
```
python main.py
```
服务器启动于 `http://127.0.0.1:8000`。
Swagger 文档:`http://127.0.0.1:8000/docs`
### 启动 Streamlit 前端(在单独的终端中)
```
streamlit run app.py
```
开启于 `http://localhost:8501`。
### 工作流程
1. 在浏览器中打开 `http://localhost:8501`
2. 上传 `.wav` 文件
3. 点击 **Analyse Audio**
4. 查看检测结果和罗盘可视化
### 直接使用 API
```
curl -X POST "http://127.0.0.1:8000/predict" \
-F "file=@path/to/audio.wav"
```
**响应(检测到枪声):**
```
{
"gunshot_detected": true,
"gunshot_confidence": 0.923,
"final_azimuth_deg": -47.3,
"reliability": "HIGH",
"frames_used": 12,
"frames_total": 58,
"avg_confidence": 0.741,
"max_confidence": 0.891
}
```
**响应(未检测到枪声):**
```
{
"gunshot_detected": false,
"gunshot_confidence": 0.112,
"message": "No gunshot detected. DOA not computed."
}
```
## 🔁 重新训练
### 重新训练枪声 SVM
```
python scripts/train_svm.py
```
需要填充 `data/gunshots/` 和 `data/non_gunshots/` 目录。
### 重新训练 DOA Random Forest
```
python scripts/train_angle_model.py
```
需要 `data/foa_dev1/` (FOA WAV 文件) 和 `data/master_metadata.csv`。
要使用更多的训练数据,请编辑 `train_angle_model.py` 中的 `max_samples`:
```
X, y_sin, y_cos = build_training_data_fast(..., max_samples=100000, ...)
```
### 运行离线 DOA 评估
```
python scripts/evaluate_angle.py
```
## 📸 截图
| 上传与检测 | DOA 罗盘 |
|---|---|
| *(截图)* | *(截图)* |
## ⚠️ 局限性
1. **仅限 FOA 音频以保证可靠的 DOA** — DOA 模型是在一阶 Ambisonics 录音上训练的。单声道或标准立体声文件将产生不太可靠的方向估计,因为双耳线索 (ITD/ILD) 会变得在物理上毫无意义。
2. **SVM 训练集较小** — 748 个片段(每类 374 个)是一个小数据集。无法保证对全新的声学环境(距离、房间声学、武器类型)的泛化能力。
3. **未充分利用 DOA 训练数据** — 354,298 个可用的标注帧中仅使用了 10,000 个。使用完整数据集进行重新训练可能会显著提高 DOA 准确度。
4. **无仰角估计** — 系统仅估计水平方位角。STARSS23 中存在仰角标签,但目前未使用。
5. **单设备假设** — 未实现多麦克风三角测量。DOA 完全依赖于学习到的声道间特征。
6. **多声源场景** — 在具有多个同时发声源的帧中,模型会预测代表所有声源混合体的单一方向。
## 🔭 未来改进
- [ ] 使用完整的 35.4 万帧数据集重新训练 DOA 模型,以提高准确度
- [ ] 实现正确的 GCC-PHAT(相位变换白化互相关)
- [ ] 添加仰角预测作为第二个输出
- [ ] 使用所有 4 个 FOA 声道(W, X, Y, Z),而不是仅使用声道 0 和 1
- [ ] 用 CRNN / LSTM 替换 Random Forest 以获取时间上下文
- [ ] 在 Streamlit UI 中添加实时麦克风录音支持
- [ ] 实施适当的交叉验证和单折评估表
- [ ] Docker 部署配置
- [ ] 基准比较(GCC-PHAT、SRP-PHAT、MUSIC 算法)
## 📁 技术栈
| 层级 | 技术 |
|---|---|
| 机器学习 | scikit-learn (SVM, Random Forest) |
| 音频 | librosa, soundfile, scipy |
| 后端 | FastAPI, uvicorn |
| 前端 | Streamlit, matplotlib |
| 数据 | STARSS23, UrbanSound8K |
| 语言 | Python 3.10+ |
## 📄 许可证
本项目使用了 [STARSS23 数据集](https://zenodo.org/record/7880637) (知识共享署名 4.0) 和 [UrbanSound8K](https://urbansounddataset.weebly.com/urbansound8k.html)。在重新分发之前,请查阅它们各自的许可证。
标签:Apex, AV绕过, FastAPI, Kubernetes, Streamlit, 公共安全, 声源定位, 机器学习, 枪声检测, 访问控制, 逆向工具, 音频智能