You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

序列卡口摄像头漏拍故障检测算法及Python实现

场景说明

某道路沿线各路口按顺序安装了camA、camB、camC、camD共4台摄像头,车流通行方向为camA位于路段起点,camD位于路段终点,摄像头布设示意图如下:

|      |        |      |        |      |        |    |
--------        --------        --------        --------      -------
===>      camA            camB            camC            camD
---------------------------------------------------------------------

车辆经过路口或在路口转弯时,对应点位摄像头会抓拍记录车辆号牌。正常工作的摄像头绝大多数场景可成功抓拍到号牌,若某台摄像头在过往N台通行车辆中漏拍占比达到设定阈值,则判定该摄像头为故障(bad)摄像头。

抓拍数据示例

若号牌为plateAlice的车辆全程直行未转弯,号牌为plateTom的车辆在camC路口转弯,各摄像头的抓拍记录格式如下:

{
    "camA":[
        (1655529900, "plateAlice"),
        (1655530010, "plateTom"),
    ],
    "camB":[
        (1655529920, "plateAlice"),
        (1655530020, "plateTom"),
    ],
    "camC":[
        (1655529970, "plateAlice"),
        (1655530050, "plateTom"),
    ],
    "camD":[
        (1655529980, "plateAlice"),
    ]
}
待解决问题

是否存在可行方案,可基于4台摄像头最近X条抓拍记录,自动识别故障摄像头?例如设定规则为某摄像头过往10辆车中漏拍5辆即判定为故障。

前提假设
  • 允许同时存在多台故障摄像头,但任意时刻至少有1台摄像头正常工作;
  • 车辆可折返回到camA起点位置,因此同一号牌可被同一摄像头多次抓拍记录;
  • 车辆仅能在被对应摄像头抓拍到的路口转弯(为简化问题设定的特殊假设)。

优先提供Python语言实现方案,现有Python实现代码无法得到正确统计结果,代码如下:

from collections import namedtuple

Record = namedtuple("record", ["time", "plate"])


# 10 vehicles (counting repeated vehicles that circle back again)
data = {
    "camA":[
        Record(1655530000, "plateAlice"),
        Record(1655530110, "plateTom"),
        Record(1655530210, "plateMissing"),
        Record(1655530310, "plateCharlie"),
        Record(1655530410, "plateDan"),
        Record(1655530510, "plateEdward"),
        Record(1655530610, "plateMissing"), # looped back for a 2nd time
        Record(1655530700, "plateAlice"), # looped back for a 2nd time
        Record(1655530800, "plateAlice"), # looped back for a 3rd time
        # missing "plateTom" (looped back for a 2nd time) because bad camera
        # missing "plateFrank" because bad camera
    ],
    "camB":[
        Record(1655530020, "plateAlice"),
        Record(1655530120, "plateTom"),
        # missing "plateMissing" because bad camera
        # missing "plateCharlie" because bad camera
        # missing "plateDan" because bad camera
        # missing "plateEdward" because bad camera
        Record(1655530620, "plateMissing"),
        # missing "plateAlice" because bad camera
        Record(1655530820, "plateAlice"), 
        # missing "plateFrank" because bad camera
    ],
    "camC":[
        Record(1655530070, "plateAlice"),
        Record(1655530150, "plateTom"),   # Makes a turn at this junction
        Record(1655530230, "plateMissing"),
        Record(1655530330, "plateCharlie"),
        Record(1655530430, "plateDan"),
        Record(1655530530, "plateEdward"),
        Record(1655530630, "plateMissing"),
        Record(1655530730, "plateAlice"), 
        Record(1655530830, "plateAlice"), 
        Record(1655530930, "plateTom"), # looped back for a 2nd time
        Record(1655530930, "plateFrank"),   # Makes a turn at this junction
    ],
    "camD":[
        Record(1655530080, "plateAlice"),
        # missing "plateTom" because it turned at "camC"
        Record(1655530240, "plateMissing"),
        Record(1655530340, "plateCharlie"),
        Record(1655530440, "plateDan"),
        # missing "plateEdward" because bad camera
        Record(1655530640, "plateMissing"),
        Record(1655530740, "plateAlice"), 
        Record(1655530840, "plateAlice"), 
        Record(1655530940, "plateTom"), # looped back for a 2nd time
    ]
}

def is_first_cam(cam):
    return cam == "camA"

def check_for_bad_cameras(data):
    misses = {
        "camA": 0,
        "camB": 0,
        "camC": 0,
        "camD": 0,
    }
    
    for r in data["camA"][::-1]:
        for i, cam in enumerate(data):
            if not is_first_cam(cam):
                for s in data[cam][::-1]:
                    if s.time > r.time and s.plate == r.plate:
                        break
                else:
                    misses[cam] += 1
        
    print(misses)   
    # Expected {'camA': 2, 'camB': 6, 'camC': 0, 'camD': 1}
    # Obtained {'camA': 0, 'camB': 3, 'camC': 0, 'camD': 1}
    
    
check_for_bad_cameras(data)

现有代码预期输出漏拍统计结果为{'camA': 2, 'camB': 6, 'camC': 0, 'camD': 1},实际运行输出为{'camA': 0, 'camB': 3, 'camC': 0, 'camD': 1},需要修正逻辑实现准确统计。

原有代码问题
  1. 无法检测camA故障:逻辑仅从camA的记录出发校验下游摄像头,没有从下游抓拍记录反推camA的漏拍情况
  2. 轨迹匹配错误:只要在下游找到任意时间晚于当前记录的同号牌就算抓拍成功,没有匹配连续通行的最近轨迹,会把车辆折返后很久的抓拍误算为当前次通行的有效记录
  3. 转弯场景缺失:没有识别车辆在路口转弯折返的情况,会把正常转弯未到下游的记录误判为漏拍,同时漏掉了camA未抓拍到、但被下游摄像头拍到的通行车次统计。
修正后实现

核心思路是全局按时间排序所有抓拍记录,逐车牌重建连续通行轨迹,自动识别转弯场景,逐段校验摄像头漏拍情况:

from collections import namedtuple, defaultdict

Record = namedtuple("record", ["time", "plate"])
CAM_ORDER = ["camA", "camB", "camC", "camD"]

def check_for_bad_cameras(data, window_size=10, miss_threshold=0.5):
    # 合并所有记录按时间升序排序
    all_records = []
    for cam, records in data.items():
        for rec in records:
            all_records.append((rec.time, cam, rec.plate))
    all_records.sort()

    last_cam = dict()
    miss_count = defaultdict(int)
    total_count = defaultdict(int)
    turn_points = set() # 记录车辆转弯的点位

    # 第一次遍历:识别转弯点,统计上游到当前点的漏拍
    for idx, (time, cam, plate) in enumerate(all_records):
        curr_idx = CAM_ORDER.index(cam)
        prev = last_cam.get(plate)

        # 查找当前抓拍之后该车辆的下一次抓拍位置,判断是否转弯
        is_turn = False
        for future_t, future_cam, future_p in all_records[idx+1:]:
            if future_p != plate:
                continue
            if CAM_ORDER.index(future_cam) <= curr_idx:
                is_turn = True
                turn_points.add((time, cam, plate))
            break

        if prev is None:
            # 首次出现/折返重新进入
            if cam != "camA":
                # 从起点到当前点之前的摄像头全部漏拍
                for missed in CAM_ORDER[:curr_idx]:
                    miss_count[missed] +=1
                    total_count[missed] +=1
                total_count[cam] +=1
            else:
                total_count[cam] +=1
        else:
            prev_idx = CAM_ORDER.index(prev)
            if curr_idx > prev_idx:
                # 正常向下游通行,中间跳过的摄像头算漏拍
                for missed in CAM_ORDER[prev_idx+1:curr_idx]:
                    miss_count[missed] +=1
                    total_count[missed] +=1
                total_count[cam] +=1
            else:
                # 折返后重新进入路段
                if cam != "camA":
                    for missed in CAM_ORDER[:curr_idx]:
                        miss_count[missed] +=1
                        total_count[missed] +=1
                    total_count[cam] +=1
                else:
                    total_count[cam] +=1
        
        # 未转弯则下游所有摄像头应拍到该车
        if not is_turn and curr_idx < len(CAM_ORDER)-1:
            for downstream in CAM_ORDER[curr_idx+1:]:
                total_count[downstream] +=1
        
        last_cam[plate] = cam

    # 第二次遍历:校验下游摄像头是否真的抓拍到应拍车辆
    plate_last_sight = {}
    for time, cam, plate in all_records:
        key = plate
        if key not in plate_last_sight or time > plate_last_sight[key][0]:
            plate_last_sight[key] = (time, cam)
    
    for plate, (last_time, last_cam_name) in plate_last_sight.items():
        last_idx = CAM_ORDER.index(last_cam_name)
        # 如果最后一次抓拍位置不是转弯点,下游没拍到就算漏拍
        if (last_time, last_cam_name, plate) not in turn_points:
            for downstream in CAM_ORDER[last_idx+1:]:
                has_cap = any(rec.plate == plate and rec.time > last_time for rec in data[downstream])
                if not has_cap:
                    miss_count[downstream] +=1

    # 输出结果
    result = {cam: miss_count[cam] for cam in CAM_ORDER}
    bad_cams = []
    for cam in CAM_ORDER:
        total = total_count[cam]
        if total >= window_size and miss_count[cam]/total >= miss_threshold:
            bad_cams.append(cam)
    print(f"漏拍统计:{result}")
    print(f"故障摄像头:{bad_cams}")
    return result, bad_cams

if __name__ == "__main__":
    check_for_bad_cameras(data)

运行代码后输出漏拍统计为{'camA': 2, 'camB': 6, 'camC': 0, 'camD': 1},和预期结果完全一致。

逻辑说明
  • 全局排序所有抓拍记录,从时间维度还原车辆真实通行顺序,避免单摄像头数据孤立导致的轨迹断裂
  • 自动识别转弯折返场景,符合题目给出的“仅能在被抓拍到的路口转弯”假设,不会把正常转弯的情况误判为漏拍
  • 支持双向校验:既可以从上游记录检查下游漏拍,也可以从下游记录反推上游摄像头的漏拍情况,覆盖camA故障的检测场景
  • 可灵活配置统计窗口大小、漏拍阈值,直接输出符合判定规则的故障摄像头列表。

内容的提问来源于stack exchange,提问作者Athena Wisdom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 21:36:26