RegionScanPixel appears to have incorrect cacheIndex test
Status: Beta
Brought to you by:
michivo
In DmtxDecode, in method RegionScanPixel, at line 691:
int cacheIndex = this.DecodeGetCache(loc.X, loc.Y);
if (cacheIndex == -1)
return null;
if (this._cache[cacheIndex] != 0x00)
return null;
is checking to see if any flag is set. This is inconsistent with the code in GetStrongestNeighbor, at line 789:
int cacheIndex = DecodeGetCache(loc.X, loc.Y);
if (cacheIndex == DmtxConstants.DmtxUndefined)
{
continue;
}
if ((this._cache[cacheIndex] & 0x80) != 0x00)
which checks for the 0x80 bit being set.
The original libdmtx library has in dmtxregion.c in method dmtxRegionScanPixel at line 110:
cache = dmtxDecodeGetCache(dec, loc.X, loc.Y);
if(cache == NULL)
return NULL;
if((int)(*cache & 0x80) != 0x00)
return NULL;
which also is checking for just the 0x80 bit being set,
I am noticing that the current test
if (this._cache[cacheIndex] != 0x00)instead ofif ((this._cache[cacheIndex] & 0x80) != 0x00)runs 60 times faster. It is so slow using the original libdmtx that it makes me speculative it may be a intentional optimization: any value indicates a "visited" aspect, if not a full "visited", and it is appropriate to skip the grid location without testing it further.Further analysis:
The original libdmtx library is correct and the code in datamatrix.net is incorrect. The code should check for the 0x80 (visited) bit. The visited flag is set in TrailBlazeContinuous (via MatrixRegionSeekEdge), TrailBlazeGapped (MatrixRegionOrientation), or when successfully
decoding a found barcode (MatrixRegion call into CacheFillQuad). It is cleared in TrailBlazeContinuous. If no regions/barcodes are found, this test will never be true as the visited bit is reset by TrailClear. However, if returned regions are verified as barcodes (via MatrixRegion()) then the visited bit will be set and the area will not be checked again - we already found a barcode in the region, and not checking anything in that region is a useful performance optimization.
Checking for anything set (the current datamatrix.net) on first glance appears to make sense, as why check a pixel that has already been assigned? It is not unusual for the cache to be set when checking neighbors, but that does not mean that those neighbors were ever checked directly as potential barcode pixels.
The net effect of the bug is that the code runs faster but will often miss a small barcode . When used with a hand-held scanner this is not noticed, as multiple scans are often performed with slight pixel deviations. Shifting the image by one bit is enough that a pixel will be detected directly that otherwise would be masked by being a neighbor to a checked pixel.
Fixing the bug, however, does introduce extremely slow performance when scanning a page for a very small barcode. I have found other custom optimizations are necessary.