How to speed up Datamatrix decoding in Python

But now for something completely different: in my latest project scan.tag.is I’m not dealing with EXIF, IPTC and other “textual” metadata residing in photo file headers, but with metadata consisting of pixels.

scan.tag.is (among other things) tries to decode datamatrix codes embedded in scanned documents in order to process them further based on the decoded information. As scan.tag.is is written in Python, the excellent pydmtx wrapper for libdmtx was the logical choice for this task.


This image displays a tiny part of a photo scan that scan.tag.is will process. The datamatrix code on the left is extended with a textual description and an url. A click on the image gives you a full-res version of the scan which can be used as sample data for this tutorial.

The simple, but slow basics


Using pydmtx is quite straightforward: first you open the to-be-decoded image with the Python Image Library (PIL) to determine its dimensions. Then you create an instance of pydmtx’ DataMatrix class and call its decode()-method with the obtained dimensions and the image data. The extra step of opening the Image through PIL astonished me a bit, but decode() needs the image dimensions as mandatory parameters. Furthermore we will use PIL later to do some preprocessing. The sample script above tries to detect datamatrix codes in an image given as first command line parameter.

If you need a spontaneous coffee break, you can test this simple decoder with the sample scan from above. Likely, the decoder will run for ages and consume about 90% of your system ressources. Not really production-ready…

Performance boost #1: Limit the number of codes to detect

Believe it or not, but with one tiny extra parameter we can radically speed up the decoding process. By default, pydmtx assumes that there are n datamatrix codes hidden in the image and thus scans the image thoroughly not to miss one of them. While pydmtx usually detects the actual datamatrix codes first and quite fast, there might always be “suspicious” areas in an image that loosely resemble a datamatrix code. Checking these potential matches is what slows pydmtx down. Because we know how many damatatrix codes are in the image, we can simply tell pydmtx to stop after the first or n-th successful detection with the max_count parameter:

dm_read = DataMatrix(max_count = 1)
Performance gain: groundbreaking. Detection time droppend from endless (I had to kill my script after 43 minutes) to 2.0 secs

Performance boost #2: restrict the detection time

Seems that we already reached our goal. The pydtmx detection takes less than a second, and we still can fine-tune the process. But what if pydmtx has difficulties with decoding an actually embedded datamatrix code? Then the “endless-loop”-scenario from our first example might come into place again. Luckily, pydtmx has a control parameter for this problem too: by simply setting timeout to a certain number of milliseconds, we restrict the total detection time per image:

dm_read = DataMatrix(max_count = 1, timeout = 1500)
Performance gain: invisible… effectively prevents the “endless loop”-like scenarios from above

Additional tweaks: specify shape, squareness and number of error corrections

Datamatrix codes can be differentiated by their shape (square or rectangular) and their module count (number of black/white “cells” that build the code). The module count also defines the maximum length of the encoded message. To speed up the decoding, we can provide the decode method with the expected shape of the Datamatrix code. The Datamatrix class defines enumerated constants for this purpose, ranging from the most common DmtxSymbolSquareAuto (-3) to DmtxSymbol16x48 (29). Thus, we can specify the expected code type like so:

dm_read = DataMatrix(max_count = 1, timeout = 1500, shape = Datamatrix.DmtxSymbolSquareAuto)

The shape parameter didn’t bring any significant speed gain in my tests. My test objects were scanned images, but if you want to decode photos or even videos, these parameters can become interesting. Panoramic distortion and noise makes decoding more difficult for pydmtx, so a precise configuration can really be valuable. There are a few other parameters such as edge threshold, error corrections or gap size. If you want to tweak’em all, I recommend to check Simon Wood’s pyDatamatrixScanner, an enhanced GUI for pydmtx that can even detect Datamatrix codes in webcam video.

Performance boost #3: Scale down and/or crop the image

You can further speed up the processing time by scaling down the image before sending it to pydmtx. PIL’s thumbnail method seems the ideal choice for this, because it takes care of the scaling ratio and lets you specify the scaling method easily. However, too drastic scaling will decrease the detection rate, so you might have to do some tweaking before you add it to your workflow.

If you know the approximate position of the datamatrix code in the image, you can use PIL’s crop method to remove the non-relevant parts before detection. Here is a sample script that proportionally scales down the input image to 1060 x 1500 pixels and then uses crop to send just the lower third to pydmtx:

These simple tweaks further reduced the total processing time from 1.9 secs to 0.41 secs, that’s a performance gain of over 75%!

Bonus Tip: Increase the detection rate by adding an “artificial” quiet zone

The Datamatrix code specification states a “quiet” zone (actually a white pixel border) around each datamatrix code. Datamatrix codes generated with pydmtx perfectly adhere to this spec, so I was quite surprised about the low code detection rate in my scans. After some research I found the guilty: as I placed my datamatrix code on the very bottom of the scanning glass. my scanner driver ruined this quiet zone by cropping away the seemingly needless pixels on the bottom of the page. As a consequence, the detection rate dropped drastically.

Luckily, PIL came to rescue again: its ImageOps module offers an easy function to draw a white border around an image, which perfectly serves as a “fake quiet zone”. Here is a version of the above script with this enhancement:

But remember: this tip only makes sense if the datamatrix code is located at the border of the image.

Conclusion

pydmtx integrates nicely with Python and is easy to install. However its documentation is lacking, which can cause programmer frustration. I hope that this tutorial eliminated the most common performance pitfalls with pydmtx and encourages more projects to use this fantastic library.

About Franz Buchinger

Digital Photography/Python Aficionado from Vienna, Austria. Tamed his chaotic photo collection with some magic metadata spells. Decided to share his humble EXIF, IPTC and XMP knowledge in this blog.
This entry was posted in Uncategorized. Bookmark the permalink.

Comments are closed.