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:
- Be able to scan barcodes with a smartphone. I have an Android, but iOS and other devices support would definitely be a plus.
- Contribute to a well-maintained project, because I probably won’t be able to maintain a standalone tool in the long term.
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.
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:
How Zxing-CPP detects 1D barcodes
To read a one-dimensional barcode, the ZXing-CPP algorithm works as follows:
- Scan a horizontal line at the center of the image, where the barcode most probably is,
- If no barcode is detected, scan again a line above and a line below,
- Continue to scan top and bottom horizontal lines alternately until a known barcode type is found, or until the top and the bottom of the image are reached.
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 clock with no half-frame number
- The clock with half-frame number, introduced in the 1990s (see this article)
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).
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!