Трекер поменял ID товара: как разобрать повторный подсчёт на видео

Один товар проходит через камеру, на секунду скрывается за рукой и появляется снова. До перекрытия трекер называл его 7, после — 12. Отчёт по уникальным track_id насчитал два товара. Детектор мог правильно найти объект на каждом доступном кадре; ошибка появилась при связывании наблюдений во времени. Чтобы её исправить, нужно видеть историю соответствия реального объекта и выданных трекером идентификаторов.

Идентификатор трека — внутреннее состояние алгоритма, а не готовый идентификатор физического товара. Он может смениться при перекрытии, выходе из кадра или сбросе процесса. Поэтому подсчёт уникальных ID за день не равен подсчёту товаров. Начните с короткого размеченного эпизода: одному физическому объекту присвойте постоянный эталонный номер и сопоставьте ему предсказанные рамки.

Сохраним траекторию на уровне кадров

Для диагностики пригодятся номер кадра, эталонный объект, track_id и рамка. Сопоставление должно учитывать геометрию и обеспечивать единственность пары на кадре. В мини-примере такое соответствие уже задано вручную по размеченному видео. Координаты позволяют увидеть, что движение продолжается плавно даже при смене ID. Код считает изменения идентификатора для одного эталонного объекта.

import numpy as np
# frame, true object, tracker id, box x1 y1 x2 y2
observations = [
    (1, "item-A", 7, [0, 10, 10, 20]),
    (2, "item-A", 7, [2, 10, 12, 20]),
    (4, "item-A", 12, [6, 10, 16, 20]),
    (5, "item-A", 12, [8, 10, 18, 20]),
]
last, switches = {}, []
for frame, true_id, track_id, box in observations:
    if true_id in last and last[true_id][0] != track_id:
        switches.append((true_id, last[true_id][1], frame))
    last[true_id] = (track_id, frame)
boxes = np.array([row[3] for row in observations])
centers = (boxes[:, :2] + boxes[:, 2:]) / 2
print("x_centers", centers[:, 0].tolist())
print("switches", switches)
print("unique_tracks", len({r[2] for r in observations}),
      "true_objects", len({r[1] for r in observations}))
assert switches == [("item-A", 2, 4)]
assert np.all(np.diff(centers[:, 0]) > 0)

Видно одну смену между кадрами 2 и 4, два track_id и один реальный объект. Это диагностический журнал по заданным соответствиям, а не полная реализация IDF1 или HOTA. В стандартной оценке трекинга есть дополнительные правила сопоставления и учёта пропусков. Для сравнения алгоритмов используйте согласованный инструмент оценки; для расследования конкретного пересчёта такой журнал остаётся полезным.

Что смотреть вокруг смены

Откройте несколько кадров до и после отмеченного интервала вместе с рамками и confidence детектора. Если объект пропал у детектора, трекеру нечего связывать. Если рамки есть, но резко изменились координаты после resize, проверьте обратное преобразование. Если рядом два похожих товара, ошибка может быть обменом ID, а не появлением нового объекта. У этих случаев разные способы исправления.

Проверьте параметры времени жизни потерянного трека и частоту кадров. Увеличение допустимого разрыва помогает пережить короткое перекрытие, но может связать уже другой товар с прежним ID. Поэтому оценивайте одновременно смены, ошибочные объединения и пропущенные объекты. Оптимальный параметр для медленного конвейера может не подойти потоку с быстрыми пересечениями траекторий.

Отделим правило учёта от внутреннего ID

Для счётчика товаров обычно задают событие: пересечение линии в нужном направлении, вход в зону или завершение прохода. Сохраняйте контекст этого события и предотвращайте повтор в пределах согласованного эпизода. При смене ID возле линии всё равно потребуется связывание наблюдений либо обработка спорного случая. Простое множество уже встреченных track_id защищает только от повторов одного и того же номера.

Если видео обрабатывается частями, добавьте идентификатор камеры, сессии и время. Два процесса могут независимо выдать track_id=7, а перезапуск способен обнулить счётчик. Склеивать их по одному числу нельзя. При передаче между камерами потребуется отдельное правило идентичности; локальный трекер одной камеры эту задачу не решает.

Для приёмки соберите короткие эпизоды с перекрытием, встречным движением, остановкой на линии и выходом с повторным входом. Покажите итоговый счёт и ошибочные события на каждом. Так изменение параметров оценивается по реальному учёту товаров, а журнал смен ID объясняет, почему итог улучшился или ухудшился.

Для записи спорного эпизода сохраняйте время исходного видео, а не только номер обработанного кадра. При пропуске кадров или изменении частоты номера перестают однозначно задавать интервал. Время поможет найти исходный фрагмент и сопоставить журналы разных этапов.

В курсе вы работаете с трекингом и видеопотоками, оценкой CV-моделей и сервисной обработкой. Следующая задача — собрать воспроизводимый разбор видео, связать траектории с событием подсчёта и контролировать качество вместе с задержкой обработки. Откройте программу платного курса «Computer Vision Engineer: от детекции и Doc-AI до продакшна».

Как перейти от кадров к событиям.

Продолжить на практике

  • Computer Vision Engineer: от детекции и Doc-AI до продакшна ↗

    Практический курс по Computer Vision. Соберёте продакшн-сервис: датасет → обучение (YOLO/RT-DETR, SAM), трекинг, OCR/Doc-AI, веб-приложение (UI+API) и мониторинг. Экспорт в ONNX/TensorRT/OpenVINO, батчинг/квантование под CPU/GPU. Плюс профессиональные блоки: mAP/IoU/Dice и калибровка доверия, QA разметки и аугментации, p95/стоимость, дрейф данных и алерты, guardrails (NMS/правила), A/B-тесты, Triton/gRPC/FastAPI.