ZXing: Finding the bounding rectangle of Barcode

mancoolgunda picture mancoolgunda · Feb 4, 2014 · Viewed 8.1k times · Source

I was experimenting with ResultPoints which returns the points related to the barcode in the image.
For a QR Code, ResultPoints returns a set of 4 points which are the coordinates of the four boxes at each corner of the QR Code. When I experimented the same with a Barcode, it returns two points which denote the width of the barcode. How do I find the bounding rectangle of the barcode? Is there any way by which I can calculate the coordinates of the top left corner and bottom right corner of the barcode using the ResultPoints array?

After a bit of research I found the class WhiteRectangleDetector. It was exactly the thing I was interested in but when I started playing around with it, it gave me partial results but not exact results. I have attached an image of the result obtained by using WhiteRectangleDetector but as we can see, it just colors the middle portion of the barcode and not the whole rectangular portion of the barcode. So I was wondering if I could be able to shade the whole rectangular portion of the barcode.

My code:

        barcodeBitmap = (Bitmap)Bitmap.FromFile("barcode-image.png");
        var luminanceSource = new ZXing.BitmapLuminanceSource(barcodeBitmap);
        var binarizer = new ZXing.Common.HybridBinarizer(luminanceSource);
        var bitMatrix = binarizer.BlackMatrix;

        var whiterect = WhiteRectangleDetector.Create(bitMatrix);

        ResultPoint[] whiterectpts = whiterect.detect();
        if (whiterectpts != null)
        {
            Console.WriteLine("\nWhiteRectangleDetector\n");
            foreach (var w in whiterectpts)
            {
                Console.WriteLine(w);
            }
            Rectangle whiterectangle = new Rectangle((int)whiterectpts[0].X, (int)whiterectpts[0].Y, (int)(whiterectpts[2].X - whiterectpts[1].X), (int)(whiterectpts[1].Y - whiterectpts[0].Y));
            img = Image.FromFile("barcode-image.png");
            g = Graphics.FromImage(img);
            g.DrawRectangle(pen, whiterectangle);
            img.Save("crop2.png");
        }

Image after cropping

Answer

Pieter Heemeryck picture Pieter Heemeryck · Jul 9, 2015

Via ResultPoints you have the sides of the barcode. Via WhiteRectangleDetector (WRDet) you have the y coordinate of the top and the height of the barcode. Putting all this information together will get you the exact coordinates!

Breaking it down explicitly:

  • left-top: y value of top from WRDet, x value via the most left ResultPoint
  • right-top: y value of top from WRDet, x value via the most right ResultPoint
  • left-bottom: y value of top from WRDet + height from WRDet, x value via the most left ResultPoint
  • right-bottom: y value of top from WRDet + height from WRDet, x value via the most right ResultPoint

It might seem overkill to call for the ResultPoints and to call the WRDet, but the WRDet algorithm is very fast if the initial search center is inside the barcode. The initial search center can be modified using following constructor:

public WhiteRectangleDetector(BitMatrix image, int initSize, int x, int y)

You know x should lie between the left and right ResultPoint, and for the y value you can choose the y value of one of the ResultPoints.


As an aside, here is the short explanation as to why WhiteRectangleDetector only captures a horizontal fraction. There is an initial rectangle that is expanded along its four sides until no more black points lie on it. The top and bottom are correct, while in a 1D barcode the white bars prevent the algorithm from searching any further.

WhiteRectangleDetector works better for 2D codes (no vertical white bars the entire height of the code), given that you know where to put the initial search center of course.