Generating aruco markers in python

Posted on 2017-09-24 in programming

Few weeks ago I suddenly had to deal with aruco markers as a part of an OpenCV AR project.

For those who are not familiar with them, aruco markers are a type of marker used in AR application to indicate where something is.

They often look like this:

Aruco marker #0409
(aruco marker #0409)

The good thing about those markers is that each one of them corresponds to a number within 0..1023 range. Meaning you could use numbers to indicate what some of those markers represent.

The bad thing is... most of the marker generation tutorials concentrate on using OpenCV contrib module. And those modules are normally not built into official precompiled opencv binaries.

Now, normally this is solved by recompiling the library in question from source, but it turned out that OpenCV takes massive amount of compilation time. In my case it took several hours, and since I was in a hurry, in the end I decided to give up on building the library and wrote both detection and marker generation routines myself.

This article deals only with marker generation.

The best available explanation of aruco markers I found is located here.

Long story short, while aruco markers are supposed to be based off hamming codes, which are supposed to allow a good way to recover lost data, in practice recovery capabilities of hamming codes doesn't seem to be used, and in what we have in practice is that one line of an aruco marker represents 2 bits of data, with least significant bits stored at the bottom line.

Here's an example:
Aruco marker #0409

From top to bottom: 0b00, 0b11, 0b10, 0b01, 0b00 (0b00 means 00 bits in binary notation)

I.e. the marker stores code 0b0011100100 or 0b 00 11 10 01 00 - all combination of bits per lines.

Because the marker has only 5 lines, it means there are 10 bits total.

So. After reading that, I wrote a script to generate all the markers. The script is here:

from PIL import Image

arucoCodes = [
    0x10, 0x17, 0x09, 0x0e
]

def makeMarker(code, pixelSize = 1):
    totalSize = 7
    innerSize = 5

    img = Image.new('L', (totalSize, totalSize))

    pixels = list(img.getdata())

    for y in range(0, innerSize):
        rowIndex = innerSize - 1 - y
        rowCode = (code >> (rowIndex * 2)) & 0x3
        rowCode = arucoCodes[rowCode]
        dstY = 1 + y
        for x in range(0, innerSize):
            bitIndex = innerSize - 1 - x

            if ((rowCode >> bitIndex) & 0x1) == 0:
                continue

            dstX = x + 1
            pixelOffset = dstY * totalSize + dstX
            pixels[pixelOffset] = 255

    img.putdata(pixels)

    if pixelSize > 1:
        return img.resize((totalSize*pixelSize, totalSize*pixelSize), Image.NEAREST)
    return img

for code in range(0, 1024):
    makeMarker(code, 16).save("{0:04}.png".format(code))

It requires Python image library (Pillow) to work and should work in python3 as well.

This particular script creates all possible markers in current directory.

Enjoy.