Hi Christoph,
On Monday, April 8, 2013 8:33:06 PM UTC+2, Christoph Schulz wrote:
Am Montag, 8. April 2013 11:10:27 UTC+2 schrieb Guenther Grau:I started to work on PDF417 improvements a while a go, but was interrupted by other urgent work. Last weekend I finally found the time to at least create a version that can decode all zxing PDF417 test images. Work is far from complete and it's not ready to be committed into the zxing repository yet, but if someone needs support for Compact PDF417 and/or Macro PDF417, it might be a good starting point.
Have you compared your changes to this
PDF417 patch and/or tested these
pdf417-3 files? Just curious.
No, I haven't compared it to the mentioned PDF417 patch. I last looked at the official ZXing java code about one and a half month ago, so I haven't even seen what the latest code looks like (there have been a few pdf417 related commits around end of March). I plan to look at the current code and will also look at your patch to compare ideas and implementations. :-)
I tested my changes against all pdf417 test files. I manages to detect all of them correctly if my binarizer is used. When using the default HybridBinarizer, a few test cases in pdf417-2 fail. I will have a look at these later.
My binarizer is extremely simple and currently only works for greyscale or black and white images. It uses a single black point for the whole image. If no valid barcode can be decoded for a blackpoint, the code simply changes to a blackpoint nearby and tries the detection again. It takes a bit longer, if no barcode can be found, but usually it's very fast. I'll do performance measurements once I'm happy with the detection rate. Btw., it doesn't need to create a black and white copy of the image bitmap. It simply compares the original image pixel with the blackpoint, so the performance is faster for the detection case and slower when doing several blackpoint adjustment iterations.
I might create a separate discussion about the binarizer later, as it requires a few changes in the zxing common code.
It's back port from our C++ changes (not merged into trunk, yet. See my
pdf417 branch). We didn't do much to the decoder (just simplifications). The biggest improvement was achieved by "guessing" codewords using squared error reduction during detection (LinesSampler.java). As a result the decoder gets a pretty perfect barcode, even if the barcode was printed with a needle printer on low ink (I really should submit a test case for that). It's even beating ClearImage in several cases, though it can't deal with barcodes that are cut in half (ec level 8). That's because the detector can't compensate for missing guard patterns or "shifted" barcode rectangles.
My changes initially also focused on "guessing" codewords. I analyzed a lot of barcode images came up with a quite complicated algorithm, which is implemented in PDF417CodewordDecoder.java and AdjustmentResult.java. In addition to this "manual" adjustment of bits, I also implemented a version, which tries more accurate sampling based on floating point width of the black and white bars. (AdjustmentResultFloat.java) This was a big improvement. The final (so far :-) change was to return a second codeword which has the two bits flipped, that are closest to a sampling coordinate between two bars. With this change, all tests can be decoded correctly.
Currently I'm looking into detection problems where black bars are wider than white bars. I have to deal with black and white scans, where the scanner has the "wrong" blackpoint :-(
If I remember correctly, the current code transforms the image to a normalized rectangle, create row sum averages and tries to decode these. I found that the transformation caused a lot of misdetected pixels, so I tried a version which works by scanning the original image line by line. This works fine for all test barcodes. It will fail, however, if the barcode is rotated too much. This is a low priority problem for me, as the barcodes I'm dealing with are at most very slightly rotated.
My code currently does not sum up several rows to get a more reliable value. Instead it counts the number of detected values for a single codeword and uses the one with the highest count that matches the row number. This doesn't work well if the image has a lot of sporadic pixel errors. I already have some ideas on how fix that, but ill look into this later.
When scanning line by line, I found it very important to get the correct start and end position of a codeword right and to know what row the codeword is in (for skewed, rotated and especially damaged images). Initially, I tried calculating the start and end positions while decoding, but this didn't work well. My current solution is twofold. First I remember the start and end positions for all detected codewords, second, I scan column by column. This made detection of codeword start and endpoints very reliable.
To know which row a codeword belongs to is important in case of damaged images or other decoding problems. Initially, I tried to set this information during decoding but found it not reliable enough. Now I simply remember the detected codeword value for each row and do the codeword line numbering when I have the complete "picture" ;-) This implementation is not yet complete, but works reasonably well.
I had a few barcodes where decoding a few values contradicted other decoded information and wasn't sure how to tell which is the correct information. I fixed that by first reading the left row indicator column completely. It contains the important meta data several times which makes it the most reliable source. Using information from that column also allows me to deal with Compact PDF417 and damaged normal PDF417 missing columns on the right. I haven't implemented dealing with a missing left row indicator column yet, but it would be fairly easy to do.
Your approach looks quite similar, without looking at details ("ScanningDecoder" roughly equals "LinesSampler"?). I'm interested in this because our approach is quite heavy performance wise, but it's capable of guessing information that can't be restored by normal error correction.
I haven't done any performance measurements or optimizations yet. Curious about your code now and will look at it :-)
Best regards,
Guenther