Как мы сделали простой офлайн‑разметчик для YOLO и COCO с семантической и panoptic‑сегментацией. COCO.. COCO. panoptic segmentation.. COCO. panoptic segmentation. PyQt6.. COCO. panoptic segmentation. PyQt6. yolo.. COCO. panoptic segmentation. PyQt6. yolo. датасет.. COCO. panoptic segmentation. PyQt6. yolo. датасет. компьютерное зрение.. COCO. panoptic segmentation. PyQt6. yolo. датасет. компьютерное зрение. разметка данных.. COCO. panoptic segmentation. PyQt6. yolo. датасет. компьютерное зрение. разметка данных. сегментация.
Окно для разметки изображений

Окно для разметки изображений

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

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

Если вы обучали свою модель детекции или сегментации, то знаете: самое долгое — не обучение, а разметка. Готовые инструменты делятся на две группы. Серверные (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’ы тоже приветствуются.

Автор: Ilgr

Источник