views:

641

answers:

3

I'm looking for a good open-source library that can find and read a barcode from an image (versus using a barcode scanner). From other questions on Stack Overflow, I've found that ZXing ("Zebra Crossing") is quite good. Though it is made for Java, there is a C# port - however, I believe that it might not be complete. Do you think it is reliable enough to parse a barcode from such a situation, or is some other library better?

EDIT: As Ed pointed out in the comments, I should just try it first. Wow, I did not think of that. :) but I guess my question is whether the partial port is reliable enough - if any of you have used it before, can it scan with proficiency?

A: 

Try compiling the java version with ikvmc, Than access it from your C# code.

zvikara
+2  A: 

I've been using the java version for more than a year, scanning approx 100 daily, and it works great. I see no reason the c# version would be any worse.

Chris
+1  A: 

This depends on what you are using it for, of course. Even the Java version of zxing has some important limitations and performance issues. For example, it can only find one barcode on a page. Also, the algorithms it uses for locating 1-D barcode on the page are not particularly efficient (no idea about the algorithms for 2-D barcodes - that wasn't part of the requirements on the project I was working on). This is all stuff that can be addressed - I started an enhancement a few months ago and was able to significantly improve 1-D location performance and reliability, but our dev priorities have shifted so I haven't worked on it since then.

As for whether the partial port to C# is good, if you want to post back with what the differences are, I'd be happy to comment.

EDIT - here is some of the refactoring that I did:

First, factor out RowNumberStrategy as follows:

public interface RowNumberStrategy {
public int getNextRowNumber();

public class OriginalRowStrategy implements RowNumberStrategy{
    int middle;
    boolean tryHarder = false;
    int rowStep;
    int maxLines;
    int maxRows;

    int x;

    public OriginalRowStrategy(int maxRows, boolean tryHarder) {
        this.x = 0;
        this.maxRows = maxRows;
        this.middle = maxRows >> 1; // divide by 2
        this.tryHarder = tryHarder;
        rowStep = Math.max(1, maxRows >> (tryHarder ? 7 : 4));
        if (tryHarder) {
          maxLines = maxRows; // Look at the whole image, not just the center
        } else {
          maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image
        }
    }

    public int getNextRowNumber() {
        if (x > maxLines)
            return -1;

        int rowStepsAboveOrBelow = (x + 1) >> 1;
        boolean isAbove = (x & 0x01) == 0; // i.e. is x even?
        int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
        if (rowNumber < 0 || rowNumber >= maxRows) {
          // Oops, if we run off the top or bottom, stop
          return -1;
        }

        x = x + 1;

        return rowNumber;
    }

}

public class LinearScanRowStrategy implements RowNumberStrategy{
    private final int maxRows;
    private int currentRow;
    public LinearScanRowStrategy(int totalRows) {
        maxRows = totalRows;
        currentRow = 0;
    }

    public int getNextRowNumber() {
        if (currentRow > maxRows)
            return -1;

        return maxRows - 1 - currentRow++;
    }

}

public class ProgressiveScanRowStrategy implements RowNumberStrategy{
    private final int maxRows;
    private int currentStepSize;
    private int currentStep;

    public ProgressiveScanRowStrategy(int totalRows) {
        maxRows = totalRows;
        currentStep = 0;
        currentStepSize = maxRows;
    }

    public int getNextRowNumber() {
        int nextRow = (currentStep++) * currentStepSize;
        if (nextRow < maxRows)
            return nextRow;

        currentStepSize = currentStepSize >> 1;
        if (currentStepSize <= 0)
            return -1;
        currentStep = 1;

        nextRow = currentStep * currentStepSize;

        return nextRow;
    }

}



}

then the top part of doDecode becomes as follows:

private Result doDecode(MonochromeBitmapSource image, Hashtable hints) throws ReaderException {


int width = image.getWidth();
int height = image.getHeight();
BitArray row = new BitArray(width);
boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
RowNumberStrategy rowProvider = new RowNumberStrategy.ProgressiveScanRowStrategy(height);  

int rowNumber;
while ((rowNumber = rowProvider.getNextRowNumber()) != -1){
...
}

ultimately, this should be something that can be set via a DecodeHintType, but we've found that the progressive strategy is faster than the old strategy in every case we could throw at it (and not just a little faster - much faster).

Kevin Day
Try the MultipleBarcodeReader for finding several barcodes in an image. Not sure what you find inefficient about the 1D detection -- scans a couple lines from the center outwards. If anything the default mode is fast but cursory.
Sean Owen
I'll definitely check out MBR - thanks for the tip. Algorithmically, the scan strategy doesn't work efficiently for large images (say 3300 rows) because it uses a fixed stepping approach. I'll post back with what I'm talking about in a full post so I can show code.
Kevin Day
Nope, the scan step is a function of the height -- by default it skips height/16 rows of the image at each step.
Sean Owen
Sean - yeah, the height/16 approach is a lot slower than it needs to be, and misses barcodes. The above adaptive strategy is much faster, and gets barcodes of all sizes. I'm going to be picking up this work in another month or two - I'll send you a patch.
Kevin Day