227 lines
6.6 KiB
Python
227 lines
6.6 KiB
Python
"""统一事件数据契约 (MVP-1 / P0)
|
|
|
|
为后续事件决策引擎、规则引擎、聚合器、LLM 复审等模块提供一致的数据模型。
|
|
所有检测服务 (YOLO / PaddleDetection / ActionDetection 等) 的输出,
|
|
都应通过 ``services/adapters/detection_adapter.py`` 适配为本文件定义的
|
|
``UnifiedDetection`` / ``DetectionResult``。
|
|
|
|
字段命名遵循 Google Python Style Guide,所有字段使用 snake_case。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
import uuid
|
|
from enum import Enum
|
|
from typing import Any, Dict, List, Optional, Tuple
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 枚举
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class EventType(str, Enum):
|
|
"""统一事件类型枚举。
|
|
|
|
与各服务底层 class_name 解耦,事件引擎只面向本枚举决策。
|
|
"""
|
|
|
|
FIRE = "fire"
|
|
SMOKE = "smoke"
|
|
SMOKING = "smoking"
|
|
FIGHT = "fight"
|
|
LOITERING = "loitering"
|
|
STATIONARY = "stationary"
|
|
INTRUSION = "intrusion"
|
|
ILLEGAL_PARKING = "illegal_parking"
|
|
VEHICLE = "vehicle"
|
|
PERSON = "person"
|
|
UNKNOWN = "unknown"
|
|
|
|
|
|
class SeverityLevel(str, Enum):
|
|
"""事件严重性级别 (供规则引擎 / 聚合器使用)。"""
|
|
|
|
INFO = "info"
|
|
LOW = "low"
|
|
MEDIUM = "medium"
|
|
HIGH = "high"
|
|
CRITICAL = "critical"
|
|
|
|
|
|
class DetectionSource(str, Enum):
|
|
"""检测来源 (用于适配器溯源 & 调度策略)。"""
|
|
|
|
YOLO = "yolo"
|
|
PADDLE = "paddle"
|
|
ACTION_DOCKER = "action_docker"
|
|
BEHAVIOR = "behavior" # 由徘徊/静止等行为分析模块产出
|
|
COMPOSITE = "composite" # 复合检测 (例如火灾双模型)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 基础结构
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class BBox(BaseModel):
|
|
"""边界框 (xyxy)。"""
|
|
|
|
model_config = ConfigDict(frozen=True)
|
|
|
|
x1: int
|
|
y1: int
|
|
x2: int
|
|
y2: int
|
|
|
|
@field_validator("x2")
|
|
@classmethod
|
|
def _check_x2(cls, v: int, info) -> int:
|
|
x1 = info.data.get("x1", 0)
|
|
if v < x1:
|
|
raise ValueError(f"bbox.x2({v}) 必须 >= x1({x1})")
|
|
return v
|
|
|
|
@field_validator("y2")
|
|
@classmethod
|
|
def _check_y2(cls, v: int, info) -> int:
|
|
y1 = info.data.get("y1", 0)
|
|
if v < y1:
|
|
raise ValueError(f"bbox.y2({v}) 必须 >= y1({y1})")
|
|
return v
|
|
|
|
@property
|
|
def width(self) -> int:
|
|
return self.x2 - self.x1
|
|
|
|
@property
|
|
def height(self) -> int:
|
|
return self.y2 - self.y1
|
|
|
|
@property
|
|
def area(self) -> int:
|
|
return self.width * self.height
|
|
|
|
@property
|
|
def center(self) -> Tuple[float, float]:
|
|
return (self.x1 + self.x2) / 2.0, (self.y1 + self.y2) / 2.0
|
|
|
|
def to_list(self) -> List[int]:
|
|
return [self.x1, self.y1, self.x2, self.y2]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 统一检测结果
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class UnifiedDetection(BaseModel):
|
|
"""统一检测对象 (单个目标)。
|
|
|
|
适配器将各引擎原始输出转换为本结构,作为事件决策引擎的唯一输入。
|
|
"""
|
|
|
|
detection_id: str = Field(default_factory=lambda: uuid.uuid4().hex)
|
|
track_id: Optional[int] = None
|
|
class_name: str
|
|
label: str
|
|
confidence: float = Field(ge=0.0, le=1.0)
|
|
bbox: BBox
|
|
source: DetectionSource = DetectionSource.YOLO
|
|
model_id: Optional[str] = None
|
|
timestamp: float = Field(default_factory=time.time)
|
|
extra: Dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class DetectionStats(BaseModel):
|
|
"""检测统计信息 (与现有 API 字段保持兼容)。"""
|
|
|
|
total_detections: int = 0
|
|
avg_confidence: float = 0.0
|
|
processing_time: float = 0.0
|
|
model_used: Optional[str] = None
|
|
fps: Optional[float] = None
|
|
|
|
|
|
class DetectionResult(BaseModel):
|
|
"""统一检测结果 (单帧 / 单图)。"""
|
|
|
|
success: bool = True
|
|
message: str = ""
|
|
source: DetectionSource = DetectionSource.YOLO
|
|
model_id: Optional[str] = None
|
|
frame_id: Optional[str] = Field(default_factory=lambda: uuid.uuid4().hex)
|
|
timestamp: float = Field(default_factory=time.time)
|
|
detections: List[UnifiedDetection] = Field(default_factory=list)
|
|
stats: DetectionStats = Field(default_factory=DetectionStats)
|
|
|
|
def to_legacy_dict(self) -> Dict[str, Any]:
|
|
"""转换为旧接口字段格式 (供 API 层向后兼容输出)。"""
|
|
|
|
return {
|
|
"success": self.success,
|
|
"message": self.message,
|
|
"detections": [
|
|
{
|
|
"class": d.class_name,
|
|
"label": d.label,
|
|
"confidence": round(d.confidence, 3),
|
|
"bbox": d.bbox.to_list(),
|
|
**({"track_id": d.track_id} if d.track_id is not None else {}),
|
|
}
|
|
for d in self.detections
|
|
],
|
|
"stats": self.stats.model_dump(exclude_none=True),
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 候选事件 (决策引擎产出)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class CandidateEvent(BaseModel):
|
|
"""候选事件 (供规则引擎与聚合器进一步处理)。"""
|
|
|
|
event_id: str = Field(default_factory=lambda: uuid.uuid4().hex)
|
|
event_type: EventType
|
|
severity: SeverityLevel = SeverityLevel.INFO
|
|
confidence: float = Field(ge=0.0, le=1.0)
|
|
detection: UnifiedDetection
|
|
source_id: Optional[str] = None # 摄像头/视频流标识
|
|
timestamp: float = Field(default_factory=time.time)
|
|
triggered_rules: List[str] = Field(default_factory=list)
|
|
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class AlertEvent(BaseModel):
|
|
"""最终预警事件 (聚合 + 规则筛选后产出)。"""
|
|
|
|
alert_id: str = Field(default_factory=lambda: uuid.uuid4().hex)
|
|
event_type: EventType
|
|
severity: SeverityLevel
|
|
confidence: float = Field(ge=0.0, le=1.0)
|
|
source_id: Optional[str] = None
|
|
detections: List[UnifiedDetection] = Field(default_factory=list)
|
|
rule_name: Optional[str] = None
|
|
first_seen: float = Field(default_factory=time.time)
|
|
last_seen: float = Field(default_factory=time.time)
|
|
occurrence_count: int = 1
|
|
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
__all__ = [
|
|
"EventType",
|
|
"SeverityLevel",
|
|
"DetectionSource",
|
|
"BBox",
|
|
"UnifiedDetection",
|
|
"DetectionStats",
|
|
"DetectionResult",
|
|
"CandidateEvent",
|
|
"AlertEvent",
|
|
]
|