【opencv】示例-barcode.cpp 条形码检测和解码

cpp 复制代码
#include <iostream> // 引入标准输入输出流库
#include "opencv2/objdetect.hpp" // 引入OpenCV物体检测库
#include "opencv2/imgproc.hpp" // 引入OpenCV图像处理库
#include "opencv2/highgui.hpp" // 引入OpenCV高层GUI库


using namespace cv; // 使用OpenCV命名空间
using namespace std; // 使用标准库命名空间


// 预定义一些颜色常量供后续使用
static const Scalar greenColor(0, 255, 0); // 绿色
static const Scalar redColor(0, 0, 255); // 红色
static const Scalar yellowColor(0, 255, 255); // 黄色
// 生成随机颜色的函数
static Scalar randColor()
{
    RNG &rng = theRNG(); // 获取随机数发生器的引用
    return Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); // 生成随机颜色
}


//==============================================================================


// TheApp结构体,其中封装了应用程序的主要逻辑
struct TheApp
{
    Ptr<barcode::BarcodeDetector> bardet; // 指向条码检测器的智能指针
    //! [output]
    vector<Point> corners; // 存储检测到的条码的角点
    vector<string> decode_info; // 存储条码的解码信息
    vector<string> decode_type; // 存储条码的类型
    //! [output]
    bool detectOnly; // 标记是否只进行检测而不解码


    // 清理函数,用于清除存储结果的vector
    void cleanup()
{
        corners.clear();
        decode_info.clear();
        decode_type.clear();
    }


    // 返回当前模式对应的字符串
    inline string modeString() const
{
        return detectOnly ? "<detect>" : "<detectAndDecode>";
    }


    // 绘制检测和解码结果的函数
    void drawResults(Mat &frame) const
{
        //! [visualize]
        for (size_t i = 0; i < corners.size(); i += 4) // 遍历所有角点
        {
            const size_t idx = i / 4; // 计算当前idx
            // 判断当前条码是否被成功解码
            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);
            }
        }
        //! [visualize]
    }


    // 绘制FPS信息的函数
    void drawFPS(Mat &frame, double fps) const
{
        ostringstream buf;
        buf << modeString()
            << " (" << corners.size() / 4 << "/" << decode_type.size() << "/" << decode_info.size() << ") "
            << cv::format("%.2f", fps) << " FPS "; // 组织FPS显示的文本内容
        putText(frame, buf.str(), Point(25, 25), FONT_HERSHEY_COMPLEX, 0.8, redColor, 2); // 将FPS信息绘制到帧上
    }


    // 调用解码函数的中间处理函数
    inline void call_decode(Mat &frame)
{
        cleanup(); // 清理之前的结果
        if (detectOnly)
        {
            //! [detect]
            bardet->detectMulti(frame, corners); // 仅进行检测,不解码
            //! [detect]
        }
        else
        {
            //! [detectAndDecode]
            bardet->detectAndDecodeWithType(frame, decode_info, decode_type, corners); // 检测并解码,获取类型信息
            //! [detectAndDecode]
        }
    }


    // 实时视频流中条码检测的函数
    int liveBarCodeDetect()
{
        VideoCapture cap(0); // 创建视频捕获对象,并打开默认摄像头
        if (!cap.isOpened()) // 如果摄像头没有成功打开
        {
            cout << "Cannot open a camera" << endl; // 输出错误信息
            return 2; // 返回错误码2
        }
        Mat frame; // 创建Mat对象用于存储捕获的帧
        Mat result; // 创建另一个Mat对象用于存放处理结果
        cap >> frame; // 从摄像头读取一帧到frame
        cout << "Image size: " << frame.size() << endl; // 输出帧的尺寸
        // 输出提示信息
        cout << "Press 'd' to switch between <detect> and <detectAndDecode> modes" << endl;
        cout << "Press 'ESC' to exit" << endl;
        for (;;) // 无限循环读取帧并处理
        {
            cap >> frame; // 捕获一帧
            if (frame.empty()) // 如果帧为空,说明视频流结束
            {
                cout << "End of video stream" << endl; // 输出视频流结束信息
                break; // 跳出循环
            }
            if (frame.channels() == 1)
                cvtColor(frame, frame, COLOR_GRAY2BGR); // 如果帧是灰度图像,则转换为BGR
            TickMeter timer; // 创建计时器
            timer.start(); // 开始计时
            call_decode(frame); // 调用解码函数处理当前帧
            timer.stop(); // 停止计时
            drawResults(frame); // 绘制检测结果
            drawFPS(frame, timer.getFPS()); // 显示帧率(FPS)
            imshow("barcode", frame); // 显示带有条码信息的帧
            const char c = (char)waitKey(1); // 等待1ms,并读取用户按键
            if (c == 'd') // 如果按下'd'键
            {
                detectOnly = !detectOnly; // 切换检测模式
                cout << "Mode switched to " << modeString() << endl; // 输出当前模式
            }
            else if (c == 27) // 如果按下'ESC'键(ASCII码为27)
            {
                cout << "'ESC' is pressed. Exiting..." << endl; // 输出退出信息
                break; // 跳出循环终止程序
            }
        }
        return 0; // 正常退出返回0
    }


    // 静态图像中条码检测的函数
    int imageBarCodeDetect(const string &in_file, const string &out_file)
{
        Mat frame = imread(in_file, IMREAD_COLOR); // 读入图像文件
        cout << "Image size: " << frame.size() << endl; // 输出图像尺寸
        cout << "Mode is " << modeString() << endl; // 输出当前模式
        const int count_experiments = 100; // 设置实验次数
        TickMeter timer; // 创建计时器
        for (size_t i = 0; i < count_experiments; i++)
        {
            timer.start(); // 开始计时
            call_decode(frame); // 调用解码函数处理图像
            timer.stop(); // 停止计时
        }
        cout << "FPS: " << timer.getFPS() << endl; // 输出平均帧率(FPS)
        drawResults(frame); // 绘制检测结果
        if (!out_file.empty()) // 如果输出文件名不为空
        {
            cout << "Saving result: " << out_file << endl; // 输出保存文件信息
            imwrite(out_file, frame); // 将结果图像保存到文件
        }
        imshow("barcode", frame); // 显示结果图像
        cout << "Press any key to exit ..." << endl; // 输出退出提示
        waitKey(0); // 等待用户按键退出
        return 0; // 正常退出返回0
    }
};




//==============================================================================


int main(int argc, char **argv)
{
    // 命令行参数定义
    const string keys = "{h help ? |        | print help messages }"
                        "{i in     |        | input image path (also switches to image detection mode) }"
                        "{detect   | false  | detect 1D barcode only (skip decoding) }"
                        "{o out    |        | path to result file (only for single image decode) }"
                        "{sr_prototxt|      | super resolution prototxt path }"
                        "{sr_model |        | super resolution model path }";
    CommandLineParser cmd_parser(argc, argv, keys); // 创建命令行解析对象
    cmd_parser.about("This program detects the 1D barcodes from camera or images using the OpenCV library."); // 程序介绍
    if (cmd_parser.has("help")) // 如果用户请求帮助信息
    {
        cmd_parser.printMessage(); // 打印帮助信息
        return 0; // 并退出程序
    }
    // 提取命令行指定的参数
    const string in_file = cmd_parser.get<string>("in");
    const string out_file = cmd_parser.get<string>("out");
    const string sr_prototxt = cmd_parser.get<string>("sr_prototxt");
    const string sr_model = cmd_parser.get<string>("sr_model");
    if (!cmd_parser.check()) // 检查参数解析是否有误
    {
        cmd_parser.printErrors(); // 打印错误信息
        return -1; // 有误则返回-1
    }


    TheApp app; // 创建应用程序实例
    app.detectOnly = cmd_parser.has("detect") && cmd_parser.get<bool>("detect"); // 设置检测模式
    //! [initialize]
    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; // 初始化失败则返回-1
    }
    //! [initialize]


    // 根据命令行参数执行相应的检测函数
    if (in_file.empty())
        return app.liveBarCodeDetect(); // 如果未提供输入文件则启动实时摄像头检测
    else
        return app.imageBarCodeDetect(in_file, out_file); // 否则进行单帧图像检测
}

bardet->detectAndDecodeWithType(frame, decode_info, decode_type, corners);

相关推荐
前端老宋Running1 分钟前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔1 分钟前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户4445543654263 分钟前
Android的自定义View
前端
WILLF4 分钟前
HTML iframe 标签
前端·javascript
枫,为落叶21 分钟前
Axios使用教程(一)
前端
小章鱼学前端26 分钟前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah27 分钟前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript
流星稍逝29 分钟前
手搓一个简简单单进度条
前端
uup33 分钟前
JavaScript 中 this 指向问题
javascript
倚栏听风雨1 小时前
详解 TypeScript 中,async 和 await
前端