Hi guys, in this post I developed a simple BarCode Detection application. In order to achieve this, I used Sobel operators and Morhphologic operations.
First, the captured image, in RGB color space, was transformed to the Graycale color model. This is needed as Sobel operators are applied on a grayscaled image.
Sobel operators detect the gradient or “change” in the image. In an image, this changes are identified as the borders of the objects found on an image. Sobel allows the detection horizontal or vertical changes. In this application, the bar code is identified by detecting the lines that conform the bar code, as these lines can be easily using the Sobel operator.
//Convert to grayscale
CvInvoke.CvtColor(capturedFrame, grayscaleFrame, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
/// Gradient X
CvInvoke.Sobel(grayscaleFrame, gradX, Emgu.CV.CvEnum.DepthType.Cv8U, 1, 0);
/// Gradient Y
CvInvoke.Sobel(grayscaleFrame, gradY, Emgu.CV.CvEnum.DepthType.Cv8U, 0, 1);
Once we have obtained the vertical and horizontal gradients from the image, we will scale them to eliminate all negative values and later we will subtract the scaled Y gradient from the X gradient. This subtraction is done to only detect the vertical lines that conform the bar code.
//Abs values of gradient
CvInvoke.ConvertScaleAbs(gradX, absGradX, (double)2, (double)0);
CvInvoke.ConvertScaleAbs(gradY, absGradY, (double)2, (double)0);
//Detection of vertical lines
CvInvoke.Subtract(absGradX, absGradY, fullGrad);
Afterwards a blur operation is applied to the resultant image and a binary threshold is applied to eliminate non-desired features and noise. The binarized image represents the pixels with higher value than the given threshold. In most cases, those pixels are part of the barcode, but it is possible that some pixels represent other objects in the image.
CvInvoke.Blur(fullGrad, bluredFrame, new Size(9, 9), new Point(-1, -1));
CvInvoke.Threshold(bluredFrame, thresholdFrame, 80, 255, Emgu.CV.CvEnum.ThresholdType.Binary);
Now it is time for Morphologic operations! The first operation to be applied will be a closure, using a horizontal rectangular element. Closure operation is composed by a dilation operation followed by an erosion using the same element. This operation fills the holes between the lines of the barcode.
The second and operation performed on the image will be an erosion. The element used is a vertical rectangle. This operation will remove all objects, such as noise, that are not part of the barcode.
CvInvoke.MorphologyEx(thresholdFrame, thresholdFrame, Emgu.CV.CvEnum.MorphOp.Close, verticalRectangle, new Point(-1, -1), 2, Emgu.CV.CvEnum.BorderType.Constant, new MCvScalar((double)0));
CvInvoke.MorphologyEx(thresholdFrame, thresholdFrame, Emgu.CV.CvEnum.MorphOp.Erode, horizontalRectangle, new Point(-1, -1), 1, Emgu.CV.CvEnum.BorderType.Constant, new MCvScalar((double)0));
Once we have joined all lines of the barcode into a big rectangle, we will use the findContours method to analyze the image and obtain all the contours of the areas found in the image. This is the tricky part, as the previous morphologic operations should have processed the lines from the bar code into a big rectangular area.
CvInvoke.FindContours(thresholdFrame, contours, null, Emgu.CV.CvEnum.RetrType.List, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxNone);
Afterwards we are going to select the biggest contour detected and a rectangle that surrounds that area will be drawn. In such area, the bar code will be located. At last, that rectangle will be drawn surrounding this area in the original frame.
//If contours were found on the image,
//Obtain the biggest contour
for (int i = 0; i < contours.Size; i++)
double a = CvInvoke.ContourArea(contours[i]);
if (a > largestArea)
largestArea = a;
indexOfLargestArea = i;
RotatedRect rect = CvInvoke.MinAreaRect(contours[indexOfLargestArea]);
PointF vertixes = rect.GetVertices();
int x = (int)vertixes.X;
int y = (int)vertixes.Y;
//Obtain rectangle that surrounds the detected contour
int width = (int)(vertixes.X – vertixes.X);
int height = (int)(vertixes.Y – vertixes.Y);
Rectangle newRectangle = new Rectangle(x, y, width, height);
//Draw the rectangle that surrounds the detected contour
CvInvoke.Rectangle(capturedFrame, newRectangle, new MCvScalar(255.0, 0.0, 0.0), 10);
Using the application:
You can test the application by placing several products in front of the webcam. If a barcode is detected, the window will display a “Bar code detected” label and a rectangle shall be displayed around the barcode. Otherwise, a “Nothing detected” label is displayed.
Barcodes identified in two products.
Downsides and future improvements:
- As we are using the Sobel operator/gradient approach, the application won’t detect the barcode if it is placed vertical.
- It is possible that the application may consider lines of words as bar code, if those words are displayed vertically.
- This application is not identiying the barcode per se. Instead, the design algorithm enhance vertical lines in order to detect the region where these lines are found in the image. One improvement would be to use these algorithm to segment the area where a barcode is likely to be found, and using that region of interest apply object-recognition techniques to validate there is a barcode and not a false-positive.
As always, you can download the code in my github repository.