The Daily Newsstand · Free, Always
Friday, October 9, 2026

Как мы сделали простой офлайн‑разметчик для YOLO и COCO с семантической и panoptic‑сегментацией

Translate

В настоящий момент я работаю над проектом по детекции дефектов дорожно‑транспортной инфраструктуры. Находимся на самом начальном этапе, подбираем модели для детекции.

Мы начинали работы с моделями детекции — как‑то по инерции с предыдущего проекта. Но этого опыта и знаний (а также обученных ранее моделей) оказалось недостаточно для решения поставленных задач. Тогда мы обратились к сегментационным моделям. И сразу возник вопрос об адекватном инструменте для разметки данных.

Если вы обучали свою модель детекции или сегментации, то знаете: самое долгое — не обучение, а разметка. Готовые инструменты делятся на две группы. Серверные (CVAT, Label Studio) умеют всё, но их нужно разворачивать и поддерживать. Эти инструменты замечательны, но очень непростые в освоении и затратные с аппаратной точки зрения.

Существующие простые десктопные вроде labelImg ставятся за минуту и очень удобные в освоении, но не обладают нужным функционалом — умеют в основном размечать прямоугольники (bbox). И стало понятно, что нам нужна была середина: программа, которая запускается на ноутбуке без сервера и делает разметку bbox и полигональную разметку.

Сделали простую программку, которая делала то, что надо. Но дальше — больше: сначала понадобились повёрнутые рамки (OBB), потом маски, затем многопользовательский режим, а под конец — семантическая и panoptic‑сегментация. И всё это нужно было выгружать в форматы, которые без доработки едят YOLO, Detectron2 и Mask2Former. Так появился YOLO Annotator. Код открыт (GPL-3.0), ниже — что он умеет и как устроен изнутри.

Сначала это был чисто внутренний инструмент, и продавать его мы не собирались. Но со временем стало понятно, что он может пригодиться не только нам: многим нужно что‑то посередине между labelImg и CVAT — без сервера, но с масками, сегментацией и нормальным экспортом. Поэтому решили просто поделиться им с сообществом: программа бесплатная, код открыт под GPL-3.0. Взамен буду рад обратной связи — что неудобно, чего не хватает, где ломается.

Что умеет

Тип разметки

Инструмент

Для чего

Прямоугольник

BBox B

детекция

Полигон

Polygon P

инстанс‑сегментация

Ломаная

Polyline L, Crack C

дороги, трещины, кабели

Повёрнутая рамка

OBB O

аэрофото, документы

Ключевые точки со скелетом

Pose K

поза, ориентиры

Точка

Point .

подсчёт объектов

Метка изображения

Classify

классификация

Маска кистью

Brush M

объекты сложной формы

Область класса

Semantic S

семантическая сегментация, фон

Вокруг этого — импорт готовых YOLO‑датасетов, разбивка на train/val/test, проверка качества (пустые изображения, дубли, слишком мелкие полигоны), полный undo/redo, настраиваемые горячие клавиши, русский и английский интерфейс и совместная работа через общую сетевую папку.

Семантическая кисть: кисть — это класс

Обычная кисть рисует объект: каждая зафиксированная маска — отдельный экземпляр. Для семантической сегментации это неудобно: дорога на снимке — не «пять объектов‑дорог», а одна область, пусть и из нескольких кусков.

В семантическом режиме всё, что нарисовано классом road, сливается в одну область этого класса на изображении. Мазок сохраняется сразу при отпускании кнопки, а каждый пиксель принадлежит не более чем одному классу. Есть три режима:

  • overwrite — мазок забирает пиксели у других классов (закрасили край дороги «тротуаром» — он перешёл к тротуару);

  • keep — рисует только в пустые места, поэтому фон вокруг уже размеченного можно заливать широкой кистью, не боясь задеть соседей;

  • erase — снимает разметку.

Пример работы в программе разметки

Пример работы в программе разметки

Как устроено. Каждый слой — PNG‑маска «изображение + класс». Самое интересное — undo: при каждом мазке пишется новый файл, а старый не трогается. Отмена просто возвращает аннотации ссылку на предыдущий файл — никаких копий битмапов в памяти. Мазок, затронувший сразу несколько классов (например, отобравший пиксели у соседнего), откатывается одной командой — изменения группируются в макрос QUndoStack.

Платить за это приходится лишними файлами, поэтому при открытии проекта работает уборка: файлы, на которые больше нет ссылок, сначала переносятся в _orphaned/ и удаляются только при следующем открытии. Плюс отсрочка в 24 часа — в многопользовательском режиме коллега мог только что нарисовать маску, а его JSON ещё не автосохранился.

Panoptic: объекты и фон в одном проекте

Panoptic‑сегментация объединяет два вида разметки: счётные объекты (things — машины, люди) и аморфные области (stuff — дорога, небо). Каждый пиксель принадлежит ровно одному сегменту: конкретному объекту, области класса‑фона или «пустоте».

У каждого класса есть роль auto / thing / stuff (по умолчанию семантические классы — stuff, остальные — things). Экспорт COCO Panoptic строит карту сегментов: сначала фон, поверх — объекты; area и bbox считаются по видимой части, а объект, полностью закрытый другими, в разметку не попадает. id сегмента кодируется в цвете пикселя (R + 256·G + 256²·B), причём id выбираются рядом с цветом класса, так что PNG можно открыть и проверить глазами.

Результат экспорта в формате COCO Panoptic

Результат экспорта в формате COCO Panoptic

COCO RLE без pycocotools

Маски в COCO можно отдать полигонами или RLE. Полигоны теряют дырки (дорога вокруг люка станет сплошной), RLE — точная попиксельная маска.

Стандартный способ закодировать RLE — pycocotools, но тянуть C‑расширение ради одной функции не хотелось. Формат несложный: маска обходится по столбцам, записываются длины чередующихся серий «фон/объект», каждая длина — как разность с длиной двумя позициями ранее, и упаковывается кусками по 5 бит в печатные символы, начиная с '0':

def _counts_to_string(counts):
    out = []
    for i, x in enumerate(counts):
        if i > 2:
            x -= counts[i - 2]
        more = True
        while more:
            c = x & 0x1F
            x >>= 5                    # арифметический сдвиг, как в C-оригинале
            more = (x != -1) if (c & 0x10) else (x != 0)
            if more:
                c |= 0x20
            out.append(chr(c + 48))
    return "".join(out)

Длины серий считаются векторно в NumPy. Проверка — побайтовое сравнение с pycocotools на 300 случайных масках: результат совпал полностью, а итоговый JSON целиком загружается через pycocotools.coco.COCO — тот же загрузчик, на который опираются Detectron2 и MMDetection.

Какой экспорт для какой модели

Модель

Формат

Ultralytics YOLO (detect / seg / OBB / pose)

соответствующий YOLO‑формат

U‑Net, DeepLab, SegFormer

Semantic Masks, режим Index

Detectron2, MMDetection

COCO Instances + RLE

Mask2Former, OneFormer

COCO Panoptic

Всего форматов 12: YOLO Detect / Segment / OBB / Pose / Point / Classify, COCO Instances / Keypoints / Panoptic, Pascal VOC, LabelMe и PNG‑маски (бинарные, индексные, цветные).

Как устроен проект

  • Внутренний формат — собственный, а не YOLO или COCO: папка .annproj с JSON на каждое изображение и PNG для масок. Координаты нормализованы, поэтому экспорт в любой формат — чистая функция от модели данных.

  • Слои разделены: домен (аннотации и классы) → хранение → экспортёры → инструменты → UI. Экспортёр не знает про Qt, инструмент не знает про форматы.

  • Все изменения идут через команды QUndoStack — undo/redo работает для любого инструмента.

  • Новые инструменты подключаются плагинами: .py‑файл в папке plugins/ — и кнопка появляется в меню.

  • 561 автотест, включая регрессионные на найденные баги.

Установка

  1. Скачать архив релиза: https://github.com/ILYAGRISH/yolo‑annotator/releases/tag/v1.5

  2. Поставить Python 3.10+ (рекомендую 3.13) с галкой «Add python.exe to PATH».

  3. Запустить setup_venv.bat — он создаст окружение и поставит зависимости с проверенными версиями.

  4. Запускать run.bat.

Ограничения и планы

Честно о том, чего пока нет:

  • AI‑авторазметки — следующий большой шаг: SAM и YOLO в отдельном процессе, чтобы тяжёлые зависимости не попадали в основную программу;

  • разметки видео — в планах, через нарезку кадров;

  • проверено только на Windows (PyQt6 кроссплатформенный, но на Linux и macOS я не тестировал).

Ссылки

Каких форматов или функций не хватает в вашей разметке? Пишите в комментариях или в Issues — pull request'ы тоже приветствуются.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.