OpenCV  4.6.0-dev Open Source Computer Vision
Bar code Recognition

Goal

In this chapter,

• We will familiarize with the bar code detection and decoding methods available in OpenCV.

Basics

Bar code is major technique to identify commodity in real life. A common bar code is a pattern of parallel lines arranged by black bars and white bars with vastly different reflectivity. Bar code recognition is to scan the bar code in the horizontal direction to get a string of binary codes composed of bars of different widths and colors, that is, the code information of the bar code. The content of bar code can be decoded by matching with various bar code encoding methods. For current work, we only support EAN13 encoding method.

EAN 13

The EAN-13 bar code is based on the UPC-A standard, which was first implemented in Europe by the International Item Coding Association and later gradually spread worldwide. Most of the common goods in life use EAN-13 barcode.

for more detail see EAN - Wikipedia

BarcodeDetector

Several algorithms were introduced for bar code recognition.

While coding, we firstly need to create a cv::barcode::BarcodeDetector object. It has mainly three member functions, which will be introduced in the following.

Initilization

User can construct BarcodeDetector with super resolution model which should be downloaded automatically to <opencv_build_dir>/downloads/barcode. If not, please download them from https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode

or choose not to use super resolution.

try{
bardet = makePtr<barcode::BarcodeDetector>(sr_prototxt, sr_model);
} catch (const std::exception& e)
{
cout <<
"\n---------------------------------------------------------------\n"
"Failed to initialize super resolution.\n"
"https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode\n"
"and put them into the current directory.\n"
"Or you can leave sr_prototxt and sr_model unspecified.\n"
"---------------------------------------------------------------\n";
cout << e.what() << endl;
return -1;
}

We need create variables to store the outputs.

vector<cv::String> decode_info;
vector<barcode::BarcodeType> decoded_type;
vector<Point> corners;

detect

It is a algorithm based on directional coherence. First of all, we compute the average squared gradients of every pixels. It was proposed in the paper "Systematic methods for the computation of the directional fields and singular points of fingerprints" by A.M. Bazen and S.H. Gerez in 2002. Then we divide the image into some square patches and compute the gradient orientation coherence and mean gradient direction of each patch. At last we connected the patches that have high gradient orientation coherence and similar gradient direction. In this stage, we use multi-scale patches to capture the gradient distribution of multi-size bar codes, and apply non-maximum suppression to filter duplicate proposals. A last, we use minAreaRect() to bound the ROI, and output the corners of the rectangles.

Detect codes in the input image, and output the corners of detected rectangles:

bool result_detection = bardet->detect(input, corners);

decode

This function first super-scales the image if it is smaller than threshold, sharpens the image and then binaries it by OTSU or local-binarization. At last reads the contents of the barcode by matching the similarity of the specified barcode pattern. Only EAN-13 barcode currently supported.

You can find more information in cv::barcode::BarcodeDetector::decode().

detectAndDecode

This function combines detect and decode. A simple example below to use this function showing recognized bar codes.

bool result_detection = bardet->detectAndDecode(input, decode_info, decode_type, corners);

Visualize the results:

static void drawFPS(Mat &color_image, double fps)
{
ostringstream convert;
convert << cv::format("%.2f", fps) << " FPS (" << (g_detectOnly ? " detector" : " decoder") << ")";
putText(color_image, convert.str(), Point(25, 25), FONT_HERSHEY_DUPLEX, 1, Scalar(0, 0, 255), 2);
}
static void drawBarcodeResults(Mat &frame, const vector<Point> &corners, const vector<cv::String> &decode_info,
const vector<cv::barcode::BarcodeType> &decode_type, double fps)
{
if (!corners.empty())
{
for (size_t i = 0; i < corners.size(); i += 4)
{
size_t bar_idx = i / 4;
vector<Point> barcode_contour(corners.begin() + i, corners.begin() + i + 4);
drawBarcodeContour(frame, barcode_contour, g_detectOnly || decode_type[bar_idx] != barcode::NONE);
cout << "BAR[" << bar_idx << "] @ " << Mat(barcode_contour).reshape(2, 1) << ": ";
if (decode_info.size() > bar_idx)
{
if (!decode_info[bar_idx].empty())
{
cout << "TYPE: " << decode_type[bar_idx] << " INFO: " << decode_info[bar_idx] << endl;
}
else
{
cout << "can't decode 1D barcode" << endl;
}
}
else
{
cout << "decode information is not available (disabled)" << endl;
}
}
}
else
{
cout << "Barcode is not detected" << endl;
}
drawFPS(frame, fps);
}

Results

Original Image

Below image shows four EAN 13 bar codes photoed by a smart phone.

image

Result of detectAndDecode

Bar codes are bounded by green box, and decoded numbers are lying on the boxes.

image