Два соприкасающихся товара: почему семантическая маска не даёт число объектов

На ленте лежат два соприкасающихся пакета. Модель сегментации аккуратно выделяет общий силуэт, площадь почти совпадает с разметкой. Затем код подсчёта связных областей сообщает об одном товаре. Увеличение порога уверенности портит контуры, но число остаётся единицей. Ошибка в том, что маска отвечает на вопрос о классе пикселя, а задача требует различать отдельные экземпляры.

Семантическая сегментация присваивает всем пикселям товара один класс. Когда два товара касаются, между ними может не остаться фоновой полосы. Подсчёт связных компонент тогда возвращает одну область по вполне правильной маске. Чтобы получить количество экземпляров, нужны дополнительные сведения о разделении: отдельные маски объектов, центры, границы или другой подходящий выход модели.

Проверим на маленькой маске

Создадим два квадрата 2×2, которые соприкасаются боковыми сторонами. В маске экземпляров первый имеет номер 1, второй — 2. После сведения к признаку «товар или фон» их идентичность исчезает. scipy.ndimage.label находит связные ненулевые области; по умолчанию в двумерном случае используются соседи по сторонам. Значения номера экземпляра эта функция сама не восстанавливает.

import numpy as np
from scipy.ndimage import label
instances = np.zeros((4, 6), dtype=np.uint8)
instances[1:3, 1:3] = 1
instances[1:3, 3:5] = 2
semantic = (instances > 0).astype(np.uint8)
components, n_components = label(semantic)
instance_ids = np.unique(instances[instances > 0])
print("instance_mask", instances.tolist())
print("semantic_components", n_components, "instances", len(instance_ids))
print("foreground_pixels", int(semantic.sum()))
assert n_components == 1 and len(instance_ids) == 2
assert semantic.sum() == 8
separated = semantic.copy()
separated[:, 3] = 0
_, after_cut = label(separated)
print("after_artificial_cut", after_cut, "pixels", int(separated.sum()))
assert after_cut == 2 and separated.sum() == 6

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

Как выбрать выход модели

Если цель — площадь занятости ленты, общий силуэт может быть достаточен. Если требуется количество пакетов, нужна проверка отдельных экземпляров. Instance segmentation возвращает объектные маски, детекция — прямоугольники объектов. Для выбора сравните тесные группы, частичные перекрытия и обрезанные краем кадра пакеты. Метрика площади не заменяет метрику подсчёта, даже когда оба показателя растут на одной выборке.

Иногда геометрия задачи позволяет разделять соприкосновения по центрам или distance transform с watershed. Но такие методы требуют собственных условий: устойчивой формы, различимых центров и согласованных параметров. Их нужно оценивать на размеченных экземплярах. Если данных для центров нет и все предметы образуют одну плоскую область, восстановление числа из одной бинарной маски может быть неоднозначным.

Разметка должна содержать нужный ответ

Попросите разметчиков разделять каждый товар отдельным идентификатором, включая соприкасающиеся пары. Уточните правило для частично видимых объектов и упаковок внутри упаковки. Иначе модель и оценка получат противоречивые цели. Важно сохранить номера экземпляров при экспорте: перевод цветной разметки в обычную бинарную PNG может незаметно стереть различие уже после разметки.

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

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

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

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

В курсе есть детекция, сегментация, работа с датасетами и прикладные 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.