Merinorus

Scanning DX Film Edge barcodes

In a previous article, I explained how to manually read the tiny barcode printed on 35mm films that identifies the film manufacturer and type, also called DX Film Edge barcode.

The word manually is important here: counting black and white bars, converting binary to decimal, and looking up the result in a database. I had hundreds of negatives to review: I only did a few before admitting to myself I ought to automate this.

Industrial film scanners and photofinishing machines read these barcodes automatically, because it helps determine the color inversion process for each manufacturer. But I don’t have this kind of machine, as I scan my films with a DSLR. So I wanted to implement it myself, and contribute it to an existing open-source barcode library.

My objective was to:

Looking for existing open-source barcode libraries

I looked at the main open-source barcode libraries. ZXing (pronounced “Zebra crossing”), the Java library, is no longer actively developed, and Google pushes Android developers towards their ML Kit API. ZBar, a C library, is no longer actively developed.

That left ZXing-CPP, originally a C++ port of ZXing but now its own thing, still actively maintained and with broader barcode support than the original. Also, it has wrappers and bindings for a lot of languages: C, Rust, Python, iOS, Kotlin, WebAssembly and more. Lastly, it includes a source code for a demo Android application, so I could easily test on my phone without having to develop an actual app.

Bonus point: It had been a long time since I last wrote C++, so that seemed like a good opportunity. I had no idea if I’d be able to contribute to this codebase, or even compile it and make it run. I didn’t even know if the maintainers would be interested, so I asked them. One of the maintainers, Axxel, kindly gave me some hints to get started.

ZXing-CPP: finding the closest existing barcode algorithm

Since I joined an existing project, I didn’t have to start from a blank page. I found there are basically two categories of barcodes in ZXing-CPP: one-dimensional barcodes and two-dimensional barcodes. Depending on this, the reading algorithm is very different.

One-dimensional barcode (ITF) vs two-dimensional (QR Code)
One-dimensional barcode (ITF) vs two-dimensional (QR Code)

Looking at my DX Film Edge barcodes, I saw them as two-dimensional barcodes, so I guessed I had to look at 2D implementations such as the QRCode. Well, while it’s technically a two-dimensional barcode (either 2×23 bits or 2×31 bits), I was wrong. Axxel showed me that the logic is closer to reading a GS1 databar stacked, which almost consists of a few stacked one-dimensional barcodes:

DX Film Edge barcode vs GS1 databar stacked
DX Film Edge barcode vs GS1 databar stacked

How Zxing-CPP detects 1D barcodes

To read a one-dimensional barcode, the ZXing-CPP algorithm works as follows:

Detecting a one-dimensional barcode with ZXing-CPP
Detecting a one-dimensional barcode with ZXing-CPP

Adapting the detection algorithm for DX Film Edge

To read a stacked barcode such as the DX Film Edge, the algorithm processes two separate one-dimensional barcodes, one after the other. The “trick” is to find the clock signal first as it’s easier, because it’s a fixed pattern. More precisely, there are two possibilities for this fixed pattern, as two types of DX Film Edge barcodes exist:

The algorithm keeps scanning the horizontal lines until it finds a clock signal. If there is one, it then looks for a data signal below the clock.

The algorithm knows which data format to expect: the DX Film Edge format requires blank separators at specific positions, and includes a parity bit for error detection. If any of these checks fail, the signal is discarded as a false positive.

Limitation: the clock is expected to be detected before the data. Therefore, the barcode must be in the bottom-half part of the image to be detected (see this issue).

Two-step detection for DX Film Edge barcode, with ZXing-CPP
Two-step detection for DX Film Edge barcode, with ZXing-CPP

A few weeks later…

After a lot of trial and error, nights of compilation and frustration… the first successful detection. I pointed my phone at an old negative, and suddenly the app recognized the barcode. I felt like a kid in a candy shop!

Bonus: Integration into a mobile application

I first thought of building a mobile app, but I know how much work it is to maintain. However, I already maintain a custom version of the Big Film Database, a website to search for 35mm films by their name or their… DX code. Hah! Thanks to the ZXing-CPP WebAssembly binding, I could easily integrate barcode scanning, so you can scan barcodes directly with your smartphone. Pretty convenient!

Going further

DX Film Edge scanning support was added in Zxing-CPP version 2.3. You’ll find all the wrappers and bindings on the ZXing-CPP source homepage: Python, C, Rust, Go, Android, iOS, .NET, etc. The updated package zxing-cpp is available in major Linux distributions: Debian 13 Trixie, Ubuntu 25.10, Arch, etc. No need to use a dedicated hardware anymore :)

If you’re interested in the source code of this algorithm, you can find it on GitHub. Feel free to contact me if you have any questions. Note it has been improved since by Axxel. Thank you for maintaining this excellent library and for continuously improving the DX Film Edge barcode detection!

Update: this library also allows generating DX Film Edge barcodes, but that will be the object of another article. Stay tuned!

#C++ #Open-Source