# Распознавание маркеров ARUCO

**Ключевые слова:** *Target, opencv, matplotlib, numpy, BFMatcher, hist, approxPolyDP, getPerspectiveTransform, warpPerspective, detectMarkers, opencv-contrib-python*\\

Рассмотрим самый простой способ по отслеживанию и наложению виртуальных объектов. Это метод отслеживания распознавания qr-кодов. Рассмотрим алгоритм, который используется для их распознавания и библиотеку python, которая позволит сделать это автоматически.

Для начала подключим библиотеки opencv, matplotlib и numpy и загрузим изображение.&#x20;

![](https://sun9-37.userapi.com/s/v1/if2/ssKcNT5h9h5_ZaZ88S6zols1oPScOT4IU1lnYOW5SEBEyjTUKqvWhkoAp44O6MTLERc29m26ViY0sIe2WTKGL8GI.jpg?size=810x884\&quality=96\&type=album)\\

Работать будем с qr-кодами которые называются ARUCO. Это специальные маркеры, которые были придуманы как раз для использования в дополненной реальности. Однако их также используют в робототехнике для позиционирования роботов.&#x20;

Какой подход будет использован для того, чтобы распознавать маркеры?

Алгоритм будет состоять из нескольких шагов.&#x20;

1. Бинаризация исходного изображения. Необходимо сделать такой алгоритм, который будет выделять маркеры со всей картинки только по цвету.
2. Выделение периметров (контуров) на изображении, которые бы имели квадратную форму, то есть имели всего четыре угла.
3. Внутри каждого квадрата нужно распознать код.

Начнем с бинаризации. Для того, чтобы бинаризировать изображение, удобно использовать *hist*. Что показывает хистограмма? По координатам Х  — расположены цвета, по координатам У  —  количество пикселей определенного цвета.&#x20;

![](https://sun4-16.userapi.com/s/v1/if2/s1dfOPvlYi4vy-oYrApd1jfGI44qP8OvpcYZ7BqOvqdSwsmnpMdGhpJVKt_-qJunPx46SpZL66qLvhymwBLGfVNg.jpg?size=1112x777\&quality=96\&type=album)\\

Видим, что больше всего серого цвета, однако можно понять, где стол и наша бумага. Для того, чтобы подробнее понять что к чему относится, попробуем отфильтровать это изображение по порогу разных цветов.&#x20;

Создаем новое изображение, такого же размера, как исходное, и далее применим к нему фильтр. Он выбирает координаты только тех пикселей на изображении, чье значение меньше 50.&#x20;

![](https://sun9-82.userapi.com/s/v1/if2/XX4_THFj9r0KkE93-Ak12O61XQOXinv7pGCh2qwP5x8w2NvWRhr7JUxOPTqW4TUbpbpJwqjncDTrXXFVmvdWmp25.jpg?size=828x782\&quality=96\&type=album)\
\
Удалось выделить маркеры, коды на коробке и круги на подушке. \\

Далее необходимо выделить контур. Контуры выделяются поверх бинарного изображения, для этого в ***opencv*** есть специальный метод ***findContours.*** В него передаются бинарные изображения, но так как этот метод на вход принимает именно бинарное изображение, а работа проводится в формате *grayscale,* нужно умножить изображение на 255.

![](https://sun9-31.userapi.com/s/v1/if2/_PE_yG2cItTCZcNfPP10HYRXn7xkBOjS1t_gtGuwcfOD7TtPP3zkKIVVcM-wMRv5hl9WGivoe_NblED5raWC1P4x.jpg?size=1280x526\&quality=96\&type=album)

Реализуем метод ***get\_markers.*** Он выдает только те контура, которые имеют форму квадрата и в которых есть определенные qr-кода. Все остальные контуры нужно отфильтровать. Для этого используется метод ***approxPolyDP***. Он аппроксимирует каждый контур, который он передает под контур квадрата. Убирает лишние точки контура для того, чтобы там оставалось всего четыре. После аппроксимации контура можем попробовать сделать из него идеальный квадрат.&#x20;

Так нужно оставить квадраты определенной величины, в массив контуров *prox* попадают только маркеры. Также необходимо, чтобы метод не только возвращал необходимые нам контуры, но и возвращал сами маркеры в оригинальном виде. Для этого берется изображение внутри каждого контура и убирается перспектива. Сейчас маркеры искажены, поэтому их необходимо привести к виду квадрата. Для этого в самом начале данной функции заданы четыре ключевых точки. Верх, низ, лево, право для оригинального квадрата. С помощью метода ***getPerspectiveTransform*** считается матрица трансформации, то, как трансформировались изображения. Следующим методом ***warpPerspective*** убираем перспективу. В объект ***dst*** попадают маркеры уже виде квадратов.&#x20;

![](https://sun9-10.userapi.com/s/v1/if2/GUZZNsbDhJVj9FcDHuYbv3otsEcQuUtOeek0boMoo5QT8mb3Zj2fUkold-0sC6QPk5SaYCI3UupSJrVsCvlstwLB.jpg?size=1041x888\&quality=96\&type=album)\\

Передаем внутрь бинаризированную картинку.

![](https://sun9-16.userapi.com/s/v1/if2/E1v5Hsk_MRBuDHfRGFTrQrMnvkVSOns5Qnl7Epn9FtmW_glTcWBobsrQnpkgs9qIHYgIT6dK3laSghIONUUFQwgA.jpg?size=573x1080\&quality=96\&type=album)\\

С изображения вырезались как маркеры, так и лишние квадраты. Чтобы этого избежать, нужно увеличить порог по площади. Сделаем его равным 600 и посмотрим на результат. Помимо этого попробуем сразу бинаризировать картинку, для того, чтобы у нас не было разводов рядом с маркером.

![](https://sun9-61.userapi.com/s/v1/if2/Rz4JHckI9t86qUIOB646e1fhkEjMjZgixwRVackxE6y0_6aiG949yCsTpR_3WzMFO81Xzq_EJrkzZllSuJ0BMXzL.jpg?size=734x1080\&quality=96\&type=album)\\

Далее необходимо идентифицировать маркеры. Для этого нужно определить значения каждого бита внутри маркера. Прописывать эту функцию необходимости нет, но рассмотрим каким образом можно перевести изображение 250х250 пикселей к размеру 8х8.

Сгенерируем некоторый массив, в котором будет храниться координата середины площади в изображении 250х250. В ***opencv*** есть специальный метод ***resize,*** помимо того, что с помощью этого метода можно увеличить изображение, его также можно сжать. Возьмем наши картинки маркеров и уменьшим до 8х8, что соответствует количеству битов в изображении. Для того, чтобы не было лишних битов, используем метод ***INTER\_LINEAR.***&#x20;

![](https://sun9-14.userapi.com/s/v1/if2/GPLqVKIpqqo_QjOkNdtpsx8K7IqmX_gd_RljQF--XRYw32haEi_LZnqwdNAat_yVoCJi5jb4dW_s-B3JbMsCSeXX.jpg?size=1280x957\&quality=96\&type=album)\\

Так как языки Python и Aruco достаточно популярны, библиотеки для работы с ними и их распознаванием уже есть. Один из таких моделей, это модуль Aruco. Он уже присутствует в opencv, но для того, чтобы его скачать, необходимо установить специальную версию opencv — ***opencv-contrib-python.***

![](https://sun9-52.userapi.com/s/v1/if2/WMP5gNTxly3DLI2zlwW6ge0HdkxuKTdd3FO1DzXcJj3ULoDU2aRIkWXrPDFU61COGZJy3wKSKi02yMPxkkMf75RD.jpg?size=606x56\&quality=96\&type=album)

&#x20;Подключим уже знакомые нам библиотеки, но не забудем и про Aruco.

![](https://sun9-71.userapi.com/s/v1/if2/2nNIw0lwBa9Q6QKyC7NgH_qi1F_2AnxDLRs8B9G_ZtO4fAVXUo563rm0WA0tVRJjsxHcjlrqaut1zIOA1-GxDb7b.jpg?size=507x157\&quality=96\&type=album)\\

Напишем метод ***find\_aruko,*** на вход которой будет передаваться оригинальная картинка, и выдавать он будет углы всех маркеров на изображениях и их индексы. Переводим изображения в *grayscale*, генерируем aruco\_dict (словарь всех маркеров определенной бинарности, то есть 6х6).

Генерируем параметры нашего словаря, и с помощью метода ***detectMarkers*** передаем изображение, словарь и параметры. С помощью данного метода получим углы и индексы тех маркеров, которые присутствуют на изображении.&#x20;

![](https://sun9-11.userapi.com/s/v1/if2/1xC91ctTazJoNIjJwZflWivILns_rD65WCkLHJJSDzlbUVuTNOehTgtU82TXodUTaO12ka3DHT8nOdUXXES1JqEo.jpg?size=1106x285\&quality=96\&type=album)\\

Откроем изображение с Aruco маркерами и воспользуемся функцией.

![](https://sun9-19.userapi.com/s/v1/if2/9X6EXQbvizQVzNRiLNfeGhqfQb0UEe1J69ThgK_Io_Uk-XwYbebUr4hFzOwhnzT5N5CeOiMDqN8xp_DtTqGcF6T6.jpg?size=865x840\&quality=96\&type=album)

Видим углы квадратов и массив, в котором присутствуют индексы тех маркеров, которые есть на фото. Реализуем специальную функцию ***draw\_aruco*** на вход, которой будем передавать изображение, углы и индексы маркеров. С помощью специальных методов ***cv2.line***, отрисуем линии поверх контуров маркеров, с помощью ***cv2.circle*** отрисуем центр маркеров и с помощью *putText* нарисуем сверху их индексы.

![](https://sun4-16.userapi.com/s/v1/if2/F3AMf_6X-lTrRC86cnT1kVcHa1-SC2A4IXU9tHr_kR_N2UrAOzFBNjRHk7hrbcMT_JAj3TXGExJCe1zwLUC2fXJi.jpg?size=1059x888\&quality=96\&type=album)\\

Видим, что маркеры выделились и сверху подписаны их индексы (Рис. 1).

![](https://sun4-16.userapi.com/s/v1/if2/pVeTRS02nEqlAQ4XBxVWOIhb3l3CJJRxEZCfYQWciYiXDnl5HFsQbeG2h0qTemTP5XX6Syh6m8GuvNXSU3Uku22O.jpg?size=651x600\&quality=96\&type=album)\\

Рис 1. Идентификация маркеров

**Дополнительные источники информации:**

1. [Пакет NumPy](https://youtu.be/eDuuKvIWzew)
2. [Библиотека Matplotlib в Python](https://pythonim.ru/libraries/biblioteka-matplotlib-v-python)
3. [Шпаргалка по Matplotlib](https://proglib.io/p/vasha-povsednevnaya-shpargalka-po-matplotlib-2021-02-04)
4. [Учебник по библиотеке NumPy: учитесь на примерах](https://pythonist.ru/uchebnik-po-biblioteke-numpy-uchites-na-primerah/)
5. [Библиотека Matplotlib](https://devpractice.ru/files/books/python/Matplotlib.book.pdf)

**Задание**: Модифицируйте функцию get\_markers так, чтобы она выделяла только маленькие квадраты (т.е. не выделяла маркеры аруко, а выделяла только бирки на коробке)
