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
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.
I'm looking into this myself, and perhaps you can modify the following parameter in a different constructor to overcome your problem: the initSize parameter in following constructor in the WhiteRectangleDetector.java enlarges the initial search area and could lead to detecting the entire 1D barcode.
public WhiteRectangleDetector(BitMatrix image, int initSize, int x, int y) throws NotFoundException {...}
What I want to ask you: does WhiteRectangleDetector work for 1D and/or for QR codes? Have you tried MonochromeRectangleDetector yet?
For QR codes: After studying the source code of ZXING I've found a simple solution for the problem. The resultsPoints of each qr code contain the following points in the following order:
index 0: bottomLeft
index 1: topLeft
index 2: topRight
So you can for example create your own class with the resultPoints as a constructor parameter to create objects that hold the qr code coordinates within an image. The following code is written in C# as we use Xamarin but the java code would look similar:
public class QRCodeCoordinates
{
public float X1 { get; set; }
public float X2 { get; set; }
public float Y1 { get; set; }
public float Y2 { get; set; }
public QRCodeCoordinates(ZXing.ResultPoint[] resultPoints)
{
this.X1 = resultPoints[0].X; // index 0: bottom left
this.X2 = resultPoints[2].X; // index 2: top right
this.Y1 = resultPoints[2].Y; // index 2: top right
this.Y2 = resultPoints[0].Y; // index 0: bottom left
}
}