条形码识别——4.8.0+新增 OpenCV v4.8.0
本文介绍了使用OpenCV进行条形码检测和解码的方法。主要内容包括:支持的条形码标准(EAN-8/13、UPC-A/E)、BarcodeDetector类的使用方法(初始化、检测、解码),以及检测结果的可视化。重点讲解了detectAndDecodeWithType函数,该函数可同时完成检测和解码,输出条形码内容和类型。文中提供了代码示例展示如何检测图像中的条形码并绘制检测结果,包括绘制检测框、顶
上一个教程 级联分类器训练
下一个教程 支持向量机简介
兼容性 | OpenCV >= 4.8 |
---|
目标
在本章中,我们将熟悉 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
代码示例
主要类别
条形码识别引入了多种算法。
在编码时,我们首先需要创建一个 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"
"Failed to initialize super resolution.\n"
"Please, download 'sr.*' from\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;
}
我们需要创建变量来存储输出。
vector<Point> corners;
vector<string> decode_info;
vector<string> decode_type;
检测
cv::barcode::BarcodeDetector::detect 方法使用基于方向一致性的算法。首先,我们计算每个像素的平均梯度平方 [18] 。然后,我们将图像划分为正方形斑块,并计算每个斑块的梯度方向一致性和平均梯度方向。然后,我们将梯度方向一致性高且梯度方向相似的所有斑块连接起来。在这一阶段,我们使用多尺度补丁来捕捉多尺寸条形码的梯度分布,并应用非最大抑制来过滤重复建议。最后,我们使用 cv::minAreaRect 限定 ROI,并输出矩形的边角。
检测输入图像中的条形码,并输出检测到的矩形边角:
bardet->detectMulti(frame, corners);
解码
cv::barcode::BarcodeDetector::decode 方法首先对小于阈值的图像进行超缩放(可选),锐化图像,然后通过 OTSU 或局部二值化对图像进行二值化。然后,它通过匹配指定条形码模式的相似性来读取条形码的内容。
检测和解码
cv::barcode::BarcodeDetector::detectAndDecode 将检测和解码结合在一次调用中。下面的一个简单示例展示了如何使用该函数:
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 ?
// 绘制条形码矩形
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);
}
}
成果
原始图像:
检测之后:
更多推荐
所有评论(0)