QR扫码综合示例教程(二十)Qt5.15.2+Qt6.2.1(qml)+opencv4.5.4实现扫码功能(多线程)

 前言:
本篇教程基本集成了之前的教程,实现了扫描二维码功能,虽然还存在部分问题,但也算是个比较完整的程序了

本教程是在 Qt5.15.2+Qt6.2.1(qml)拍照捕获视频帧(示例合并) 示例的基础上修改

一、准备openCV库

在windows系统下,openCV官方有预编译库,可以直接下载

笔者使用的是自己编译的版本,兴趣的小伙伴可以去看下

在android系统下,openCV官方也有预编译库 ,可以直接下载

笔者使用的是官方编译库 

一、修改pro工程文件

修改工程文件是为了添加openCV的库

若是使用的VS2019 64位编译器,添加如下代码

win32{
#添加opencv库(VS版)
CONFIG(release, debug|release){
    LIBS += -LOPENCV_DIR/opencv4.5.4/build_vs2019_64/install/x64/vc16/lib/ -lopencv_core454
    LIBS += -LOPENCV_DIR/opencv4.5.4/build_vs2019_64/install/x64/vc16/lib/ -lopencv_imgproc454
    LIBS += -LOPENCV_DIR/opencv4.5.4/build_vs2019_64/install/x64/vc16/lib/ -lopencv_objdetect454
}
else:CONFIG(debug, debug|release){
    LIBS += -LOPENCV_DIR/opencv4.5.4/build_vs2019_64/install/x64/vc16/lib/ -lopencv_core454d
    LIBS += -LOPENCV_DIR/opencv4.5.4/build_vs2019_64/install/x64/vc16/lib/ -lopencv_imgproc454d
    LIBS += -LOPENCV_DIR/opencv/opencv4.5.4/build_vs2019_64/install/x64/vc16/lib/ -lopencv_objdetect454d
}
LIBS += -LOPENCV_DIR/opencv4.5.4/build_vs2019_64/install/x64/vc16/bin/
INCLUDEPATH += OPENCV_DIR/opencv4.5.4/build_vs2019_64/install/include
DEPENDPATH += OPENCV_DIR/opencv4.5.4/build_vs2019_64/install/include
}

#OPENCV_DIR为openCV库存放目录

只要加载自己需要的模块就可以了,以减小发布包的体积

若是使用的android arm64-v8a编译器,添加如下代码

unix:!macx{
#添加opencv库(android arm64-v8a版)
contains(ANDROID_TARGET_ARCH,arm64-v8a) {
    message("arm64-v8a")

ANDROID_OPENCV = opencv_dir/opencv4.5.4/OpenCV-android-sdk/sdk/native

    INCLUDEPATH += $$ANDROID_OPENCV/jni/include/opencv2 \
                   $$ANDROID_OPENCV/jni/include

    LIBS += \
            $$ANDROID_OPENCV/staticlibs/arm64-v8a/libopencv_core.a \
            $$ANDROID_OPENCV/staticlibs/arm64-v8a/libopencv_imgproc.a \
            $$ANDROID_OPENCV/staticlibs/arm64-v8a/libopencv_objdetect.a \
            $$ANDROID_OPENCV/3rdparty/libs/arm64-v8a/libcpufeatures.a \
            $$ANDROID_OPENCV/3rdparty/libs/arm64-v8a/libittnotify.a \
            $$ANDROID_OPENCV/3rdparty/libs/arm64-v8a/libquirc.a \
            $$ANDROID_OPENCV/3rdparty/libs/arm64-v8a/libtegra_hal.a \
            $$ANDROID_OPENCV/libs/arm64-v8a/libopencv_java4.so

}
ANDROID_EXTRA_LIBS = opencv_dir/opencv4.5.4/OpenCV-android-sdk/sdk/native/libs/arm64-v8a/libopencv_java4.so

}

若是使用的android armeabi-v7a编译器,添加如下代码

unix:!macx{
#添加opencv库(android armeabi-v7a版)
contains(ANDROID_TARGET_ARCH,armeabi-v7a) {
    message("armeabi-v7a")

ANDROID_OPENCV = opencv_dir/opencv4.5.4/OpenCV-android-sdk/sdk/native

    INCLUDEPATH += $$ANDROID_OPENCV/jni/include/opencv2 \
                   $$ANDROID_OPENCV/jni/include

    LIBS += \
            $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_core.a \
            $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_imgproc.a \
            $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_objdetect.a \
            $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libcpufeatures.a \
            $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libittnotify.a \
            $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libquirc.a \
            $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libtegra_hal.a \
            $$ANDROID_OPENCV/libs/armeabi-v7a/libopencv_java4.so

}
ANDROID_EXTRA_LIBS = opencv_dir/opencv4.5.4/OpenCV-android-sdk/sdk/native/libs/armeabi-v7a/libopencv_java4.so

}

Android下的openCV库只有静态版本的,添加时注意有先后顺序,不能错。只要添加用到的库就可以。若有小伙伴想使用动态库,则需要自己添加,可以参考笔者的文档:Qt+openCV学习笔记(六)openCV4.5.4+android-ndk-r21-windows-x86_64编译armeabi-v7a动、静态库_aggs1990的专栏-CSDN博客

​Qt+openCV学习笔记(七)openCV4.5.4+android-ndk-r21-windows-x86_64编译arm64-v8a动、静态库​_aggs1990的专栏-CSDN博客

二、添加处理类

可以直接从 QR扫码综合示例教程(九)Qt5.15.2+Qt6.2.1(widget)+opencv4.5.4实现扫码功能(多线程)的示例中复制过来修改
类Tool_Process头文件修改如下
class Tool_Process : public QObject
{
    Q_OBJECT
public:
    explicit Tool_Process(QObject *parent = nullptr);
    ~Tool_Process();

    //添加待处理的图像
    void setImage(const QImage &image, bool isModeMultiQR);
signals:
    //返回已解读的二维码数据
    void decoded(const QStringList resultStringList, const QImage image);

    //启动定时器
    void start();
protected:
    //处理定时器事件
    void timerEvent(QTimerEvent *event) override;
private:
    //二维码解码
    void process();
    //将QImage对象转换为cv::Mat
    cv::Mat QImage2cvMat(QImage image);
    //将cv::Mat对象转换为QImage
    const QImage MatToQImage(cv::Mat mtx);

    //二维码解码并绘制
    double processQRCodeDetection(cv::QRCodeDetector& qrcode, const cv::Mat& input, cv::Mat& result,
                                  std::vector<cv::Point>& corners, bool isModeMultiQR, QStringList &resultStringList);
    //二维码解码
    void runQR(cv::QRCodeDetector& qrcode, const cv::Mat& input,
        std::vector<cv::Point>& corners, std::vector<cv::String>& decode_info,
        bool isModeMultiQR);

    //二维码边框、用时绘制
    void drawQRCodeResults(cv::Mat& frame, const std::vector<cv::Point>& corners, const std::vector<cv::String>& decode_info, double fps);

    //二维码边框绘制
    void drawQRCodeContour(cv::Mat &color_image, const std::vector<cv::Point>& corners);

    //用时绘制
    void drawFPS(cv::Mat &color_image, double fps);


    //启动定时器
    void onStart();

    //保存的图像
    QImage m_image;
    //是否多个二维码
    bool m_isModeMultiQR;
    //定时器Id
    int m_timerId = -1;
    //互斥量
    QMutex m_mutex;
};

源文件修改的关键代码

/*!
 * \brief Tool_Process::process 二维码解码
 */
void Tool_Process::process()
{
    QImage image;
    {
        QMutexLocker locker(&m_mutex);
        if(m_image.isNull())
            return;
        image = m_image;
        m_image = QImage();
    }

    cv::QRCodeDetector qrcode;
    //将QImage转换成cv::Mat
    const cv::Mat input =QImage2cvMat(image);
    //存放绘制后的图像
    cv::Mat result;
    //存放检测到的二维码边框的点
    std::vector<cv::Point> corners;
    //是否要同时检测多个二维码
//    bool isModeMultiQR = true;
    //存放检测到的二维码文本
    QStringList resultStringList;

    //检测二维吗,返回耗时
    double fps = processQRCodeDetection(qrcode, input, result, corners, m_isModeMultiQR, resultStringList);
    std::cout << "FPS: " << fps << std::endl;
    //释放检测结果
    emit decoded(resultStringList, MatToQImage(result).copy());
}

/*!
 * \brief Tool_Process::runQR 二维码解码
 * \param qrcode 解码器
 * \param input 输入图像
 * \param corners 二维码定位点
 * \param decode_info 二维码解码信息
 * \param isModeMultiQR 是否多个二维码
 */
void Tool_Process::runQR(cv::QRCodeDetector &qrcode, const cv::Mat &input, std::vector<cv::Point> &corners, std::vector<cv::String> &decode_info, bool isModeMultiQR)
{
    if (!isModeMultiQR)
    {
        //只检测一个二维码 input为输入图像 corners存放二维码定位点 返回解码后的文件
        cv::String decode_info1 = qrcode.detectAndDecode(input, corners);
        decode_info.push_back(decode_info1);
    }
    else
    {
        //检测多个二维码 input为输入图像 corners存放二维码定位点 decode_info存放解码后的文件
        bool result_detection = qrcode.detectAndDecodeMulti(input, decode_info, corners);
        CV_UNUSED(result_detection);
    }
}

说明:

1.在程序运行时,主线程调用setImage()函数,不停注入新的图像,但处理类只保留最后一张图像

2.process()运行在新开的线程中,被定时触发,若有图像未处理,就处理掉

有关解码处理函数的具体实现方法,修改自openCV官方示例,在此就不再解释

三、修改UI显示

Qt6的界面修改如下

qml修改的关键代码如下

Connections {
        target: imageCapture
        function onErrorOccurred(requestId, error, message) {//发生错误
            console.debug("imageCapture onErrorOccurred",requestId, error, message)
        }
        function onImageCaptured(requestId, previewImage) {//已经捕获到QImage
            console.debug("imageCapture onImageCaptured",requestId, previewImage,imageCapture.preview)
            tool.setCapturedImage(previewImage, checkBox_isMultiQR.checked)

        }
        function onImageSaved(requestId, path) {//已经捕获图片并保存到文件
            console.debug("imageCapture onImageSaved",requestId, path,imageCapture.preview)
        }
    }

    Connections {
        target: tool
        function onDecoded(resultStringList) {//处理解码结果
            console.debug("tool onDecoded", resultStringList)
            //显示解码后的图像
            image2.source = "image://imageProvider/0"
            image2.source = "image://imageProvider/1"

            //显示解码的的文本
            label_result.text = ""
            for(let i = 0; i < resultStringList.length; i ++)
            {
                label_result.text += resultStringList[i] + "\n"
            }
            console.debug("tool onDecoded1111", label_result.text)
        }
    }

Qt5的界面修改如下

 

注意:在qt5中, VideoOutput的autoOrientation属性,要设置为true,否则在某些系统上会显示旋转或倒置或镜像的预览图像,抓取的图像也是异常的

 qml修改的关键代码如下

Connections {
        target: camera.imageCapture
        function onCaptureFailed(requestId, message) {//捕获图像出错
            console.debug("onImageCaptured",requestId,message)
        }

        function onImageCaptured(requestId, preview) {//已捕获图像的url
            // Show the preview in an Image
            console.debug("onImageCaptured",requestId,preview)
            image.source = preview
        }
        function onImageSaved(requestId, path) {//已捕获图像的文件名
            console.debug("onImageSaved",requestId,path)
            tool.setCapturedImageFileName(path, checkBox_isMultiQR.checked)
        }
    }

    Connections {
        target: tool
        function onDecoded(resultStringList) {//处理解码结果
            console.debug("tool onDecoded", resultStringList)
            //显示解码后的图像
            image2.source = "image://imageProvider/0"
            image2.source = "image://imageProvider/1"

            //显示解码的的文本
            label_result.text = ""
            for(let i = 0; i < resultStringList.length; i ++)
            {
                label_result.text += resultStringList[i] + "\n"
            }
        }
    }

修改完成以上代码,就可以运行程序了,以下是笔者的运行结果

1.Qt6.2.1在win10下的运行结果

 2.Qt5.15.2在win10下的运行结果

 3.Qt6.2.1在android下的运行结果

 4.Qt5.15.2在android下的运行结果

 划线的效果不是很明显,小伙伴们可以自己加粗或修改下颜色

本次教程源码下载

后记:

在使用openCV库前,笔者就看到网上有评论说,openCV的识别效果不好。

笔者测试发现,若二维码太小,无法识别;无法识别条码;

若二维码内容多点,直接会导致程序异常退出,查看控制台发现是openCV提示内存不足

下一篇教程会使用微信开源的二维码扫码代码测试下,所说效果不错

其实本教程的示例早就写完了,但因工作关系,一直到现在才完成;同时感觉同事提供的私人平板测试,希望项目经理早点给赏个摄像头和平板

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

꧁白杨树下꧂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值