- 提取 BaseAlgorithm 基类,四个算法共享 ROI 检查、目标类过滤、告警 ID 管理 - 硬编码比率阈值提取为类常量(RATIO_ON_DUTY_CONFIRM 等) - 滑动窗口添加 maxlen=1000 防内存溢出 - tracks 合并遍历 _scan_tracks() 减少重复遍历 - 比率/均值缓存,process() 入口计算一次 - 拥堵消散比例可配置(dissipation_ratio 参数) - 入侵 CONFIRMING_CLEAR 逻辑拆分为独立方法 - 补齐 AlgorithmType 枚举(illegal_parking、vehicle_congestion) - 修复 _leave_start_time None guard 防 TypeError - 修复 AlgorithmManager 默认参数与构造函数不一致 - 热更新补充支持 illegal_parking 和 vehicle_congestion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
27 KiB
algorithms.py 代码审查报告
审查日期: 2026-04-02 审查文件: algorithms.py (1733行) 审查范围: LeavePostAlgorithm, IntrusionAlgorithm, IllegalParkingAlgorithm, VehicleCongestionAlgorithm, AlgorithmManager
1. 功能基线清单
1.1 LeavePostAlgorithm (离岗检测)
状态定义 (7个):
| 状态 | 常量 | 含义 |
|---|---|---|
| INIT | STATE_INIT |
初始化,等待检测到人 |
| CONFIRMING_ON_DUTY | STATE_CONFIRMING_ON_DUTY |
上岗确认中(需持续检测到人) |
| ON_DUTY | STATE_ON_DUTY |
已确认在岗 |
| CONFIRMING_OFF_DUTY | STATE_CONFIRMING_OFF_DUTY |
离岗确认中(持续未检测到人) |
| OFF_DUTY_COUNTDOWN | STATE_OFF_DUTY_COUNTDOWN |
离岗倒计时(确认离岗后等待告警) |
| ALARMED | STATE_ALARMED |
已告警(等待回岗) |
| NON_WORK_TIME | STATE_NON_WORK_TIME |
非工作时间 |
状态转换矩阵:
| 从 \ 到 | INIT | CONFIRMING_ON_DUTY | ON_DUTY | CONFIRMING_OFF_DUTY | OFF_DUTY_COUNTDOWN | ALARMED | NON_WORK_TIME |
|---|---|---|---|---|---|---|---|
| INIT | - | roi_has_person==True | - | - | - | - | not in_working_hours |
| CONFIRMING_ON_DUTY | detection_ratio==0 | - | elapsed>=confirm_on_duty_sec AND ratio>=0.6 | - | - | - | not in_working_hours |
| ON_DUTY | - | - | - | detection_ratio<0.2 | - | - | not in_working_hours |
| CONFIRMING_OFF_DUTY | - | - | detection_ratio>=0.5 | - | elapsed>=confirm_off_duty_sec AND ratio<0.2 | - | not in_working_hours |
| OFF_DUTY_COUNTDOWN | - | - | roi_has_person==True | - | - | elapsed>=leave_countdown_sec AND cooldown ok | not in_working_hours |
| ALARMED | - | roi_has_person==True | - | - | - | - | not in_working_hours |
| NON_WORK_TIME | in_working_hours | - | - | - | - | - | - |
关键行为:
- 使用滑动窗口(10秒)平滑检测结果,计算 detection_ratio
- ALARMED -> CONFIRMING_ON_DUTY 复用上岗确认状态(不是独立的回岗确认状态)
- 进入 ON_DUTY 状态时,若存在
_last_alarm_id,自动发送alarm_resolve事件 - 进入 NON_WORK_TIME 时,若有未结束告警,发送 resolve_type="non_work_time" 的 resolve 事件
- 冷却期检查使用
cooldown_key = f"{camera_id}_{roi_id}" _last_alarm_id由外部 main.py 通过set_last_alarm_id()回填_leave_start_time在进入 OFF_DUTY_COUNTDOWN 时记录(值等于 state_start_time)
构造函数参数:
confirm_on_duty_sec: int = 10 (上岗确认窗口)confirm_off_duty_sec: int = 30 (离岗确认窗口)confirm_return_sec: int = 10 (回岗确认窗口 -- 注意: 代码中实际未使用此参数)leave_countdown_sec: int = 300 (离岗倒计时)cooldown_sec: int = 600 (告警冷却期)working_hours: Optional[List[Dict]] = Nonetarget_class: Optional[str] = "person"alarm_level: Optional[int] = None (默认2)confirm_leave_sec: Optional[int] = None (向后兼容旧参数名)
1.2 IntrusionAlgorithm (周界入侵)
状态定义 (4个):
| 状态 | 常量 | 含义 |
|---|---|---|
| IDLE | STATE_IDLE |
空闲,无入侵 |
| CONFIRMING_INTRUSION | STATE_CONFIRMING_INTRUSION |
入侵确认中 |
| ALARMED | STATE_ALARMED |
已告警(等待入侵消失) |
| CONFIRMING_CLEAR | STATE_CONFIRMING_CLEAR |
入侵消失确认中 |
状态转换矩阵:
| 从 \ 到 | IDLE | CONFIRMING_INTRUSION | ALARMED | CONFIRMING_CLEAR |
|---|---|---|---|---|
| IDLE | - | roi_has_person==True | - | - |
| CONFIRMING_INTRUSION | not roi_has_person OR cooldown内 OR state_start_time==None | - | elapsed>=confirm_intrusion_seconds AND cooldown ok | - |
| ALARMED | - | - | - | not roi_has_person |
| CONFIRMING_CLEAR | elapsed>=confirm_clear_seconds AND no person | - | person_elapsed>=confirm_intrusion_seconds (持续有人) OR state_start_time==None | - |
关键行为:
- 不使用滑动窗口,直接使用当前帧的
roi_has_person判断 - CONFIRMING_CLEAR 有子状态追踪:
_person_detected_in_clear_time用于判断短暂有人 vs 持续有人 - 冷却期内入侵确认直接回到 IDLE(不触发告警)
- 包含防御性编程: state_start_time==None 时重置到 IDLE
_check_target_class允许 target_class 为 None(匹配所有类别)
构造函数参数:
cooldown_seconds: int = 300confirm_seconds: int = 5 (向后兼容)confirm_intrusion_seconds: Optional[int] = None (默认使用 confirm_seconds)confirm_clear_seconds: Optional[int] = None (默认180)target_class: Optional[str] = Nonealarm_level: Optional[int] = None (默认1)
1.3 IllegalParkingAlgorithm (车辆违停)
状态定义 (5个):
| 状态 | 常量 | 含义 |
|---|---|---|
| IDLE | STATE_IDLE |
空闲 |
| CONFIRMING_VEHICLE | STATE_CONFIRMING_VEHICLE |
车辆确认中 |
| PARKED_COUNTDOWN | STATE_PARKED_COUNTDOWN |
违停倒计时 |
| ALARMED | STATE_ALARMED |
已告警 |
| CONFIRMING_CLEAR | STATE_CONFIRMING_CLEAR |
消失确认中 |
状态转换矩阵:
| 从 \ 到 | IDLE | CONFIRMING_VEHICLE | PARKED_COUNTDOWN | ALARMED | CONFIRMING_CLEAR |
|---|---|---|---|---|---|
| IDLE | - | roi_has_vehicle | - | - | - |
| CONFIRMING_VEHICLE | ratio<0.3 OR state_start_time==None | - | elapsed>=confirm_vehicle_sec AND ratio>=0.6 | - | - |
| PARKED_COUNTDOWN | ratio<0.2 (车离开) OR cooldown内 OR state_start_time==None | - | - | elapsed>=parking_countdown_sec AND cooldown ok | - |
| ALARMED | - | - | - | - | ratio<0.15 |
| CONFIRMING_CLEAR | elapsed>=confirm_clear_sec AND ratio<0.2 OR state_start_time==None | - | - | ratio>=0.5 | - |
关键行为:
- 使用滑动窗口(WINDOW_SIZE_SEC=10秒)
- 支持多类车辆: target_classes 默认 ["car", "truck", "bus", "motorcycle"]
- 告警字段包含
confidence和duration_minutes - CONFIRMING_CLEAR -> IDLE 时清除 alert_cooldowns(新车违停可正常告警)
- ALARMED 进入 CONFIRMING_CLEAR 的阈值(0.15)比其他算法更严格
构造函数参数:
confirm_vehicle_sec: int = 15parking_countdown_sec: int = 300confirm_clear_sec: int = 120cooldown_sec: int = 1800target_classes: Optional[List[str]] = None (默认 ["car", "truck", "bus", "motorcycle"])alarm_level: Optional[int] = None (默认1)
1.4 VehicleCongestionAlgorithm (车辆拥堵)
状态定义 (4个):
| 状态 | 常量 | 含义 |
|---|---|---|
| NORMAL | STATE_NORMAL |
正常 |
| CONFIRMING_CONGESTION | STATE_CONFIRMING_CONGESTION |
拥堵确认中 |
| CONGESTED | STATE_CONGESTED |
拥堵中 |
| CONFIRMING_CLEAR | STATE_CONFIRMING_CLEAR |
消散确认中 |
状态转换矩阵:
| 从 \ 到 | NORMAL | CONFIRMING_CONGESTION | CONGESTED | CONFIRMING_CLEAR |
|---|---|---|---|---|
| NORMAL | - | avg_count >= count_threshold | - | - |
| CONFIRMING_CONGESTION | avg_count < count_threshold OR cooldown内 OR state_start_time==None | - | elapsed >= confirm_congestion_sec AND cooldown ok | - |
| CONGESTED | - | - | - | avg_count < count_threshold * 0.5 |
| CONFIRMING_CLEAR | elapsed >= confirm_clear_sec OR state_start_time==None | - | avg_count >= count_threshold | - |
关键行为:
- 使用滑动窗口(WINDOW_SIZE_SEC=10秒)存储车辆计数,取平均值判断
- 消散需车辆数降到阈值的 50% 以下才开始确认(避免抖动)
- CONFIRMING_CLEAR -> NORMAL 时清除 alert_cooldowns
- 告警字段包含
vehicle_count和confidence
构造函数参数:
count_threshold: int = 5confirm_congestion_sec: int = 60confirm_clear_sec: int = 180cooldown_sec: int = 1800target_classes: Optional[List[str]] = None (默认 ["car", "truck", "bus", "motorcycle"])alarm_level: Optional[int] = None (默认2)
1.5 AlgorithmManager
数据结构:
self.algorithms: Dict[str, Dict[str, Dict[str, Algorithm]]]
结构: { roi_id: { "{roi_id}_{bind_id}": { algo_type: algo_instance } } }
公开方法:
start_config_subscription()- 启动 Redis 配置订阅stop_config_subscription()- 停止配置订阅load_bind_from_redis(bind_id)- 从 Redis 加载单个绑定配置reload_bind_algorithm(bind_id)- 重载单个绑定reload_algorithm(roi_id)- 重载单个 ROI 的所有算法update_algorithm_params(roi_id, bind_id, bind_config)- 仅更新参数,保留状态reload_all_algorithms(preserve_state=True)- 重载全部算法register_algorithm(roi_id, bind_id, algorithm_type, params)- 注册算法(带缓存)process(roi_id, bind_id, camera_id, algorithm_type, tracks, current_time)- 处理检测结果update_roi_params(roi_id, bind_id, algorithm_type, params)- 更新参数reset_algorithm(roi_id, bind_id=None)- 重置算法状态reset_all()- 重置所有算法remove_roi(roi_id)- 移除 ROIremove_bind(roi_id, bind_id)- 移除绑定get_status(roi_id)- 获取状态
2. 接口契约清单
2.1 process() 方法统一签名
所有四个算法的 process() 方法具有相同签名:
def process(
self,
roi_id: str,
camera_id: str,
tracks: List[Dict],
current_time: Optional[datetime] = None,
) -> List[Dict]
tracks 输入格式 (每个元素):
{
"track_id": str, # 跟踪ID
"class": str, # 检测类别 ("person", "car", "truck", ...)
"confidence": float, # 置信度
"bbox": List[float], # 边界框 [x1, y1, x2, y2]
"matched_rois": [ # 匹配的ROI列表
{"roi_id": str}
],
}
2.2 告警输出格式
LeavePostAlgorithm 告警:
{
"track_id": str, # 等于 roi_id
"camera_id": str,
"bbox": List[float], # 可能为空 []
"alert_type": "leave_post",
"alarm_level": int, # 默认 2
"message": "人员离岗告警",
"first_frame_time": str, # 格式: '%Y-%m-%d %H:%M:%S'
}
注意: leave_post 告警使用 track_id 而非 roi_id 字段名(与其他算法不同)。
IntrusionAlgorithm 告警:
{
"roi_id": str,
"camera_id": str,
"bbox": List[float],
"alert_type": "intrusion",
"alarm_level": int, # 默认 1
"message": "检测到周界入侵",
"first_frame_time": str, # 格式: '%Y-%m-%d %H:%M:%S'
}
IllegalParkingAlgorithm 告警:
{
"roi_id": str,
"camera_id": str,
"bbox": List[float],
"alert_type": "illegal_parking",
"alarm_level": int, # 默认 1
"confidence": float,
"message": str, # 动态生成,包含停留分钟数
"first_frame_time": str, # 格式: '%Y-%m-%d %H:%M:%S',可能为 None
"duration_minutes": float,
}
VehicleCongestionAlgorithm 告警:
{
"roi_id": str,
"camera_id": str,
"bbox": List[float],
"alert_type": "vehicle_congestion",
"alarm_level": int, # 默认 2
"confidence": float,
"message": str, # 动态生成,包含平均车辆数和持续秒数
"first_frame_time": str, # 格式: '%Y-%m-%d %H:%M:%S',可能为 None
"vehicle_count": int,
}
alarm_resolve 事件 (所有算法统一格式):
{
"alert_type": "alarm_resolve",
"resolve_alarm_id": str,
"duration_ms": int,
"last_frame_time": str, # 格式: '%Y-%m-%d %H:%M:%S'
"resolve_type": str, # "person_returned" | "non_work_time" | "intrusion_cleared" | "vehicle_left" | "congestion_cleared"
}
2.3 AlgorithmManager.process() 签名
def process(
self,
roi_id: str,
bind_id: str,
camera_id: str,
algorithm_type: str,
tracks: List[Dict],
current_time: Optional[datetime] = None,
) -> List[Dict]
2.4 AlgorithmManager.register_algorithm() 签名
def register_algorithm(
self,
roi_id: str,
bind_id: str,
algorithm_type: str, # "leave_post" | "intrusion" | "illegal_parking" | "vehicle_congestion"
params: Optional[Dict[str, Any]] = None,
)
3. 已发现的潜在问题
3.1 Critical (必须修复)
[C1] LeavePostAlgorithm: confirm_return_sec 参数声明但从未使用
- 位置: 第53行声明,但状态机中 ALARMED -> CONFIRMING_ON_DUTY -> ON_DUTY 的转换直接复用
confirm_on_duty_sec - 影响: 使用者设置
confirm_return_sec以为可以独立控制回岗确认时长,但实际无效 - 建议: 文档中声明此参数复用
confirm_on_duty_sec,或实现独立的回岗确认逻辑
[C2] LeavePostAlgorithm: resolve 事件的 duration_ms 计算依赖 _leave_start_time,但该值可能为 None
- 位置: 第325行
duration_ms = int((current_time - self._leave_start_time).total_seconds() * 1000) - 场景: 如果算法在 OFF_DUTY_COUNTDOWN 之前(即 _leave_start_time 赋值之前)因某种异常跳到 ON_DUTY 且 _last_alarm_id 非空,会抛出 TypeError
- 风险: 低概率但会导致该帧整个 process 调用抛异常
[C3] LeavePostAlgorithm: _leave_start_time 赋值时机问题
- 位置: 第277行
self._leave_start_time = self.state_start_time state_start_time此时是 CONFIRMING_OFF_DUTY 的开始时间(非 OFF_DUTY_COUNTDOWN 的开始时间,因为 state_start_time 在下一行第276行才被更新为 current_time)- 实际效果:
_leave_start_time记录的是离岗确认开始时间,不是倒计时开始时间 - 审查结论: 这是有意设计,离开时间应该从人离开被确认开始计算,但代码注释"记录离开时间"可能造成误解
3.2 Important (应该修复)
[I1] LeavePostAlgorithm 告警字典使用 track_id 而非 roi_id
- 位置: 第299行
"track_id": roi_id - 其他三个算法统一使用
"roi_id": roi_id - 影响: main.py 中的
_handle_detections不直接使用此字段(它有自己的 roi_id),所以不影响功能,但接口不一致
[I2] AlgorithmManager 缺乏线程安全
process()方法未加锁(第1637-1651行),而register_algorithm()也未加锁_update_lock仅在load_bind_from_redis和reload_all_algorithms中使用- 风险: 如果 config_update_worker 线程调用
reload_all_algorithms同时主线程调用process,可能读到不一致的self.algorithms字典 - 缓解: Python GIL 在字典读操作上提供了一定程度的原子性保护,实际崩溃概率很低
[I3] AlgorithmManager.default_params 中 illegal_parking 的 confirm_clear_sec 默认值(30) 与 IllegalParkingAlgorithm 构造函数默认值(120) 不一致
- 位置: 第1233行 vs 第751行
- 影响: 通过 AlgorithmManager 创建的 illegal_parking 算法 confirm_clear_sec 为 30,直接创建为 120
[I4] AlgorithmManager.default_params 中 vehicle_congestion 的 count_threshold 默认值(3) 与 VehicleCongestionAlgorithm 构造函数默认值(5) 不一致
- 位置: 第1237行 vs 第1004行
- 影响: 通过 AlgorithmManager 创建的算法阈值为 3,直接创建为 5
[I5] update_algorithm_params 仅支持 leave_post 和 intrusion
- 位置: 第1461-1496行
- 缺少 illegal_parking 和 vehicle_congestion 的参数更新逻辑(第1494行注释 "其他算法类型可以在此添加")
- 影响:
reload_all_algorithms(preserve_state=True)对 illegal_parking/vehicle_congestion 会回退到load_bind_from_redis,会重置算法状态
[I6] load_bind_from_redis 仅支持 leave_post 和 intrusion
- 位置: 第1322-1403行
- 缺少 illegal_parking 和 vehicle_congestion 的 Redis 加载逻辑
- 影响: 从 Redis 热更新配置时,这两种算法无法被加载
[I7] IntrusionAlgorithm: _get_latest_bbox 不检查 target_class
- 位置: 第456-460行
- 与 LeavePostAlgorithm 不同(第139-142行会检查 target_class)
- 影响: 可能返回非目标类别的 bbox
[I8] _is_in_working_hours 不支持跨午夜时间段
- 位置: 第112行
if start_minutes <= current_minutes < end_minutes - 如果 working_hours 配置为
{"start": "22:00", "end": "06:00"},则无法正确判断 - 影响: 夜班场景可能不工作
3.3 Suggestions (建议改进)
[S1] 滑动窗口的 window_size_sec 硬编码为 10 秒
- LeavePostAlgorithm 第80行:
self.window_size_sec = 10 - IllegalParkingAlgorithm 第744行:
WINDOW_SIZE_SEC = 10 - VehicleCongestionAlgorithm 第1000行:
WINDOW_SIZE_SEC = 10 - 建议: 提取为可配置参数
[S2] LeavePostAlgorithm._update_detection_window 与 IllegalParkingAlgorithm._update_window 实现逻辑相同但代码重复
- 可提取为基类方法或工具函数
[S3] LeavePostAlgorithm.get_state() 使用 datetime.now() 而非参数传入的 current_time
- 位置: 第367-372行
- 在测试场景中会导致状态信息不准确(不影响核心逻辑,仅影响监控展示)
[S4] AlgorithmManager.get_status() 中 leave_post 分支访问 alarm_sent 属性
- 位置: 第1724行
- LeavePostAlgorithm 实际上没有
alarm_sent属性,getattr 返回 False - 这是旧版本残留代码
[S5] AlgorithmManager.remove_roi() 中 bind_id 解析逻辑脆弱
- 位置: 第1703行
key.split("_")[-1] - 如果 bind_id 本身包含下划线,解析会出错
- key 格式为
"{roi_id}_{bind_id}",应该用key[len(roi_id)+1:]提取 bind_id
[S6] _is_in_working_hours 中的 bare except: (第99行, 第124行)
- 应该至少 except Exception 或更具体的异常类型
4. 测试覆盖分析
4.1 test_leave_post_full_workflow.py 覆盖的场景
| 场景 | 状态路径 | 覆盖 |
|---|---|---|
| 上岗确认成功 | INIT -> CONFIRMING_ON_DUTY -> ON_DUTY | YES |
| 离岗确认 | ON_DUTY -> CONFIRMING_OFF_DUTY -> OFF_DUTY_COUNTDOWN | YES |
| 倒计时触发告警 | OFF_DUTY_COUNTDOWN -> ALARMED | YES |
| 回岗 resolve | ALARMED -> CONFIRMING_ON_DUTY -> ON_DUTY (+ resolve) | YES |
| 告警字段验证 | 无 duration_minutes, 有 first_frame_time | YES |
| resolve 字段验证 | duration_ms, resolve_alarm_id, resolve_type | YES |
| set_last_alarm_id 回填 | - | YES |
4.2 test_vehicle_algorithms.py 覆盖的场景
IllegalParkingAlgorithm:
| 场景 | 覆盖 |
|---|---|
| 完整生命周期 IDLE->CONFIRMING->COUNTDOWN->ALARMED->CLEAR->IDLE | YES |
| 车辆短暂路过不触发 | YES |
| 多类车辆检测 (truck, bus) | YES |
| person 不触发违停 | YES |
| 冷却期内不重复告警 | YES |
| resolve 事件发送 | YES |
VehicleCongestionAlgorithm:
| 场景 | 覆盖 |
|---|---|
| 完整生命周期 NORMAL->CONFIRMING->CONGESTED->CLEAR->NORMAL | YES |
| 少于阈值不触发 | YES |
| 短暂拥堵不触发 | YES |
| resolve 事件发送 | YES |
AlgorithmManager:
| 场景 | 覆盖 |
|---|---|
| 注册 illegal_parking | YES |
| 注册 vehicle_congestion | YES |
| process 调用 | YES |
| get_status 调用 | YES |
| 重复注册走缓存 | YES |
| reset_algorithm | YES |
4.3 测试覆盖缺口
LeavePostAlgorithm 未覆盖:
- CONFIRMING_ON_DUTY -> INIT (人消失)
- CONFIRMING_OFF_DUTY -> ON_DUTY (人回来,ratio>=0.5)
- OFF_DUTY_COUNTDOWN -> ON_DUTY (倒计时期间回来)
- 非工作时间自动 resolve
- NON_WORK_TIME -> INIT (工作时间恢复)
- 冷却期内不重复告警
- 空 tracks 输入
- working_hours 配置解析(字符串格式)
IntrusionAlgorithm 完全未测试:
- 完整生命周期 IDLE->CONFIRMING->ALARMED->CLEAR->IDLE
- 入侵确认中人消失
- CONFIRMING_CLEAR 中短暂有人 vs 持续有人
- 冷却期
- resolve 事件
- target_class=None 匹配所有类别
- state_start_time==None 的防御性代码分支
IllegalParkingAlgorithm 未覆盖:
- state_start_time==None 的防御性代码分支 (CONFIRMING_VEHICLE, PARKED_COUNTDOWN, CONFIRMING_CLEAR)
- CONFIRMING_CLEAR -> ALARMED (车辆又出现, ratio>=0.5)
- ALARMED 状态下 ratio 在 0.15-0.5 之间(维持 ALARMED)
VehicleCongestionAlgorithm 未覆盖:
- state_start_time==None 的防御性代码分支
- CONFIRMING_CLEAR -> CONGESTED (又拥堵了)
- 消散阈值 0.5*count_threshold 的边界值
- 冷却期测试
AlgorithmManager 未覆盖:
- start_config_subscription / stop_config_subscription
- load_bind_from_redis
- reload_all_algorithms (含孤立实例清理)
- update_algorithm_params
- remove_roi / remove_bind
- 并发调用安全性
- register_algorithm 的 leave_post 和 intrusion 类型
5. 优化安全边界
5.1 不可修改区域 (功能合约)
以下代码是外部依赖的契约,修改会破坏 main.py 或其他模块:
- 所有算法的
process()方法签名 -- main.py 的_handle_detections直接调用 - 告警字典的字段名和类型 -- main.py 的
_handle_detections依赖alert_type,alarm_level,confidence,bbox,message,first_frame_time,duration_minutes,vehicle_count alarm_resolve事件格式 -- main.py 的 resolve 逻辑依赖resolve_alarm_id,duration_ms,last_frame_time,resolve_typeset_last_alarm_id(alarm_id)方法 -- main.py 回填 alarm_idreset()方法 -- AlgorithmManager 调用get_state()方法 -- AlgorithmManager.get_status() 调用- AlgorithmManager.process() 签名和返回值 -- main.py 直接调用
- AlgorithmManager.register_algorithm() 签名 -- main.py 直接调用
- AlgorithmManager.algorithms 的三层字典结构 -- main.py 直接访问内部实例来获取
_leave_start_time等属性 (第905-911行)
5.2 可安全优化区域
以下代码修改不会影响外部行为:
- 滑动窗口实现 --
_update_detection_window,_update_window,_update_count_window的内部实现可以优化,只要_get_detection_ratio(),_get_window_ratio(),_get_avg_count()的语义不变 _check_detection_in_roi/_check_target_class/_check_target_classes-- 内部实现可优化,接口不变即可_get_latest_bbox/_get_max_confidence-- 辅助方法,内部实现可优化_is_in_working_hours/_parse_time_to_minutes-- 内部实现可优化(建议修复跨午夜问题)- AlgorithmManager 的 Redis 相关方法 --
load_bind_from_redis,reload_*,_config_update_worker可以修改,不影响算法核心逻辑 - 日志输出 -- 所有 logger.* 调用可以调整
default_params字典 -- 可以修正默认值不一致的问题
5.3 高风险修改区域 (需要完整回归测试)
-
状态转换条件 (ratio 阈值) -- 任何 detection_ratio, window_ratio 的阈值变更都可能影响告警灵敏度
- LeavePostAlgorithm: 0.6 (上岗), 0.2 (离岗开始), 0.5 (离岗恢复), 0.2 (离岗确认)
- IllegalParkingAlgorithm: 0.3 (放弃确认), 0.6 (确认有车), 0.2 (车离开), 0.15 (开始消失确认), 0.5 (车又来), 0.2 (消失确认)
- VehicleCongestionAlgorithm: count_threshold (开始确认), 0.5*count_threshold (开始消散)
-
时间比较逻辑 --
elapsed >= xxx_sec的方向(大于等于 vs 大于) -
冷却期检查 --
cooldown_key的构造方式和比较逻辑 -
resolve 事件触发逻辑 -- LeavePostAlgorithm 的 "进入 ON_DUTY 且 _last_alarm_id 存在" 的检查
5.4 重复代码可提取区域
以下方法在多个算法中重复实现,可以提取为基类或 mixin:
| 方法 | 出现在 | 可提取 |
|---|---|---|
_check_detection_in_roi |
全部4个 | YES |
_check_target_class |
LeavePost, Intrusion | YES |
_check_target_classes |
IllegalParking, VehicleCongestion | YES |
_get_latest_bbox |
全部4个 | YES (注意 Intrusion 不检查 target_class) |
_get_max_confidence |
IllegalParking, VehicleCongestion | YES |
set_last_alarm_id |
全部4个 | YES |
| 滑动窗口逻辑 | LeavePost, IllegalParking, VehicleCongestion | YES |
6. config_models.py 与 algorithms.py 的一致性
6.1 AlgorithmType 枚举缺失
config_models.py 中的 AlgorithmType 枚举:
LEAVE_POST = "leave_post"
INTRUSION = "intrusion"
CROWD_DETECTION = "crowd_detection" # 已在 algorithms.py 中注释掉
FACE_RECOGNITION = "face_recognition" # algorithms.py 中不存在
缺失:
ILLEGAL_PARKING = "illegal_parking"-- algorithms.py 已实现但枚举未添加VEHICLE_CONGESTION = "vehicle_congestion"-- algorithms.py 已实现但枚举未添加
6.2 ROIInfo 默认值
config_models.py 的 ROIInfo.confirm_leave_sec 默认值为 10,而 AlgorithmManager.default_params["leave_post"]["confirm_leave_sec"] 为 30,LeavePostAlgorithm.confirm_off_duty_sec 默认为 30。
7. main.py 集成要点
7.1 main.py 对 algorithms.py 的依赖
-
直接访问算法内部属性 (第905-911行):
for attr in ('_leave_start_time', '_parking_start_time', '_congestion_start_time', '_intrusion_start_time'): val = getattr(algo, attr, None)这是紧耦合,如果内部属性名变更会导致 first_frame_time 丢失。
-
alarm_id 回填 (第943-945行):
algo.set_last_alarm_id(alarm_info.alarm_id) -
两层去重机制:
- ROI级别:
_active_alarms[f"{roi_id}_{alert_type}"] - 摄像头级别:
_camera_alert_cooldown[f"{camera_id}_{alert_type}"](30秒冷却)
- ROI级别:
-
duration_ms 在 ext_data 中的计算 (第925行):
"duration_ms": int(alert.get("duration_minutes", 0) * 60 * 1000) if alert.get("duration_minutes") else None,仅 IllegalParkingAlgorithm 的告警包含
duration_minutes,其他算法的 ext_data.duration_ms 为 None。
附录: 状态机可视化
LeavePostAlgorithm
+--> NON_WORK_TIME --+
| (any state) | (in_working_hours)
| v
INIT --+--> CONFIRMING_ON_DUTY ---> ON_DUTY ---> CONFIRMING_OFF_DUTY
^ | ^ |
+----------+ | v
(ratio==0) | OFF_DUTY_COUNTDOWN
| | |
| (roi_has_person) | v
| +---------------+ ALARMED
| | |
+-------+ (roi_has_person) |
ON_DUTY <-- (if _last_alarm_id, send resolve)
IntrusionAlgorithm
IDLE ---> CONFIRMING_INTRUSION ---> ALARMED ---> CONFIRMING_CLEAR ---> IDLE
^ | | |
+------------+ +--------+
(person gone) (person back >= confirm_intrusion_sec)
-> ALARMED
IllegalParkingAlgorithm
IDLE --> CONFIRMING_VEHICLE --> PARKED_COUNTDOWN --> ALARMED --> CONFIRMING_CLEAR --> IDLE
^ | | |
+----------+ | v
(ratio<0.3) (ratio<0.2) | ALARMED
^ | | (ratio>=0.5)
+--------------------------+ v
ALARMED
VehicleCongestionAlgorithm
NORMAL --> CONFIRMING_CONGESTION --> CONGESTED --> CONFIRMING_CLEAR --> NORMAL
^ | |
+--------------+ v
(avg<threshold) CONGESTED
(avg>=threshold)