在水利工程 AI 运行管理平台中,搭建 YOLO 视频分析服务 是实现"人员违规入侵检测"、"水面漂浮物识别"、"未穿救生衣告警"等场景的核心。
为了保证与 Django 主业务系统解耦且具备高性能,推荐使用 FastAPI + YOLOv8 (Ultralytics) + OpenCV 的微服务架构。
以下是完整的 YOLO 服务搭建指南,从环境准备到代码实现,再到与 Django 的业务联动。
一、 技术选型与架构
- AI 框架 :
Ultralytics YOLOv8(目前工业界落地最成熟,支持检测、分割,且 API 极其友好) - Web 框架 :
FastAPI(原生异步,高并发,适合做推理 API) - 视频处理 :
OpenCV(cv2) 用于拉取 RTSP 摄像头视频流并抽帧 - 通信方式 : YOLO 服务检测到异常后,通过 HTTP Webhook 主动推送告警信息(含截图)给 Django 后端。
二、 环境准备
建议在带有 NVIDIA GPU 的服务器或开发机上运行,以保障实时推理速度。
bash
# 1. 创建虚拟环境
python -m venv yolo_env
source yolo_env/bin/activate # Windows: yolo_env\Scripts\activate
# 2. 安装核心依赖
pip install fastapi uvicorn opencv-python-headless ultralytics requests pydantic python-multipart
# 3. (可选) 如果需要 GPU 加速,请确保已安装 CUDA 和 PyTorch GPU 版本
#
在水利工程 AI 运行管理平台中,搭建 **YOLO 视频分析服务** 是实现"人员违规入侵检测"、"水面漂浮物识别"、"未穿救生衣告警"等场景的核心。
为了保证与 Django 主业务系统解耦且具备高性能,推荐使用 **FastAPI + YOLOv8 (Ultralytics) + OpenCV** 的微服务架构。
以下是完整的 YOLO 服务搭建指南,从环境准备到代码实现,再到与 Django 的业务联动。
---
### 一、 技术选型与架构
* **AI 框架**: `Ultralytics YOLOv8` (目前工业界落地最成熟,支持检测、分割,且 API 极其友好)
* **Web 框架**: `FastAPI` (原生异步,高并发,适合做推理 API)
* **视频处理**: `OpenCV` (`cv2`) 用于拉取 RTSP 摄像头视频流并抽帧
* **通信方式**: YOLO 服务检测到异常后,通过 **HTTP Webhook** 主动推送告警信息(含截图)给 Django 后端。
---
### 二、 环境准备
建议在带有 NVIDIA GPU 的服务器或开发机上运行,以保障实时推理速度。
```bash
# 1. 创建虚拟环境
python -m venv yolo_env
source yolo_env/bin/activate # Windows: yolo_env\Scripts\activate
# 2. 安装核心依赖
pip install fastapi uvicorn opencv-python-headless ultralytics requests pydantic python-multipart
# 3. (可选) 如果需要 GPU 加速,请确保已安装 CUDA 和 PyTorch GPU 版本
# pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
三、 核心代码实现
我们将服务拆分为两个文件:模型推理封装 (detector.py) 和 FastAPI 路由 (main.py)。
1. detector.py (YOLO 推理与流处理核心)
python
import cv2
import base64
import logging
from ultralytics import YOLO
from typing import List, Dict, Any
# 配置日志
logging.basicConfig(level=logging.INFO)
class WaterYOLODetector:
def __init__(self, model_path: str = "yolov8n.pt", device: str = "0"):
"""
初始化 YOLO 模型
:param model_path: 模型权重路径 (可以是官方预训练模型,也可以是你自己微调的 .pt 文件)
:param device: '0' 表示 GPU, 'cpu' 表示 CPU
"""
logging.info(f"Loading YOLO model from {model_path} on device {device}...")
self.model = YOLO(model_path)
self.device = device
# 水利场景关注的类别 ID (以 COCO 数据集为例,实际需根据你的自定义数据集调整)
# 0: person (人员), 41: cup (漂浮物/垃圾), 等
self.target_classes = {0: "person", 41: "cup"}
def process_frame(self, frame: cv2.Mat, conf_threshold: float = 0.5) -> Dict[str, Any]:
"""
处理单帧图像,返回检测结果和标记后的图像
"""
# 推理
results = self.model(frame, device=self.device, conf=conf_threshold, verbose=False)
detections = []
annotated_frame = frame.copy()
for result in results:
boxes = result.boxes
for box in boxes:
cls_id = int(box.cls[0])
if cls_id in self.target_classes:
class_name = self.target_classes[cls_id]
conf = float(box.conf[0])
x1, y1, x2, y2 = map(int, box.xyxy[0])
detections.append({
"class": class_name,
"confidence": round(conf, 2),
"bbox": [x1, y1, x2, y2]
})
# 在图像上画框 (B, G, R)
color = (0, 0, 255) if class_name == "person" else (0, 255, 0)
cv2.rectangle(annotated_frame, (x1, y1), (x2, y2), color, 2)
cv2.putText(annotated_frame, f"{class_name} {conf:.2f}", (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
return {
"detections": detections,
"annotated_frame": annotated_frame
}
def encode_image_to_base64(self, frame: cv2.Mat) -> str:
"""将 OpenCV 图像编码为 Base64 字符串,便于通过 HTTP 传输"""
_, buffer = cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), 70]) # 70% 质量以减小体积
return base64.b64encode(buffer).decode('utf-8')
2. main.py (FastAPI 服务入口与业务联动)
python
from fastapi import FastAPI, BackgroundTasks, HTTPException
from pydantic import BaseModel
import cv2
import requests
import threading
import time
from detector import WaterYOLODetector
app = FastAPI(title="Water Conservancy YOLO AI Service")
# 全局初始化模型 (避免每次请求都加载模型)
detector = WaterYOLODetector(model_path="yolov8n.pt", device="0") # 若无GPU改为 "cpu"
# 模拟的 RTSP 摄像头流字典,实际中可动态增删
camera_streams = {
"cam_001": "rtsp://admin:password@192.168.1.100:554/stream1",
"cam_002": "rtsp://admin:password@192.168.1.101:554/stream1"
}
# Django 后端接收告警的 Webhook 地址
DJANGO_WEBHOOK_URL = "http://localhost:8000/api/ai-alerts/webhook/"
class AlertPayload(BaseModel):
camera_id: str
alert_type: str
confidence: float
timestamp: str
image_base64: str
def send_alert_to_django(payload: dict):
"""后台任务:将告警推送到 Django"""
try:
# 实际生产中建议加入重试机制或使用 Celery/RabbitMQ
response = requests.post(DJANGO_WEBHOOK_URL, json=payload, timeout=5)
if response.status_code == 200:
print(f"[Success] Alert sent to Django for camera {payload['camera_id']}")
else:
print(f"[Failed] Django responded with {response.status_code}")
except Exception as e:
print(f"[Error] Failed to send alert: {e}")
def process_camera_stream(camera_id: str, rtsp_url: str):
"""
持续处理摄像头视频流 (运行在独立线程中)
"""
print(f"[Info] Starting stream processing for {camera_id}: {rtsp_url}")
cap = cv2.VideoCapture(rtsp_url)
# 设置缓冲区大小,避免延迟累积
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
frame_count = 0
while cap.isOpened():
ret, frame = cap.read()
if not ret:
print(f"[Warning] Failed to read frame from {camera_id}. Reconnecting...")
cap.release()
time.sleep(3) # 断流重连等待
cap = cv2.VideoCapture(rtsp_url)
continue
frame_count += 1
# 性能优化:不需要每帧都检测,例如每 15 帧检测一次 (假设 30fps,即每秒检测 2 次)
if frame_count % 15 != 0:
continue
# 1. YOLO 推理
result = detector.process_frame(frame, conf_threshold=0.6)
# 2. 判断是否触发告警
if result["detections"]:
for det in result["detections"]:
# 构建告警数据
import datetime
payload = {
"camera_id": camera_id,
"alert_type": det["class"],
"confidence": det["confidence"],
"timestamp": datetime.datetime.now().isoformat(),
"image_base64": detector.encode_image_to_base64(result["annotated_frame"])
}
# 3. 异步发送给 Django
# 注意:在真实高并发场景中,这里应推送到 Redis 队列,由专门的 worker 消费
send_alert_to_django(payload)
# (可选) 如果你想提供实时视频流 API 给前端,可以将 result["annotated_frame"] 编码为 MJPEG
# 但生产环境更推荐使用 WebRTC 或专门的流媒体服务器 (如 ZLMediaKit)
@app.on_event("startup")
async def startup_event():
"""服务启动时,自动开启所有摄像头的后台检测线程"""
for cam_id, rtsp_url in camera_streams.items():
thread = threading.Thread(target=process_camera_stream, args=(cam_id, rtsp_url), daemon=True)
thread.start()
print(f"[Info] Thread started for {cam_id}")
@app.get("/health")
def health_check():
return {"status": "healthy", "model_loaded": True}
# 启动命令: uvicorn main:app --host 0.0.0.0 --port 8001
四、 与 Django 后端的联动 (Webhook 接收)
在 Django 端,你需要创建一个接口来接收 YOLO 服务推送的告警,并保存入库或触发短信通知。
Django views.py 示例:
python
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from django.utils import timezone
from .models import AIAlertLog # 假设你有一个告警日志模型
import base64
import os
@api_view(['POST'])
@permission_classes([AllowAny]) # 实际生产中应使用内部 Token 验证
def ai_alert_webhook(request):
try:
data = request.data
camera_id = data.get('camera_id')
alert_type = data.get('alert_type')
image_b64 = data.get('image_base64')
# 1. 保存截图到本地或 OSS/MinIO
if image_b64:
img_data = base64.b64decode(image_b64)
filename = f"alert_{camera_id}_{timezone.now().strftime('%Y%m%d_%H%M%S')}.jpg"
filepath = os.path.join('media/ai_alerts', filename)
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, 'wb') as f:
f.write(img_data)
# 更新数据中的图片路径,移除 base64 以节省数据库空间
data['image_url'] = f"/media/ai_alerts/{filename}"
del data['image_base64']
# 2. 存入数据库
alert = AIAlertLog.objects.create(
camera_id=camera_id,
alert_type=alert_type,
confidence=data.get('confidence'),
alert_time=timezone.now(),
image_url=data.get('image_url'),
is_handled=False
)
# 3. (可选) 触发 Celery 异步任务发送短信/微信通知
# send_sms_alert.delay(alert.id)
return Response({"status": "success", "alert_id": alert.id})
except Exception as e:
return Response({"status": "error", "message": str(e)}, status=400)
五、 针对水利场景的进阶优化建议
-
模型微调 (Fine-tuning) :
官方的
yolov8n.pt只能识别通用物体(如person,cup)。为了精准识别"水面漂浮物"、"未穿救生衣"、"特定型号的水位尺",你需要:- 收集水利场景图片(可使用
Roboflow平台标注)。 - 使用
yolo task=detect mode=train data=your_dataset.yaml epochs=100 imgsz=640训练专属模型。 - 将生成的
best.pt替换到detector.py中。
- 收集水利场景图片(可使用
-
多路视频流性能瓶颈 :
上述代码使用 Python 多线程处理 RTSP。如果摄像头超过 4-8 路,Python 的 GIL 和 OpenCV 的解码会成为瓶颈。
- 中级方案 :使用
multiprocessing为每个摄像头分配独立进程。 - 高级方案 (生产推荐) :放弃 Python 直接拉流,部署专业的流媒体网关(如 ZLMediaKit 或 SRS )。YOLO 服务只通过 HTTP-FLV 或 共享内存 从网关获取抽帧图像,或者直接使用 NVIDIA DeepStream SDK 进行硬件级多路并发推理。
- 中级方案 :使用
-
区域入侵检测 (ROI) :
水利场景中,并非画面里出现"人"就告警,而是"人进入了危险区域(如大坝边缘、闸门附近)"。
- 实现 :在 Django 后台配置每个摄像头的多边形坐标 (Polygon ROI)。YOLO 检测到
person后,计算其bbox中心点是否落在该多边形内,只有落在内部才触发 Webhook 告警。
- 实现 :在 Django 后台配置每个摄像头的多边形坐标 (Polygon ROI)。YOLO 检测到
-
容器化部署 (Docker) :
为了方便在边缘计算盒子或服务器上部署,提供
Dockerfile:dockerfileFROM python:3.9-slim # 安装系统级 OpenCV 依赖 RUN apt-get update && apt-get install -y libgl1-mesa-glx libglib2.0-0 WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . # 预下载模型,避免容器启动时下载失败 RUN python -c "from ultralytics import YOLO; YOLO('yolov8n.pt')" CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001"]