Ложные срабатывания детекции: почему камера видит человека, которого нет

·

5 мин чтения

Детект ловит только пару ног над реальным столбом; на фоне станок и верстак
Паспорт генерации изображения

МодельQwen-Image 2512

КвантованиеQ6_K GGUF

Сид7

Шаги26

CFG2.5

Размер1360×768

Сэмплерeuler · simple

Время генерации20:16

Промт генерации изображения

Dark neon 'underside of progress' scene, cyberpunk workshop mood. A night veranda in cold infrared light: a weathered wooden post and a potted plant stand motionless, traced by an icy teal-cyan detection outline, real and static; hovering over them a warm amber holographic detection rectangle framing a schematic genderless wireframe human outline made of glowing amber scanlines, a featureless abstract mannequin silhouette, the machine's confident phantom; the veranda is empty, only the false amber box glows. Wet condensation and fine scratches on the aged wood, moths near the infrared lamp, night damp, infrared grain. Deep shadow, volumetric haze, warm amber and cyan color palette, shallow depth of field, cinematic film still on 35mm, intricately detailed, ultra-detailed, fine textures, rich layered background, ornate mechanical detail, sharp focus, moody atmosphere.

Ночь, веранда и уверенный призрак с коэффициентом 0.52.

Всё началось с тишины. После того как я отключил одну из слишком шумных проверок в системе видеонаблюдения, телефон должен был замолчать. Вместо этого он превратился в виброфоном: десятки уведомлений «Человек на веранде!» посыпались в Telegram одно за другим. Выхожу на крыльцо - пустота. Возвращаюсь, смотрю в монитор - чисто. Но детектор продолжает рапортовать о присутствии кого-то живого прямо в центре кадра.

Ловушка ночного шума

Первая версия была слишком очевидной, почти банальной. YOLO-модели, особенно облегчённые версии для CPU, имеют привычку галлюцинировать на ночных ИК-снимках. 2K картинка, высокая контрастность подсветки и рой мошек, которые в инфракрасном спектре превращаются в летающие белые искры. Тени от дождя или колыхание листвы часто принимаются за движение человеческого тела.

Казалось, всё сходится. Решение лежало на поверхности: поднять порог уверенности (confidence threshold). Если модель уверена в «человеке» лишь на 30%, мы просто игнорируем такие события. Но когда я залез в логи, выяснилось, что самый настырный «призрак» бил по порогу 0.523.

Безопасный стандарт обычно держится на отметке 0.5. Мой объект был не просто шумом, он был уверенно человеком. Чистая статистика здесь бессильна: либо я оставляю порог низким и тону в ложных срабатываниях, либо поднимаю его слишком высоко и перестаю видеть реальных людей.

Улика в сырых координатах

Я скормил ИИ массив сырых метаданных из meta_*.json по всем событиям за сутки. Не просил найти решение, а попросил просто структурировать данные по координатам ограничивающих рамок (bbox). И тут всплыла деталь, которую глаз замыливает при просмотре сотен скриншотов.

# одни и те же координаты рамки, разное время суток, разные классы
22:02   person         0.523   bbox (0,115)-(40,232)
03:01   person         0.327   bbox (0,115)-(40,232)
днём    potted plant     -     bbox (0,115)-(40,232)

# лог детектора по тому же кадру
0: 384x640 5 potted plants, 168.2ms

Оказалось, что в разное время суток в одних и тех же пикселях происходит странная метаморфоза.

  • В 22:02 координаты (0,115)-(40,232) определяются как person.
  • В 03:01 те же самые координаты снова становятся person.
  • Днём в этом же квадрате детектор видит potted plant или chair.

Живой человек не может стоять замороженным в одной точке несколько часов, сменяя класс объекта в зависимости от освещения. Это был статичный предмет.

Когда я открыл снимки и посмотрел на них не как на «события», а как на изображения, всё встало на свои места. В углу кадра стоял оконный столб, рядом - горшечное растение и сруб на фоне. При определённом угле падения ИК-света эта композиция для нейронки выглядела как силуэт человека, замершего в ожидании.

Геометрия против вероятности

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

Решение оказалось чисто геометрическим: создать зону игнорирования для конкретных классов в конкретной части кадра. Чтобы не гадать с координатами, я попросил ИИ проанализировать исходный код детектора на GitHub. В README не было сказано, как именно работает матчинг зон - по всей площади или по центру. Чтение firescrew.go прояснило ситуацию: проверка идёт по центру bbox.

Правка в config.json заняла минуту:

{
  "confidenceMinThreshold": 0.5,
  "ignoreAreasClasses": [
    {
      "class": ["person", "cat", "dog"],
      "coordinates": "0,360,0,110"
    }
  ]
}

Порог уверенности я всё равно поднял с 0.3 до 0.5, чтобы отсечь мелкий мусор, но главный удар нанёс именно зона игнора. Теперь системе всё равно, насколько «уверенно» столб притворяется человеком - в этой области координаты просто вычеркнуты из списка значимых.

Вынос

Вероятностный порог - это иллюзия контроля. Мы привыкли думать, что confidence отражает истинность события, но на деле это лишь мера того, насколько текущий набор пикселей похож на то, что модель видела в обучающей выборке.

Если статичный объект при специфическом свете стабильно получает высокий score, он становится «уверенным призраком». И никакое закручивание гаек в настройках чувствительности его не выселит. Единственная защита в таких случаях - привязка к месту. Геометрия кадра всегда надёжнее, чем вероятность, потому что столб не умеет ходить, а нейронка - этого не знает.

Часть темы «Умный дом без облака» - там собраны остальные разборы этого стека.

Как зашло?