import os import cv2 import json import requests from celery import Celery from ultralytics import YOLO from shapely.geometry import Point, Polygon # 初始化 Celery redis_url = os.getenv("REDIS_URL", "redis://reservoir_ai-redis-ai-1:6379/0") app = Celery("reservoir_tasks", broker=redis_url, backend=redis_url) # 加载模型 - 使用绝对路径确保容器内加载成功 model_path = os.path.join(os.path.dirname(__file__), 'yolov8n.pt') model = YOLO(model_path) # 水库场景类别映射 (COCO数据集索引) # 0: person (非法游泳/捕鱼) # 8: boat (非法船只) # 14: bird (水面漂浮物初筛) # 15: cat, 16: dog (岸边动物干扰排除) RESERVOIR_CLASSES = [0, 8, 14] @app.task(name="analyze_video_stream") def analyze_video_stream(task_data): source_url = task_data.get("source_url") webhook_url = task_data.get("webhook_url") scene_type = task_data.get("scene_type") roi_points = task_data.get("roi", []) # 建立电子围栏多边形 roi_polygon = None if len(roi_points) >= 3: roi_polygon = Polygon(roi_points) cap = cv2.VideoCapture(source_url) if not cap.isOpened(): return {"status": "error", "message": f"无法打开视频流: {source_url}"} frame_count = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break # 抽帧处理:每5帧处理一次,降低CPU压力 frame_count += 1 if frame_count % 5 != 0: continue # 推理推理:设置置信度0.4,只看特定类别 results = model.track( frame, persist=True, conf=0.4, classes=RESERVOIR_CLASSES, verbose=False ) for r in results: if r.boxes: boxes = r.boxes for box in boxes: # 获取坐标 x1, y1, x2, y2 = box.xyxy[0].tolist() conf = float(box.conf[0]) cls = int(box.cls[0]) label = model.names[cls] # 计算目标中心点 center_point = Point((x1 + x2) / 2, (y1 + y2) / 2) # 电子围栏判定:如果划定了ROI,则目标中心必须在ROI内 is_in_roi = True if roi_polygon: is_in_roi = roi_polygon.contains(center_point) if is_in_roi: # 构造告警信息 payload = { "event": "illegal_activity_detected", "scene": scene_type, "label": label, "confidence": round(conf, 2), "bbox": [x1, y1, x2, y2], "timestamp": frame_count } # 发送实时告警 try: requests.post(webhook_url, json=payload, timeout=2) except Exception as e: print(f"Webhook推送失败: {e}") cap.release() return {"status": "completed", "processed_frames": frame_count}