Когда камера видит грязь лучше, чем объект
Представьте: промышленная линия, десятки камер следят за качеством продукции. Внезапно одна из них покрывается пылью, масляной плёнкой или каплей воды. Система начинает детектировать несуществующие дефекты, останавливает производство, вызывает ложные тревоги. Убытки измеряются тысячами долларов в час.
Именно такую задачу нам подкинули на хакатоне по компьютерному зрению. Не детектировать объекты, а детектировать саму проблему детекции. Ирония в квадрате.
Главная сложность: загрязнения камеры не имеют чёткой формы. Это могут быть размытые пятна, полупрозрачные потёки, локальные блики. Классические методы фильтрации шума здесь бесполезны — они сглаживают всё подряд, включая реальные дефекты продукции.
Почему бинарная сегментация, а не классификация?
Первая ошибка большинства команд — попытка решить задачу как классическую классификацию: «чистая камера» vs «грязная камера». На практике это не работает. Почему?
- Загрязнение может занимать 5% кадра, но полностью искажать критическую область
- Система должна не просто сигнализировать о проблеме, а показывать ГДЕ именно
- Оператору нужно видеть маску загрязнения, чтобы понять, протирать ли объектив или это внутренняя проблема
Бинарная сегментация даёт пиксельную маску. Чёрное — чисто, белое — загрязнено. Просто? Только на словах.
1 Собираем датасет, который не существует
Вот где начинается настоящий ад. Готовых датасетов с разметкой загрязнений камер нет в природе. Придётся создавать с нуля.
Мы использовали трёхэтапный подход:
- Симуляция: накладываем на чистые кадры искусственные загрязнения — гауссовы размытия, шум, полупрозрачные слои. Быстро, но нереалистично.
- Контролируемое загрязнение: берём реальную камеру, наносим на объектив пыль, брызги, отпечатки пальцев. Снимаем один и тот же сценарный ряд до и после. Трудоёмко, зато физически точно.
- Промышленные данные (если повезёт): иногда организаторы хакатона дают реальные записи с проблемных камер. Ценность таких данных — в неожиданных артефактах, которые не придумаешь специально.
2 Выбор архитектуры: YOLO или не YOLO?
Здесь начинаются холивары. Классика для сегментации — U-Net, DeepLab, Mask R-CNN. Но у нас хакатон, а значит:
- Время ограничено
- Вычислительные ресурсы — shared GPU на Colab или Kaggle
- Модель должна работать быстро в инференсе (реальное время!)
Мы выбрали YOLOv8-seg. Почему?
| Архитектура | Скорость обучения | Инференс (RTX 3080) | Качество на наших данных |
|---|---|---|---|
| U-Net | Медленно | ~45 мс | Высокое |
| DeepLabV3+ | Очень медленно | ~120 мс | Высокое |
| YOLOv8-seg (nano) | Быстро | ~8 мс | Достаточное |
8 миллисекунд против 120. В промышленном CV, где камер десятки, эта разница определяет экономику всего проекта.
YOLO даёт приемлемое качество при в 10-15 раз меньших вычислительных затратах. На хакатоне это значит, что вы успеете сделать больше экспериментов, пока другие ждут окончания эпохи обучения тяжёлой модели.
3 Валидация, которая ломает интуицию
Стандартные метрики сегментации — Dice, IoU — здесь работают плохо. Почему?
Представьте: загрязнение в виде тонкой плёнки занимает 90% кадра, но почти прозрачно. Человек его едва видит, модель сегментировала 85%. IoU = 0.85, отличный результат! Но на практике эта плёнка почти не влияет на работу системы детекции.
И наоборот: маленькое жирное пятно в 1% кадра, но прямо на критическом объекте. Модель его пропустила. IoU всё ещё высокий (0.99!), но система уже даёт ложные срабатывания.
Мы ввели кастомную метрику — Weighted Critical IoU:
def weighted_critical_iou(mask_pred, mask_true, critical_regions):
# critical_regions - бинарная маска важных областей кадра
# (например, зона конвейера, места крепления деталей)
iou_total = calculate_iou(mask_pred, mask_true)
iou_critical = calculate_iou(mask_pred * critical_regions,
mask_true * critical_regions)
# Вес критической области - 0.7, остальной кадр - 0.3
return 0.7 * iou_critical + 0.3 * iou_total
Эта метрика отражает реальную бизнес-логику: загрязнение в «пустой» части кадра менее критично, чем в рабочей зоне.
Три ошибки, которые совершают 90% команд
1. Игнорирование временного контекста
Загрязнения камеры редко появляются мгновенно. Обычно это постепенный процесс: пыль накапливается, конденсат образуется капля за каплей.
Умные команды используют временные последовательности. Не один кадр, а окно из 5-10 последовательных кадров. Модель учится отличать статичное загрязнение (на объективе) от динамического (пролетела муха, упала тень).
2. Переобучение на артефакты симуляции
Создали датасет с искусственными загрязнениями? Отлично. Но если модель никогда не видела реальных данных, она будет детектировать только похожие на симуляцию паттерны.
Решение: всегда держите валидационный набор из реальных данных. Даже если их всего 50 кадров. Как собрать такие данные без тысяч часов ручной работы, читайте в этом руководстве.
3. Попытка сегментировать всё подряд
Некоторые загрязнения настолько слабые, что их детекция не имеет практического смысла. Пылинка в 2 пикселя в углу кадра? Игнорируйте.
Мы добавили post-processing фильтр: если площадь сегментированной области меньше N пикселей или её контрастность ниже порога — отбрасываем. Фальшивых срабатываний стало в 3 раза меньше.
Промышленный пайплайн: от хакатона к production
Выиграть хакатон — это 10% успеха. Внедрить решение на реальном производстве — оставшиеся 90%.
Наш пайплайн выглядел так:
- Детектор загрязнений (YOLOv8-seg) работает на каждом N-ном кадре (скажем, каждый 10-й кадр, чтобы снизить нагрузку)
- При обнаружении загрязнения выше порога — триггер полного анализа: следующие 50 кадров анализируются с максимальным качеством
- Визуализация для оператора: накладываем маску загрязнения на исходное видео с прозрачностью 30%. Красное — критическая зона, жёлтое — не критическая
- Исторический трекинг: сохраняем статистику по каждой камере. Если загрязнение нарастает постепенно — предупреждаем заранее, до срабатывания аварийного порога
Самая частая ошибка при переходе к production — попытка запустить тяжёлую модель на каждом кадре. В промышленности с десятками камер это съедает все ресурсы. Используйте каскадный подход: лёгкий детектор «подозрений» → тяжёлый валидатор только при необходимости.
Что делать, если нет GPU для обучения?
Хакатон — не всегда про доступ к A100. Часто приходится работать на CPU или слабых GPU. Вот тактика для таких условий:
- Используйте YOLO nano или tiny версии. Они обучаются на CPU за приемлемое время
- Уменьшайте разрешение для обучения. Загрязнения обычно крупные, детализация в 4K не нужна. 640x640 часто достаточно
- Применяйте transfer learning. Берите предобученные на COCO веса — они уже умеют выделять объекты, остаётся доучить специфику загрязнений
- Используйте техники из статьи «4 метода улучшения моделей визуальной аномалии» — кроп, аугментации, удаление фона
Фильтр видеопотока в реальном времени
Самое интересное начинается, когда нужно не просто детектировать загрязнение, а компенсировать его эффект.
Допустим, камера запылилась, но менять её прямо сейчас нельзя. Можно ли «вычесть» загрязнение из видеопотока?
Мы экспериментировали с адаптивным фильтром:
def adaptive_contamination_filter(frame, contamination_mask):
"""
frame: исходный кадр
contamination_mask: бинарная маска загрязнения от модели
возвращает: кадр с частично компенсированным загрязнением
"""
# Размываем загрязнённые области
blurred = cv2.GaussianBlur(frame, (15, 15), 0)
# Создаём весовую маску (чем сильнее загрязнение, тем больше размытия)
# contamination_intensity можно оценить по контрастности области
weight_mask = estimate_contamination_intensity(contamination_mask)
# Смешиваем исходный кадр с размытым
result = cv2.addWeighted(frame, 1 - weight_mask,
blurred, weight_mask, 0)
return result
Это не идеальное решение, но как временная мера — работает. Главное, не переборщить с компенсацией, иначе можно «замылить» реальные дефекты продукции. О цветовых артефактах и правильной работе в Lab-пространстве читайте в отдельном материале.
Чеклист для вашего хакатона
- ☑️ Определите критичные зоны кадра (где загрязнение наиболее опасно)
- ☑️ Создайте датасет с тремя типами данных: симуляция, контролируемое загрязнение, реальные артефакты
- ☑️ Выберите YOLOv8-seg (nano/small) для баланса скорости и качества
- ☑️ Разработайте кастомную метрику валидации, отражающую бизнес-логику
- ☑️ Добавьте post-processing: фильтрацию по площади и контрастности
- ☑️ Реализуйте каскадную обработку: лёгкий детектор → тяжёлый валидатор
- ☑️ Подготовьте визуализацию для оператора с цветовой индикацией критичности
И последнее: не пытайтесь достичь 99% IoU. В промышленном CV 85% с минимальным количеством ложных срабатываний лучше, чем 95% с регулярными ложными тревогами. Производство прощает пропущенное пятнышко, но не простаивает из-за «паранойи» системы.
Как однажды сказал наш тимлид: «Лучшая модель детекции загрязнений — это регулярная протирка объективов по графику». Но пока люди не научились соблюдать графики, нейросети будут следить за чистотой камер.