Recognizing Barcodes: Take 3

Last week I spent some time working on code to recognize an EAN13 barcode from a photograph. You can read the earlier blogs on my work here and here.

After improving the algorithms for determining whether a particular pixel was part of a bar or a space, I was still having trouble with decoding one of my barcode pictures. Considering I had only tried two barcodes, I’d say that a 50% failure rate isn’t very good. So the question now was “how do I improve recognition?”

Stepping through the code, I saw where the failure was occurring. My code was determining a “bit width” based on the EAN13’s start delimiter of bar-space-bar, then stepping across the image at that interval. I had originally tried to add some “intelligence” to the code to have the sample point shift left or right on the pixel line if it was close to a state transition. While it was an interesting idea and improved decoding of the first barcode, it still wasn’t doing much on this second barcode.

It then occurred to me that I was being a bit of an idiot. EAN13 is a fixed-width code, so it always contains the exact same number of bits, so if I just found the start and the end, I could divide the total width by the number of bits in the code to get a better bit-width. Even better, rather than sampling one pixel per bit, why not just take the average luminance across the entire width and then check that *average* against the threshold? I updated the code and sure enough, I was able to decode the second barcode. Huzzah, success!

So I’d say that now I have at least a rudimentary library for recognizing and decoding an EAN13 barcode, but it still lacks one critical piece (and several things that would be nice-to-haves). When you want to recognize a barcode, you have to provide a Y position across which the luminance line is extracted and you have to provide a “threshold” value which the library uses to determine the value of a bit. A real consumer of this library isn’t going to want to have to provide this information.

Sure, for the Y position we can make an assumption that a horizontal line across the center of the image will be on the barcode (and that’s what I’m doing now) but are there potentially better places to sample? Would we get better results if we did some sort of vertical averaging? And what about the threshold? The two barcodes I was working with had fairly different threshold values that successfully returned a result.

One of the next steps would be to add code to find the barcode bounds. I’m already finding the left and right, which is simple, but finding the top and bottom would be a bit more of a challenge. Also I’d need a way to efficiently determine a “best” threshold. Those will be the tasks for this week (I’ve already done some promising work on bounds finding, which I’ll report in the next blog entry).