5.28. QR-коды и AprilTags¶
Рассмотренные до сих пор детекторы – блобов, линий, окружностей, прямоугольников – находят геометрические признаки: положения и контуры, которые интерпретирует последующий этап обработки. Оставшиеся детекторы находят символьные признаки: печатные узоры, чья визуальная структура существует именно для того, чтобы закодировать полезную нагрузку. Камера находит их, декодер считывает биты, и на выходе получается не положение, а строка (или ID), которую намеренно выбрал тот, кто напечатал символ.
В приложениях для малых камер преобладают два таких семейства. QR-коды несут произвольный текст, URL-адреса, визитные карточки или двоичные данные – это обращённые к потребителю двумерные коды, которые встречаются на плакатах, упаковке и посадочных талонах. AprilTags несут единственный числовой ID из небольшого фиксированного набора, быстро декодируются даже на большом расстоянии и (при заданных параметрах объектива) сообщают позу с шестью степенями свободы в кадре камеры – это ориентированные на робототехнику двумерные коды, которыми отмечают дроны, калибровочные мишени и реперные точки. Оба детектора возвращают объекты-результаты с тем же набором понятий ограничивающей рамки, что используют детекторы блобов и прямоугольников, но полезная нагрузка делает их по-настоящему отличными от всего, что было рассмотрено ранее.
5.28.1. QR-коды¶
find_qrcodes() сканирует кадр в поисках QR-кодов и возвращает список объектов-результатов QRCode:
codes = img.find_qrcodes()
for c in codes:
img.draw_rectangle(c.rect, color=(0, 255, 0))
for corner in c.corners:
img.draw_circle((corner[0], corner[1], 4),
color=(0, 255, 0))
print(c.payload)
Детектор принимает единственный необязательный параметр roi для ограничения области поиска. Ему нужен вход в оттенках серого – цветной кадр перед декодированием преобразуется внутренне.
Каждое обнаружение несёт ограничивающую рамку (x, y, w, h, rect), четыре обнаруженных угла (corners – проективный четырёхугольник, который очерчивают поисковые узоры QR-кода) и декодированную полезную нагрузку в виде строки. При аннотировании обнаружения правильно рисовать именно углы – QR-код, наблюдаемый под углом, не выровнен по осям, и ограничивающая рамка даёт лишь приблизительный контур.
Метаданные декодера охватывают всё, что декодер QR-кода узнал по ходу работы. version – это версия QR-кода, от 1 до 40, которая задаёт размер сетки модулей (код версии 1 имеет ширину 21 модуль, код версии 40 – 177). ecc_level – уровень коррекции ошибок (от 0 до 3 для L / M / Q / H); более высокие уровни резервируют больше кодовых слов под коррекцию ошибок и выдерживают большие повреждения ценой меньшего места под полезную нагрузку. mask – это маскирующий узор (от 0 до 7), выбранный кодировщиком для минимизации путаницы декодера. data_type – кодировка, о которой сообщил декодер – числовая, буквенно-цифровая, двоичная или Kanji – а флаги is_numeric / is_alphanumeric / is_binary / is_kanji представляют то же значение в виде более удобных булевых величин.
eci – значение Extended Channel Interpretation, которое определяет текстовую кодировку байтов (UTF-8, ISO-8859-1 и так далее). QR-код с произвольного печатного материала может не гарантировать UTF-8; приложение, которому нужно корректно декодировать байты, проверяет eci и декодирует соответственно. Особый случай – Kanji: MicroPython не разбирает кодировку Kanji, поэтому полезную нагрузку с is_kanji приходится трактовать как массив байтов и декодировать средствами приложения.
Типичное применение: камера считывает QR-коды с конвейера и сообщает декодированную полезную нагрузку хосту. Камера вызывает find_qrcodes() один раз на кадр, перебирает возвращённый список, выбирает коды, у которых data_type соответствует ожиданиям приложения, и пересылает c.payload по UART или USB. Данные ограничивающей рамки и углов полезны для предпросмотра в IDE, но не интересуют хост.
5.28.2. AprilTags¶
find_apriltags() сканирует кадр в поисках AprilTags и возвращает список объектов-результатов AprilTag:
tags = img.find_apriltags(families=image.TAG36H11)
for t in tags:
img.draw_rectangle(t.rect, color=(0, 255, 0))
img.draw_cross(t.cx, t.cy, color=(0, 255, 0))
print(t.id, t.decision_margin)
AprilTags отличаются от QR-кодов целями проектирования. QR-код создан для кодирования произвольных данных в одном плотном символе, который пользователь считывает однократно с близкого расстояния. AprilTag создан для кодирования небольшого ID в разрежённом символе, который камера непрерывно считывает с расстояния, с такой устойчивостью к ошибкам, какую позволяет код Хэмминга его семейства. Компромисс проявляется в обе стороны: QR-код может нести сотни байтов, но требует считывания вблизи; AprilTag несёт лишь несколько сотен уникальных ID, но надёжно считывается с расстояния в метры.
Ключевое слово families принимает битовую маску семейств меток для декодирования. Доступные семейства: image.TAG16H5, image.TAG25H9, image.TAG36H10, image.TAG36H11, image.TAGCIRCLE21H7, image.TAGCIRCLE49H12, image.TAGCUSTOM48H12, image.TAGSTANDARD41H12 и image.TAGSTANDARD52H13. Каждое семейство идёт на компромисс между количеством ID и устойчивостью. Число H в имени – это минимальное расстояние Хэмминга между любыми двумя кодами в семействе, то есть сколько битов должно измениться, прежде чем один корректный код превратится в другой: TAG16H5 имеет 30 ID при расстоянии 5, TAG25H9 – 35 ID при расстоянии 9, а TAG36H11 (используется по умолчанию и наиболее распространён) – 587 ID при расстоянии 11. Детектор исправляет до двух битовых ошибок независимо от семейства, поэтому расстояние определяет, насколько рискованно это исправление: случайный узор в зашумлённом кадре достаточно, чтобы оказался в пределах двух битов от корректного кода, и он будет декодирован как ложное обнаружение, а семейства с большим расстоянием размещают свои коды настолько разрежённее, что такие совпадения становятся редкими – по этой причине TAG36H11 является рекомендуемым выбором. Время обнаружения растёт с числом включённых семейств, поэтому приложение включает только то, что действительно печатает. Когда в одном вызове нужно несколько семейств, битовая маска получается побитовым ИЛИ констант семейств.
Каждое обнаружение несёт набор понятий ограничивающей рамки – x, y, w, h, rect, area, целочисленные и субпиксельные центроиды (cx, cy, cxf, cyf) – и четыре обнаруженных угла (corners). Далее следуют поля идентификации: id – числовой ID внутри семейства (от 0 до 586 для TAG36H11), family – числовая константа семейства, а name – имя семейства в виде строки.
Поля качества совпадения – это то, что приложение использует для фильтрации обнаружений. decision_margin – это оценка достоверности от 0.0 до 1.0; чем выше, тем лучше, и отсеивание обнаружений с decision_margin > 0.1 очищает большинство ложных срабатываний без затрат. hamming подсчитывает битовые ошибки, которые декодер принял для этой метки – чем ниже, тем лучше, а 0 означает идеальное декодирование. goodness – историческая метрика качества изображения, которую текущий декодер больше не вычисляет; она всегда равна 0.0 и может быть проигнорирована.
5.28.3. Поза из параметров¶
Преобразующая возможность find_apriltags(), та, что оправдывает выбор AprilTags в качестве предпочтительной реперной точки в робототехнике, состоит в том, что метод может восстановить позу метки с шестью степенями свободы в кадре камеры непосредственно из обнаруженных углов и небольшого набора калибровочных параметров. Эти параметры – фокусные расстояния камеры по X и Y в пикселях (fx, fy) и оптический центр в пикселях (cx, cy), все четыре из которых приложение однократно измеряет с помощью процедуры калибровки и затем жёстко прописывает в коде.
Когда параметры заданы, возвращаемый объект AprilTag заполняет свои поля x_translation, y_translation, z_translation положением метки относительно камеры, а поля x_rotation, y_rotation, z_rotation (и дублирующее rotation для симметрии) – ориентацией метки. Без параметров все шесть полей равны 0.0, и приложение само отвечает за необходимую ему оценку позы.
Поля смещения сообщаются в ширинах метки: декодер считает метку шириной 1 единица, поэтому приложение умножает каждое смещение на физическую ширину напечатанной метки, чтобы получить метрические расстояния. Метка, напечатанная шириной 100 мм и сообщающая z_translation = 8.3, находится в 830 мм от камеры; та же метка, напечатанная шириной 50 мм на том же расстоянии, сообщила бы z_translation = 16.6. Поля поворота указаны в радианах и не требуют масштабирования.
Оценка позы служит основой для широкого круга робототехнических приложений: стыковки робота с зарядной станцией, отмеченной меткой, движения по напечатанному маршруту из путевых точек, восстановления собственной позы камеры по нескольким известным меткам в окружении. Камера, которая знает параметры, видит метку и имеет реальное положение для этой метки, по той же арифметике имеет и реальное положение для самой себя.
5.28.4. Когда что выбирать¶
QR-коды и AprilTags решают разные задачи. Выбор между ними сводится к тому, что несёт напечатанный символ.
Когда приложению нужно нести произвольные данные через напечатанный символ – URL, строку серийного номера, контактную запись – правильным выбором является QR-код. Сотни байтов умещаются в коде умеренного размера, кодировка общедоступна и поддерживается на каждом смартфоне, а декодер справляется с поворотом, умеренными повреждениями и косыми углами.
Когда приложению нужен небольшой ID, непрерывно считываемый с расстояния, с опциональной позой – реперная точка на движущемся роботе, калибровочная мишень в помещении, стыковочный маркер на зарядной станции – правильным выбором является AprilTag. Сотен ID более чем достаточно для таких сценариев, код Хэмминга восстанавливается после битовых ошибок, которые сорвали бы QR-код, а оценка позы получается бесплатно после калибровки параметров.
Некоторые приложения используют оба варианта: AprilTag отмечает известное место, а связанный с ним QR-код (напечатанный рядом) несёт метаданные о том, что это место означает. Два детектора работают независимо на одном кадре, и приложение сопоставляет их ограничивающие рамки, чтобы сопоставить каждую метку с её сопутствующим кодом.