How to detect multiple data matrix codes fast using threading in Python

Based on contributions by Jasmijn.

Decoding a single data matrix code is slow enough that decoding many of them (for example 50 pre-defined regions on one 4K image) one after another can take a noticeable amount of time. This how-to explains the approach: crop each code’s region out of the image first, then decode the regions in parallel with a thread pool instead of one at a time.

What you need

:warning: Check: the original 4K test image (which the author reports gets a 100% decode success rate) was too large to attach to the forum post. Only a downsized FHD version is included in the zip, which the author reports gets roughly 40% success. Decode success rate depends heavily on image resolution — expect worse results if you scale your own images down too far.

Steps

1. Define regions of interest (ROI boxes)

Instead of scanning the whole image for data matrix codes, the script works from a list of pre-defined rectangular regions (“boxes”), each given as (top, bottom, left, right) — the same coordinate convention used for slicing an image, see How to zoom into an image or video frame with OpenCV using array slicing.

For example, (110, 196, 780, 866) means: rows 110–196 (86 px tall), columns 780–866 (86 px wide).

The example project defines 50 such boxes covering different areas of the source image.

2. Crop each box out of the image

For each box, crop the corresponding rectangle out of the full-resolution image using slicing:

crop = image[top:bottom, left:right]

You can optionally display all the crops in a grid first, to visually check that the box coordinates line up with where the codes actually are, before running the decoder.

3. Decode each crop with zxing-cpp

Each crop is scanned individually with the zxing-cpp library, which returns the decoded text if a data matrix code is found in that crop, or nothing if it isn’t.

4. Decode sequentially or in parallel

The decoder supports two modes:

  • Sequential — decode each box one after another. Simpler and uses less CPU, but slower overall. Good for testing that your boxes are correct.
  • Parallel — decode multiple boxes at once using a thread pool. Faster overall, at the cost of more CPU usage. The number of worker threads is configurable (2–16 is a reasonable range; use more if your machine has the cores to spare).
# Normal operation: decode and visualize results
MODE = 1

decode_and_visualize_datamatrices(
    window_size="FHD",                 # Display size: "4K", "FHD", or "QHD"
    enable_parallel=True,              # Use multiple threads (faster)
    num_workers=5,                     # Number of worker threads

    show_all_crops=False,              # True: show all 50 crop previews before decoding
    show_final_result=True,            # True: show result image with decoded data

    print_decoder_results=False,       # True: print timing for each box
    print_detailed_info_results=False, # True: print decoded text per box
    print_successrate=False,           # True: print success-rate statistics
)

5. Find the best worker count for your hardware

A separate benchmark mode sweeps a range of worker counts and reports which one decodes fastest on your machine:

# Benchmark mode
MODE = 2

run_performance_benchmark(
    start_workers=2,             # start testing with 2 workers
    max_workers=16,               # test up to 16 workers
    iterations_per_worker=5,      # run 5 iterations per worker count
    debugging=False,              # True: show crops and results during testing
)

6. Read the results

The result image marks each ROI box:

  • Green outline — exact position of the detected data matrix code inside the box.
  • White box — a code was successfully decoded in this region.
  • Red box — no code was found in this region.

Each successfully decoded box also shows its box number, the decoded text, and the code’s exact position and size.

If print_successrate=True, the script prints a summary like:

Success rate
============
Total boxes processed: 50
Success: 48 (96.0%)
Failed:   2 (4.0%)

Quick reference

Setting Values Purpose
window_size "4K", "FHD", "QHD" Display size for the result image
enable_parallel True, False Use multiple threads
num_workers 2–16 Number of parallel threads
show_all_crops True, False Show all 50 crop previews
show_final_result True, False Show the result image with decoded data
print_decoder_results True, False Print timing info per box
print_detailed_info_results True, False Print decoded text per box
print_successrate True, False Print success statistics at the end

Troubleshooting

  • Low success rate: check the image resolution first. As noted above, decoding a downsized image gives noticeably worse results than the original high-resolution capture.
  • A box consistently shows red: display the crops (show_all_crops=True) and check that the box coordinates actually contain the code — a slightly misaligned ROI is the most common cause of a failed decode.

Related


Rewritten and consolidated (Sept 2026) from the original student how-to’s: How to detect more than 1 datamatrix fast using threading.