Как рисовать ограничивающие рамки для детекции объектов
Соберите задачу детекции объектов в Potato, от стартовой конфигурации с тремя метками до боевой конфигурации для видео с дорог, и экспортируйте разметку в COCO или YOLO.
В этом руководстве мы собираем в Potato задачу разметки для детекции объектов: начинаем с конфигурации из трёх меток и заканчиваем экспортом в COCO или YOLO, на котором обучается детектор. Полный список инструментов для изображений и их параметров есть в документации по разметке изображений.
Файл данных
Каждая строка файла данных описывает одно изображение. Potato нужны идентификатор и поле с URL или путём к изображению, а item_properties указывает, какое поле за что отвечает:
{"id": "img_001", "image_url": "https://example.com/street_001.jpg"}
{"id": "img_002", "image_url": "/static/images/street_002.jpg"}Относительный путь разрешается от media_directory, поэтому для локальных изображений не нужен запущенный файловый сервер.
Минимальная конфигурация
За область рисования отвечает схема image_annotation. Ей нужны имя, описание, которое разметчики читают как инструкцию, список включённых инструментов и набор меток:
annotation_task_name: "Object Detection Annotation"
task_dir: "."
output_annotation_dir: "annotation_output/"
data_files:
- "data/images.json"
item_properties:
id_key: id
text_key: image_url
annotation_schemes:
- annotation_type: image_annotation
name: objects
description: "Draw a box around every car, person, and bicycle"
tools:
- bbox
labels:
- name: car
color: "#FF6B6B"
- name: person
color: "#4ECDC4"
- name: bicycle
color: "#45B7D1"Три метки выбраны для старта намеренно. Каждая добавленная метка означает решение, которое разметчик должен принимать для каждого объекта, и пограничный случай, о котором его нужно заранее предупредить. Поэтому начните с классов, которые действительно нужны модели, а остальные добавляйте, когда инструкция устоится.
Запустите сервер и откройте задачу:
potato start config.yaml -p 8000Что делает разметчик
Разметчик выбирает метку, затем протягивает прямоугольник вокруг каждого объекта и подвигает углы, пока рамка не прилегает плотно:
Инструмент ограничивающих рамок позволяет разметчикам рисовать и подписывать прямоугольные области на изображениях
Масштабирование и панорамирование включены по умолчанию, и это важно для мелких или удалённых объектов. Если изображения однородны и лишние элементы управления только мешают, оба можно отключить через zoom_enabled: false и pan_enabled: false.
Горячие клавиши меток
В профиле клавиш по умолчанию v7 инструменты привязаны к буквам: r выбирает рамку, p полигон, b кисть. Горячие клавиши задаются для каждой метки через key_value, и безопаснее всего использовать цифры, потому что метка, чей key_value совпадает с клавишей инструмента, запускает оба действия сразу:
annotation_schemes:
- annotation_type: image_annotation
name: objects
description: "Annotate all visible objects"
tools:
- bbox
labels:
- name: person
color: "#FF6B6B"
key_value: "1"
- name: car
color: "#4ECDC4"
key_value: "2"
- name: motorcycle
color: "#45B7D1"
key_value: "3"
- name: bicycle
color: "#96CEB4"
key_value: "4"
- name: traffic_light
color: "#FFEAA7"
key_value: "5"
- name: stop_sign
color: "#DDA0DD"
key_value: "6"Цвета стоит подбирать, а не соглашаться с первыми попавшимися. В плотных дорожных сценах разметчик просматривает кадр в поисках пропущенного, и два похожих цвета у соседних классов замедляют этот просмотр. Установите keybinding_profile: legacy, если ваши разметчики привыкли к клавишам Potato до версии 2.8 и вы не хотите их переучивать.
Добавление атрибутов объектов
В Potato нет блока атрибутов для отдельной рамки. У метки image_annotation читаются name, color и key_value, а всё остальное в словаре метки отбрасывается. Поэтому вложенный список attributes: ничего не делает, и после того как рамка нарисована, никакой запрос не появляется.
Ту же информацию можно собрать двумя способами. Первый: встроить различие в набор меток, чтобы атрибут стал частью того, что выбирает разметчик:
annotation_schemes:
- annotation_type: image_annotation
name: objects
description: "Draw a box around each object"
tools:
- bbox
labels:
- name: person
color: "#FF6B6B"
- name: person_occluded
color: "#C44536"Второй: спросить об изображении целиком через вторую схему. Такой вопрос относится к элементу, а не к отдельной рамке, и в этом цена: вы узнаёте, что на изображении что-то было перекрыто, но не узнаёте, какая это была рамка:
- annotation_type: multiselect
name: image_conditions
description: "Conditions affecting this image"
labels:
- Heavy occlusion
- Truncated objects
- Low lightАтрибуты сохраняются только при импорте из VIA: via_importer читает region_attributes каждой области и встраивает их в метку. Это понятие времени импорта, а не конфигурации.
Минимум одна рамка
Изображение без рамок неоднозначно: разметчик мог ничего не найти, а мог пропустить элемент. min_annotations снимает эту неоднозначность, отклоняя отправку, в которой фигур меньше порога, поэтому для действительно пустого изображения нужна явная отрицательная метка:
annotation_schemes:
- annotation_type: image_annotation
name: objects
description: "Draw a box around every traffic participant"
tools:
- bbox
labels: [...]
min_annotations: 1Размер отображаемого изображения
Область рисования повторяет отрисованное изображение, поэтому маленькое изображение даёт разметчику маленький холст. Размер задаётся в instance_display, а масштабирование для работы с деталями остаётся доступным:
instance_display:
fields:
- key: image_url
type: image
label: "Frame"
display_options:
max_width: 1200
max_height: 800
zoomable: trueБоевая конфигурация
Конфигурация для дорожных кадров ниже содержит пять классов, которые нужны датасету для детекции. У меток остаются цифровые клавиши, чтобы левая рука разметчика не уходила с цифрового ряда, а min_annotations: 1 оправдан, потому что в каждом кадре этого корпуса есть хотя бы один участник дорожного движения:
annotation_task_name: "Autonomous Driving - Object Detection"
task_dir: "."
output_annotation_dir: "annotation_output/"
data_files:
- "data/driving_frames.json"
item_properties:
id_key: id
text_key: frame_path
annotation_schemes:
- annotation_type: image_annotation
name: objects
description: "Annotate all traffic participants and objects"
tools:
- bbox
min_annotations: 1
labels:
- name: vehicle
color: "#FF6B6B"
key_value: "1"
- name: pedestrian
color: "#4ECDC4"
key_value: "2"
- name: cyclist
color: "#45B7D1"
key_value: "3"
- name: traffic_sign
color: "#FFEAA7"
key_value: "4"
- name: traffic_light
color: "#DDA0DD"
key_value: "5"Пять классов уже на границе того, что цифровые клавиши покрывают без труда. После десяти разметчик больше ищет клавиши, чем смотрит на кадр, и с этого момента разделить задачу на две лучше, чем добавлять ещё одну метку.
Как выглядит разметка
Potato хранит все фигуры одного изображения в плоском JSON-массиве, каким бы инструментом они ни были нарисованы. Координаты нормированы к диапазону от 0 до 1 относительно изображения, поэтому разметка остаётся верной, даже если позже изображение отдаётся в другом размере:
[
{
"type": "bbox",
"label": "vehicle",
"color": "#FF6B6B",
"coordinates": {"x": 0.234, "y": 0.352, "width": 0.120, "height": 0.130}
},
{
"type": "bbox",
"label": "pedestrian",
"color": "#4ECDC4",
"coordinates": {"x": 0.427, "y": 0.370, "width": 0.026, "height": 0.139}
}
]bbox хранит свою геометрию в coordinates в виде {x, y, width, height}. Полигоны хранят в том же ключе список точек, а маски вместо этого хранят кодирование длин серий (run-length encoding) в rle.
Экспорт для обучения
Код обучения детекторов читает COCO или YOLO, и Potato пишет оба формата:
python -m potato.export --config config.yaml --format coco --output ./export/
python -m potato.export --config config.yaml --format yolo --output ./export/Эти два экспорта различаются не только синтаксисом. COCO пишет один файл annotations.json с рамками в пикселях в виде [x, y, width, height] и не копирует изображения, так что значения file_name указывают туда, где изображения уже лежат. YOLO пишет по одному текстовому файлу на изображение с нормированными координатами центра, раскладывает изображения и метки по каталогам train/ и val/ и добавляет data.yaml и classes.txt, чтобы запуск обучения мог прочитать список классов без отдельного указания.
Как сохранить единообразие рамок
Рисуйте рамки вплотную к объекту: каждый пиксель фона внутри рамки модель учится считать передним планом. Запишите, как поступать в неудобных случаях (перекрытие, объекты, обрезанные краем кадра, отражения), и в первую неделю разберите вместе пачку примеров, пока менять инструкцию ещё дёшево. Пока идёт исследование, следите за числом рамок и распределением их размеров у каждого разметчика, потому что резкий сдвиг обычно означает, что кто-то начал иначе понимать правило, а не что изменились изображения.
Что дальше
- Добавьте разметку полигонами, когда рамки слишком грубы для задачи
- Используйте наборы ключевых точек для оценки позы
- Настройте контроль качества, прежде чем исследование выйдет за пределы нескольких разметчиков
Остальные параметры изображений описаны в разделе Разметка изображений.