上一篇教程: 级联分类器训练
下一篇教程: 支持向量机简介
目标
在本节中,我们将熟悉 OpenCV 中提供的条形码检测和解码方法。
基础
条形码是现实生活中识别商品的主要技术。常见的条形码是通过黑条和白条排列成的平行线图案,黑条和白条具有截然不同的反射率。条形码识别是对条形码进行水平方向扫描,获取由不同宽度和颜色的条形组成的二进制代码串,即条形码的编码信息。条形码的内容可以通过与各种条形码编码方法进行匹配来解码。目前,我们支持 EAN-8、EAN-13、UPC-A 和 UPC-E 标准。
参见 https://en.wikipedia.org/wiki/Universal_Product_Code 和 https://en.wikipedia.org/wiki/International_Article_Number
相关论文:[303] , [144] , [21]
代码示例
主类
条形码识别引入了多种算法。
在编码时,我们首先需要创建一个 cv::barcode::BarcodeDetector 对象。它主要有三个成员函数,将在下面介绍。
初始化
用户可以选择使用超分辨率模型来构造条形码检测器,该模型应从 https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode(sr.caffemodel
、sr.prototxt
)下载。
try
{
app.bardet = makePtr<barcode::BarcodeDetector>(sr_prototxt, sr_model);
}
catch (const std::exception& e)
{
cout <<
"\n---------------------------------------------------------------\n"
"无法初始化超分辨率。\n"
"请从以下地址下载 'sr.*':\n"
"https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode\n"
"并将它们放在当前目录中。\n"
"或者您可以不指定 sr_prototxt 和 sr_model。\n"
"---------------------------------------------------------------\n";
cout << e.what() << endl;
return -1;
}
我们需要创建变量来存储输出。
vector<Point> corners;
vector<string> decode_info;
vector<string> decode_type;
检测
cv::barcode::BarcodeDetector::detect 方法使用基于方向一致性的算法。首先,我们计算每个像素的平均平方梯度,[21] 。然后我们将图像划分为方形块,并计算每个块的梯度方向一致性和平均梯度方向。然后,我们将所有具有高梯度方向一致性和相似梯度方向的块连接起来。在此阶段,我们使用多尺度块来捕获多尺寸条形码的梯度分布,并应用非最大抑制来过滤重复的提议。最后,我们使用 cv::minAreaRect 来绑定 ROI,并输出矩形的角点。
检测输入图像中的代码,并输出检测到的矩形的角点
bardet->detectMulti(frame, corners);
解码
cv::barcode::BarcodeDetector::decode 方法首先(可选)对小于阈值的图像进行超缩放,锐化图像,然后通过 OTSU 或局部二值化将其二值化。然后,它通过匹配指定条形码模式的相似性来读取条形码的内容。
检测和解码
cv::barcode::BarcodeDetector::detectAndDecode 在一个调用中组合了 detect
和 decode
。以下简单示例展示了如何使用此函数
bardet->detectAndDecodeWithType(frame, decode_info, decode_type, corners);
可视化结果
for (size_t i = 0; i < corners.size(); i += 4)
{
const size_t idx = i / 4;
const bool isDecodable = idx < decode_info.size()
&& idx < decode_type.size()
&& !decode_type[idx].empty();
const Scalar lineColor = isDecodable ? greenColor : redColor;
vector<Point> contour(corners.begin() + i, corners.begin() + i + 4);
const vector< vector<Point> > contours {contour};
drawContours(frame, contours, 0, lineColor, 1);
for (size_t j = 0; j < 4; j++)
circle(frame, contour[j], 2, randColor(), -1);
if (isDecodable)
{
ostringstream buf;
buf << "[" << decode_type[idx] << "] " << decode_info[idx];
putText(frame, buf.str(), contour[1], FONT_HERSHEY_COMPLEX, 0.8, yellowColor, 1);
}
}
结果
原始图像
图像
检测后
图像