Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ZXing ("Zebra Crossing") in C#

Tags:

java

c#

barcode

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?

like image 540
Maxim Zaslavsky Avatar asked Oct 28 '09 23:10

Maxim Zaslavsky


2 Answers

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.

like image 96
Chris Avatar answered Oct 23 '22 14:10

Chris


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).

like image 36
Kevin Day Avatar answered Oct 23 '22 14:10

Kevin Day