initial commit
This commit is contained in:
commit
0bafbee31f
54 files changed
+6317
No files matched your search
+455
@@ -0,0 +1,455 @@
|
||||
# Bird Detect Cascade Service:YOLOv5 + ConvNeXt 鸟类级联识别服务
|
||||
|
||||
本项目用于将 **YOLOv5 目标检测模型** 和 **ConvNeXt 鸟类细粒度分类模型** 级联起来:
|
||||
|
||||
1. 接收图片或视频;
|
||||
2. 使用 YOLOv5 检测鸟类目标位置;
|
||||
3. 对每个检测框进行目标裁剪;
|
||||
4. 将裁剪图送入 ConvNeXt 分类模型;
|
||||
5. 返回每个目标的位置、检测置信度、分类类别、分类置信度;
|
||||
6. 可选返回画框后的图片或视频。
|
||||
|
||||
项目默认使用 **ONNX Runtime** 推理,适合用 Docker 部署成 HTTP API 服务,方便网站后端调用。
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目结构
|
||||
|
||||
```text
|
||||
bird_detect_cascade_service/
|
||||
├── app/
|
||||
│ ├── main.py # FastAPI 接口入口
|
||||
│ ├── config.py # 配置读取
|
||||
│ ├── schemas.py # 返回结构
|
||||
│ ├── pipeline.py # YOLO + 裁剪 + ConvNeXt 级联流程
|
||||
│ ├── utils.py # 图像工具函数
|
||||
│ └── inference/
|
||||
│ ├── yolo_onnx.py # YOLOv5 ONNX 推理与后处理
|
||||
│ └── convnext_onnx.py # ConvNeXt ONNX 推理
|
||||
├── models/
|
||||
│ ├── yolov5.onnx # 你的 YOLOv5 ONNX 模型,需自行放入
|
||||
│ ├── convnext.onnx # 你的 ConvNeXt ONNX 模型,需自行放入
|
||||
│ ├── det_classes.txt # YOLO 类别名
|
||||
│ └── cls_classes.txt # ConvNeXt 类别名
|
||||
├── test_client.py # 本地接口测试脚本
|
||||
├── requirements.txt
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 模型准备
|
||||
|
||||
### 2.1 YOLOv5 导出 ONNX
|
||||
|
||||
如果你的 YOLOv5 是 `.pt` 权重,一般可在 YOLOv5 项目目录下执行:
|
||||
|
||||
```bash
|
||||
python export.py \
|
||||
--weights /path/to/best.pt \
|
||||
--img 960 960 \
|
||||
--batch 1 \
|
||||
--device cpu \
|
||||
--include onnx \
|
||||
--simplify
|
||||
```
|
||||
|
||||
导出后把 ONNX 文件复制为:
|
||||
|
||||
```bash
|
||||
models/yolov5.onnx
|
||||
```
|
||||
|
||||
### 2.2 ConvNeXt 导出 ONNX
|
||||
|
||||
如果你已有 ConvNeXt 的 `.onnx`,直接复制为:
|
||||
|
||||
```bash
|
||||
models/convnext.onnx
|
||||
```
|
||||
|
||||
如果你只有 PyTorch 权重,需要用你训练时的模型定义导出,输入通常为:
|
||||
|
||||
```text
|
||||
1 × 3 × 224 × 224
|
||||
```
|
||||
|
||||
如果你的 ConvNeXt 输入尺寸不是 224,需要修改 `.env` 或 `docker-compose.yml` 中的:
|
||||
|
||||
```bash
|
||||
CLS_IMG_SIZE=224
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 类别文件格式
|
||||
|
||||
### 3.1 YOLO 检测类别
|
||||
|
||||
`models/det_classes.txt`
|
||||
|
||||
如果 YOLO 只检测鸟,可以写:
|
||||
|
||||
```text
|
||||
bird
|
||||
```
|
||||
|
||||
如果 YOLO 有多个检测类别,例如:
|
||||
|
||||
```text
|
||||
bird
|
||||
person
|
||||
cat
|
||||
dog
|
||||
```
|
||||
|
||||
每一行对应一个类别,行号从 0 开始。
|
||||
|
||||
### 3.2 ConvNeXt 分类类别
|
||||
|
||||
`models/cls_classes.txt`
|
||||
|
||||
每一行一个鸟类类别,顺序必须和训练时类别索引一致,例如:
|
||||
|
||||
```text
|
||||
Aix_galericulata
|
||||
Anas_crecca
|
||||
Anser_cygnoides
|
||||
Cygnus_columbianus
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 本地直接运行
|
||||
|
||||
安装依赖:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
启动服务:
|
||||
|
||||
```bash
|
||||
uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
访问接口文档:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:8000/docs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Docker 构建与运行
|
||||
|
||||
### 5.1 构建镜像
|
||||
|
||||
```bash
|
||||
docker build -t bird-detect-cascade-service:latest .
|
||||
```
|
||||
|
||||
### 5.2 启动容器
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name bird-detect-cascade-service \
|
||||
-p 8000:8000 \
|
||||
-v $(pwd)/models:/app/models \
|
||||
bird-detect-cascade-service:latest
|
||||
```
|
||||
|
||||
### 5.3 使用 docker compose
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
查看日志:
|
||||
|
||||
```bash
|
||||
docker logs -f bird-detect-cascade-service
|
||||
```
|
||||
|
||||
停止服务:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. API 说明
|
||||
|
||||
### 6.1 健康检查
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8000/health
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"det_model_loaded": true,
|
||||
"cls_model_loaded": true
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 图片识别接口
|
||||
|
||||
接口:
|
||||
|
||||
```text
|
||||
POST /predict
|
||||
```
|
||||
|
||||
参数:
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|---|---|---:|---|
|
||||
| file | file | 必填 | 上传图片 |
|
||||
| return_image | bool | false | 是否返回画框后的 base64 图片 |
|
||||
| det_conf | float | 配置文件默认值 | YOLO 检测置信度阈值 |
|
||||
| iou_thres | float | 配置文件默认值 | NMS 的 IoU 阈值 |
|
||||
|
||||
curl 示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://127.0.0.1:8000/predict?return_image=true" \
|
||||
-F "file=@/path/to/test.jpg"
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"image_width": 1920,
|
||||
"image_height": 1080,
|
||||
"num_objects": 1,
|
||||
"results": [
|
||||
{
|
||||
"bbox_xyxy": [500.2, 130.7, 820.9, 600.4],
|
||||
"det_confidence": 0.91,
|
||||
"det_class_id": 0,
|
||||
"det_class_name": "bird",
|
||||
"cls_class_id": 12,
|
||||
"cls_class_name": "Aix_galericulata",
|
||||
"cls_confidence": 0.87
|
||||
}
|
||||
],
|
||||
"annotated_image_base64": "..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Python 测试调用
|
||||
|
||||
```bash
|
||||
python test_client.py \
|
||||
--url http://127.0.0.1:8000/predict \
|
||||
--image /path/to/test.jpg \
|
||||
--save result.jpg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 关键环境变量
|
||||
|
||||
这些配置可以在 `docker-compose.yml` 中修改。
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
|---|---:|---|
|
||||
| DET_MODEL_PATH | /app/models/yolov5.onnx | YOLOv5 ONNX 路径 |
|
||||
| CLS_MODEL_PATH | /app/models/convnext.onnx | ConvNeXt ONNX 路径 |
|
||||
| DET_CLASSES_PATH | /app/models/det_classes.txt | 检测类别文件 |
|
||||
| CLS_CLASSES_PATH | /app/models/cls_classes.txt | 分类类别文件 |
|
||||
| DET_IMG_SIZE | 960 | YOLO 输入尺寸 |
|
||||
| CLS_IMG_SIZE | 224 | ConvNeXt 输入尺寸 |
|
||||
| DET_CONF_THRES | 0.25 | YOLO 默认置信度阈值 |
|
||||
| DET_IOU_THRES | 0.45 | NMS 默认 IoU 阈值 |
|
||||
| CROP_PADDING_RATIO | 0.08 | 裁剪框外扩比例 |
|
||||
| MAX_DETECTIONS | 50 | 单张图片最多返回目标数 |
|
||||
| DEVICE | cpu | onnxruntime 设备,默认 cpu |
|
||||
|
||||
---
|
||||
|
||||
## 9. 给网站同事的集成方式
|
||||
|
||||
网站后端只需要以 `multipart/form-data` 方式上传图片到:
|
||||
|
||||
```text
|
||||
POST http://服务器IP:8000/predict
|
||||
```
|
||||
|
||||
前端或后端拿到 JSON 后,根据 `results` 字段展示识别结果即可。
|
||||
|
||||
如果网站只需要结果,不需要画框图:
|
||||
|
||||
```text
|
||||
return_image=false
|
||||
```
|
||||
|
||||
如果网站需要展示画框图:
|
||||
|
||||
```text
|
||||
return_image=true
|
||||
```
|
||||
|
||||
返回的 `annotated_image_base64` 是 JPEG 的 base64 字符串,前端可拼接为:
|
||||
|
||||
```javascript
|
||||
const imgSrc = "data:image/jpeg;base64," + annotated_image_base64;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 注意事项
|
||||
|
||||
1. YOLO 的输入尺寸必须和导出 ONNX 时一致,例如导出时是 `--img 960 960`,配置里也建议用 `DET_IMG_SIZE=960`。
|
||||
2. ConvNeXt 的类别文件顺序必须和训练时完全一致。
|
||||
3. 如果 YOLO 输出不是标准 YOLOv5 ONNX 格式 `[1, num_boxes, 5 + num_classes]`,需要根据实际输出改 `app/inference/yolo_onnx.py`。
|
||||
4. 如果 ConvNeXt 训练时使用了非 ImageNet 的 mean/std,需要修改 `app/inference/convnext_onnx.py` 中的归一化参数。
|
||||
5. 如果部署机器没有 GPU,默认 CPU 推理即可;如果需要 GPU 版本,需要替换 `onnxruntime` 为 `onnxruntime-gpu`,并使用支持 CUDA 的基础镜像。
|
||||
|
||||
---
|
||||
|
||||
## 11. 常见问题
|
||||
|
||||
### 11.1 为什么要用 HTTP API?
|
||||
|
||||
因为最终要集成到网站中,HTTP API 是最直接的对接方式。网站后端不需要关心模型细节,只需要上传图片并接收 JSON。
|
||||
|
||||
### 11.2 为什么先检测再分类?
|
||||
|
||||
YOLOv5 负责解决“鸟在哪里”,ConvNeXt 负责解决“这只鸟是什么种类”。先裁剪目标区域可以减少背景干扰,更适合细粒度分类。
|
||||
|
||||
### 11.3 多只鸟怎么处理?
|
||||
|
||||
YOLO 检测到几个目标,就裁剪几个目标,并分别送入 ConvNeXt 分类,最终返回一个结果列表。
|
||||
|
||||
### 11.4 没检测到鸟怎么办?
|
||||
|
||||
返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"num_objects": 0,
|
||||
"results": []
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 12. 视频识别接口
|
||||
|
||||
当前版本新增视频接口:
|
||||
|
||||
```text
|
||||
POST /predict_video
|
||||
```
|
||||
|
||||
### 12.1 后台测试视频上传
|
||||
|
||||
启动服务后,在项目根目录执行:
|
||||
|
||||
```bash
|
||||
bash backend_test_video.sh /path/to/test.mp4
|
||||
```
|
||||
|
||||
等价于:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://127.0.0.1:8000/predict_video?sample_interval=25&save_annotated_video=true" \
|
||||
-F "file=@/path/to/test.mp4"
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `sample_interval=25` 表示每 25 帧处理 1 帧;
|
||||
- `save_annotated_video=true` 表示保存画框后视频;
|
||||
- 如果视频很长,可以把 `sample_interval` 调大,例如 50、100,减少计算量。
|
||||
|
||||
### 12.2 视频运行结果保存位置
|
||||
|
||||
如果使用 `docker compose up -d --build` 启动,`docker-compose.yml` 已经挂载:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./models:/app/models
|
||||
- ./runs:/app/runs
|
||||
```
|
||||
|
||||
所以结果会同时出现在:
|
||||
|
||||
容器内:
|
||||
|
||||
```text
|
||||
/app/runs/video_时间_随机ID/
|
||||
```
|
||||
|
||||
宿主机项目目录下:
|
||||
|
||||
```text
|
||||
./runs/video_时间_随机ID/
|
||||
```
|
||||
|
||||
每次处理一个视频,会生成一个新的任务目录,例如:
|
||||
|
||||
```text
|
||||
runs/
|
||||
└── video_20260518_123000_a1b2c3d4/
|
||||
├── source_video.mp4 # 上传的原始视频副本
|
||||
├── annotated.mp4 # 画框后的结果视频
|
||||
├── summary.json # 本次视频任务摘要
|
||||
├── frame_results.json # 每个抽帧的详细识别结果
|
||||
└── frames/
|
||||
├── frame_00000000.jpg # 有识别结果的关键帧截图
|
||||
├── frame_00000025.jpg
|
||||
└── ...
|
||||
```
|
||||
|
||||
### 12.3 返回结果示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"task_dir": "/app/runs/video_20260518_123000_a1b2c3d4",
|
||||
"source_video": "/app/runs/video_20260518_123000_a1b2c3d4/source_video.mp4",
|
||||
"annotated_video": "/app/runs/video_20260518_123000_a1b2c3d4/annotated.mp4",
|
||||
"frames_dir": "/app/runs/video_20260518_123000_a1b2c3d4/frames",
|
||||
"frame_results_json": "/app/runs/video_20260518_123000_a1b2c3d4/frame_results.json",
|
||||
"summary_json": "/app/runs/video_20260518_123000_a1b2c3d4/summary.json",
|
||||
"sample_interval": 25,
|
||||
"processed_frames": 120,
|
||||
"detected_frames": 87,
|
||||
"elapsed_sec": 35.6
|
||||
}
|
||||
```
|
||||
|
||||
### 12.4 网站集成建议
|
||||
|
||||
网站后端上传视频到:
|
||||
|
||||
```text
|
||||
POST http://服务器IP:8000/predict_video
|
||||
```
|
||||
|
||||
如果网站需要展示结果,可以让网站后端读取返回的:
|
||||
|
||||
```text
|
||||
annotated_video
|
||||
frame_results_json
|
||||
summary_json
|
||||
```
|
||||
|
||||
正式部署时更推荐把 `runs/` 目录映射到网站后端可访问的静态文件目录,或者在本服务里再增加 `/files/...` 静态下载接口。
|
||||
Reference in new issue
Block a user