上一个教程 级联分类器训练

下一个教程 支持向量机简介

兼容性 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

相关论文: [288] , [132] , [18]

代码示例

主要类别

条形码识别引入了多种算法。

在编码时,我们首先需要创建一个 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 / 4const 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)}
        }

成果

原始图像:
在这里插入图片描述

检测之后:
在这里插入图片描述

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐