← Артефакты
Фаза 3. Практик·Урок 41

Урок 41. Object Detection

Цель: находить объекты рамками и понимать, чем меряется качество детекции Время: ~60 минут Источник: AI-For-Beginners + документация Ultralytics YOLO

Зачем тебе этот урок

В Уроке 37 сеть отвечала на вопрос «что на картинке» — один ярлык на весь кадр. Заказчику этого не хватает: ему нужно не «на фото есть каска», а «касок три, вот координаты, а этот человек без каски».

Это и есть object detection: что и где, для каждого объекта отдельно. К концу урока разберёшься с форматом данных, IoU и mAP, поймёшь, зачем нужен NMS, и запустишь готовую модель на своей фотографии.

1. Что за задача и чем отличается от соседних

Компьютерное зрение — лесенка задач: чем ниже, тем подробнее ответ и дороже разметка.

ЗадачаЧто на выходеУрок
КлассификацияОдин класс на весь кадр + вероятность37
Object detectionСписок рамок: бокс + класс + confidence на каждую41 (этот)
СегментацияКласс для каждого пикселя, а в instance-варианте — контур каждого экземпляра42

Детекция — золотая середина: рамку нарисовать в разы быстрее, чем обвести контур, а для большинства задач её хватает.

2. Формат данных: бокс, класс, confidence

Одно предсказание — четыре координаты плюс два ярлыка: класс (индекс класса у модели) и confidence (уверенность 0..1). Рамка (bounding box) почти всегда параллельна сторонам кадра, а координаты пишут в двух конвенциях — путаница между ними и есть самый частый баг новичка:

xyxy → (x1, y1, x2, y2)   левый верхний и правый нижний углы
xywh → (xc, yc, w, h)     центр бокса + ширина и высота

xc = (x1 + x2) / 2   yc = (y1 + y2) / 2   w = x2 - x1   h = y2 - y1

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

Боксы «съехали» или вылезли за край кадра — почти всегда это смешанные конвенции: нормализованные числа взяты как пиксельные либо xywh как xyxy. Распечатай пару боксов и сверь глазами.

3. Разметка: сколько это стоит

Модель учится на кадрах, где человек уже нарисовал все рамки. Инструменты: CVAT, Label Studio, Roboflow, labelImg — все экспортируют в формат YOLO и в COCO.

Трудозатраты считай порядками: одна рамка — секунды, кадр с десятком объектов — минуты, тысяча изображений — десятки человеко-часов. Это самая дорогая часть проекта.

  1. Инструкция разметчику до старта: что считаем объектом, что делаем с перекрытыми и обрезанными краем кадра. Противоречивая разметка ставит потолок качеству, который не пробить никакой моделью.
  2. Размечай партиями: 100 кадров → обучение → модель предразмечает следующую партию → человек правит. Правка быстрее рисования в разы. И не бери подряд идущие кадры видео: они почти одинаковые, а в валидацию попадёт утечка.

4. IoU — линейка для рамок

В классификации ответ либо верный, либо нет. С рамкой так не выйдет: предсказанный бокс никогда не совпадает с размеченным пиксель в пиксель — нужна мера «насколько близко». IoU (Intersection over Union) — площадь пересечения двух рамок, делённая на площадь объединения:

IoU = площадь(A ∩ B) / площадь(A ∪ B)

IoU = 0     рамки не пересекаются
IoU ≈ 0.5   перекрылись наполовину — «в целом попал»
IoU = 1     идеальное совпадение

Дальше вводят порог IoU: предсказание с IoU ≥ 0.5 и верным классом считается попаданием. Это соглашение, а не константа: посчитать людей — хватит 0.5, навести захват робота — нужно 0.75.

5. Precision, recall и mAP

С порогом IoU каждое предсказание падает в одну из трёх корзин: TP — нашли настоящий объект (IoU выше порога, класс верный, объект ещё не занят); FP — ложная тревога либо второй бокс на уже найденный объект; FN — размеченный объект, который модель пропустила.

Precision = TP / (TP + FP)   какая доля наших рамок настоящая
Recall    = TP / (TP + FN)   какую долю настоящих объектов мы нашли

Между ними компромисс, и крутит его порог confidence: 0.05 — recall высокий, precision в пол; 0.9 — почти каждая рамка верна, но половина объектов пропущена.

Чтобы не привязываться к одному порогу, строят PR-кривую: прогоняют все пороги и рисуют precision против recall. Площадь под кривой для класса — AP, среднее по классам — mAP, главная метрика детекции. mAP@0.5 считают при пороге IoU 0.5, mAP@0.5:0.95 — усредняя по порогам 0.5...0.95, поэтому оно всегда ниже. Сравнивать модели можно только по одинаковой записи.

6. Non-Maximum Suppression

Сырой выход детектора — сотни рамок: одна машина порождает гроздь почти одинаковых боксов, потому что соседние ячейки сетки говорят одно и то же. NMS (Non-Maximum Suppression) — простой алгоритм отбора:

  1. Выбрось всё, у чего confidence ниже порога, и отсортируй остаток по убыванию уверенности.
  2. Возьми самую уверенную рамку, положи в ответ, а все рамки того же класса с IoU выше порога NMS выбрось — это дубликаты.
  3. Повторяй, пока список не опустеет.

У NMS свой порог IoU, отдельный от порога метрики. Слишком низкий (скажем, 0.3) — алгоритм съедает соседние объекты: два человека вплотную схлопнутся в одного. Слишком высокий — останутся дубликаты.

7. Два семейства подходов

ДвухстадийныеОдностадийные
ИдеяШаг 1 — выделить области-кандидаты, шаг 2 — классифицировать каждую и уточнить рамкуОдин проход: кадр делится на сетку, каждая ячейка сразу выдаёт рамки и классы
КтоR-CNN, Fast R-CNN, Faster R-CNNYOLO, SSD, RetinaNet
БалансАккуратнее на мелочи, но медленнее и тяжелее в продеБыстрее, работает в реальном времени, хуже на мелочи

Что выбирать. Видеопоток, камера, слабое железо — одностадийная, вариантов почти нет. Оффлайн-разбор архива, где кадр может думаться секунду, — можно двухстадийную. Большинство проектов стартует с одностадийной.

8. Anchor boxes и сдвиг к anchor-free

Сети тяжело выдавать координаты «из воздуха». Поэтому придумали anchor boxes — набор рамок-шаблонов разных размеров и пропорций на каждой ячейке сетки. Сеть предсказывает не абсолютный бокс, а поправку к шаблону плюс вероятность, что там вообще объект — маленькую поправку к заготовке выучить куда проще.

Минус: якоря — гиперпараметр. Объекты длинные и узкие (трубы, доски), а шаблоны квадратные — модель мучается. Отсюда сдвиг к anchor-free: сеть напрямую предсказывает центр объекта и расстояния от него до четырёх сторон рамки — меньше гиперпараметров и проще переносить на новые данные. Термин anchor всё равно будет попадаться в конфигах.

9. Практический путь: не обучай с нуля

Детектор с нуля требует огромного датасета и недель вычислений — так не делают почти никогда. Работает логика Урока 38 про transfer learning:

  1. Готовые веса, обученные на большом наборе вроде COCO (80 повседневных классов). Твой объект уже среди них — обучение не нужно вообще.
  2. Класса нет — дообучаешь готовую модель на своих данных. Backbone уже видит края, текстуры и формы, ему остаётся выучить твою специфику.

10. Код: детекция готовой моделью

Ставим ultralytics и opencv-python, веса скачаются сами.

API библиотек детекции движется быстро: имена весов и аргументы меняются от версии к версии. Код ниже — про логику, названия сверь с актуальной документацией Ultralytics.

Отдельно про лицензию: ultralytics распространяется под AGPL — для учёбы и экспериментов это ничего не меняет, но перед тем как нести в коммерческий продукт, прочитай условия или посмотри модели с более мягкой лицензией.
from ultralytics import YOLO
import cv2

WEIGHTS = "yolo11n.pt"   # имя весов сверь с документацией — линейка обновляется
CONF = 0.25              # порог уверенности: ниже — больше находок и мусора
IOU_NMS = 0.7            # порог IoU для NMS: ниже — жёстче режет дубликаты

model = YOLO(WEIGHTS)
names = model.names      # {0: 'person', 1: 'bicycle', ...}

img = cv2.imread("street.jpg")
r = model.predict(img, conf=CONF, iou=IOU_NMS, verbose=False)[0]
print("найдено объектов:", len(r.boxes))

for b in r.boxes:
    x1, y1, x2, y2 = b.xyxy[0].tolist()      # углы в пикселях
    cls_id, conf = int(b.cls[0]), float(b.conf[0])
    print(f"{names[cls_id]:12s} conf={conf:.2f}  xyxy=({x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f})")

    cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 200, 0), 2)
    cv2.putText(img, f"{names[cls_id]} {conf:.2f}", (int(x1), int(y1) - 6),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 200, 0), 1)

cv2.imwrite("street_detected.jpg", img)

# На камере меняется только источник кадров:
cap = cv2.VideoCapture(0)               # 0 — веб-камера
while True:
    ok, frame = cap.read()
    if not ok:
        break
    r = model.predict(frame, conf=CONF, verbose=False)[0]
    cv2.imshow("detection", r.plot())   # plot() рисует рамки сам
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break
cap.release()

11. Дообучение на своих классах

Датасет — картинки и текстовая разметка, файл в файл по имени:

dataset/
  images/train/img_001.jpg    labels/train/img_001.txt
  images/val/img_042.jpg      labels/val/img_042.txt
  data.yaml

# img_001.txt — строка на объект: class_id xc yc w h (нормализованные)
0 0.412 0.508 0.180 0.240
1 0.130 0.640 0.090 0.115

# data.yaml
path: ./dataset
train: images/train
val: images/val
names: {0: crack, 1: scratch}
model = YOLO(WEIGHTS)          # стартуем с готовых весов, не с нуля
model.train(data="dataset/data.yaml", epochs=100, imgsz=640, batch=16)
metrics = model.val()          # mAP на валидации

Сколько примеров. Универсального числа нет. Ориентир: считай не изображения, а экземпляры объектов на класс, и начинай от нескольких сотен. Разнообразие важнее объёма.

Аугментация (Урок 36) работает так же, с одной оговоркой: при повороте или отражении картинки рамки обязаны трансформироваться вместе с ней — библиотеки делают это сами. И не включай отражение, если у класса есть направление: стрелки и буквы станут неотличимы.

12. Где это применяют

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

Этическая заметка. Как только в кадре появляются люди, ты работаешь с персональными данными. Детекция человека и распознавание лица — разные задачи, но граница тоньше, чем кажется заказчику. Решай задачу минимально необходимыми данными: нужна статистика — храни счётчики, а не кадры. Законность сценария выясняй до пилота.

13. Тонкости и подводные камни

  • Мелкие объекты теряются. Объект в 15 пикселей после свёрток и пулинга исчезает из карт признаков. Лечится увеличением входного размера и тайлингом — нарезкой кадра на перекрывающиеся куски.
  • Плотные сцены. Толпа, стеллаж, стая: объекты перекрывают друг друга, и NMS вычёркивает настоящие рамки как дубликаты. Крути порог IoU в NMS раньше, чем менять модель.
  • Дисбаланс классов. Редкий класс с десятком примеров модель игнорирует, а общий mAP прячет это за цифрами частых. Смотри метрику по каждому классу.
  • Ночь, дождь, новый ракурс. Модель видела дневную съёмку с одного угла — в темноте слепнет. Собирай данные там, где система будет работать, включая худшие условия.
  • Тест врёт. Случайное разбиение кадров одного видео завышает метрику: почти идентичные кадры попадают и в train, и в val. Дели по времени, камере, локации.

14. Глоссарий

Bounding box

Рамка вокруг объекта: xyxy (два угла) или xywh (центр плюс размеры), в пикселях или нормализованно.

Confidence

Уверенность модели в предсказании, 0..1. Порог по ней крутит баланс precision и recall.

IoU

Intersection over Union — пересечение двух рамок, делённое на их объединение. Мера совпадения с разметкой.

NMS

Non-Maximum Suppression — отбор лучшей рамки из грозди перекрывающихся дубликатов.

mAP

Mean average precision — площадь под PR-кривой, усреднённая по классам. Главная метрика детекции.

15. Практика (50 минут)

  1. Поставь ultralytics и opencv-python, запусти код из раздела 10 на своей фотографии с кучей объектов: улица, стол, полка.
  2. Прогони то же изображение с conf=0.05 и conf=0.6, посчитай рамки. Потом покрути IOU_NMS (0.3 и 0.9) на кадре с двумя близко стоящими объектами — на низком пороге один исчезнет.
  3. Возьми рамку из вывода и переведи её из xyxy в нормализованный xywh руками по формулам раздела 2. Сверь с тем, что печатает код.
  4. Запусти детекцию на камере или видеофайле — отследи, что теряется в движении и на смазанных кадрах.
  5. Сними 30–50 фотографий объекта, которого нет в COCO, размети, экспортируй в формате YOLO и дообучи модель. Данных мало, метрика будет скромной — цель пройти путь целиком: разметка → data.yaml → обучение → val → инференс.
  6. Запиши в progress.md: какие пороги confidence и NMS оказались рабочими, сколько ушло на разметку кадра и какой mAP вышел после дообучения.

16. Проверь себя

1. Чем object detection отличается от классификации?
Классификация даёт один ярлык на весь кадр, детекция — рамку, класс и confidence на каждый объект: отвечает ещё и на «где» и «сколько».

2. Что такое IoU и зачем он нужен?
Пересечение предсказанной и размеченной рамок, делённое на их объединение. Рамки не совпадают точно — нужна мера близости и порог попадания.

3. Почему после модели обязательно запускают NMS?
Сеть выдаёт гроздь перекрывающихся рамок на объект. NMS оставляет самую уверенную, остальные с высоким IoU вычёркивает.

4. Когда брать одностадийный детектор, а когда двухстадийный?
Одностадийный — когда нужна скорость: видеопоток, камера, слабое железо. Двухстадийный — когда время не критично, а важна аккуратность на мелочи.

5. mAP на валидации отличный, а в цеху модель работает плохо. Что проверить первым?
Как делили данные: скорее всего в валидации кадры того же дня и камеры, а в цеху другой свет и ракурс.

17. Что должно остаться в голове

  1. Детекция = рамка + класс + confidence на каждый объект. Не «что», а «что и где».
  2. Координаты бывают xyxy и xywh, в пикселях и нормализованные. Путаница конвенций — самый частый баг.
  3. IoU — линейка совпадения рамок, порог IoU выбираешь ты. Сводная метрика — mAP.
  4. Модель всегда выдаёт гроздь дубликатов, NMS оставляет лучший бокс.
  5. Двухстадийные точнее и медленнее, одностадийные быстрее и годятся для реального времени.
  6. Anchor boxes — рамки-шаблоны, к которым предсказывают поправку. Тренд — anchor-free.
  7. На практике: готовые веса → при необходимости дообучение. Дорогая часть проекта — разметка.
  8. Разрыв между тестом и продом создают данные: мелочь, перекрытия, редкие классы, ночь и погода.
📌 Закрепление: запусти готовую модель на своей фотографии и покрути два порога — confidence и IoU в NMS. Когда увидишь, как одни и те же веса превращаются из «находит всё подряд» в «пропускает половину», ты поймёшь детекцию лучше, чем из любого объяснения.