?前言: 本篇教程基本集成了之前的教程,实现了扫描二维码功能,虽然还存在部分问题,但也算是个比较完整的程序了
本教程是在?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提示内存不足
下一篇教程会使用微信开源的二维码扫码代码测试下,所说效果不错
其实本教程的示例早就写完了,但因工作关系,一直到现在才完成;同时感觉同事提供的私人平板测试,希望项目经理早点给赏个摄像头和平板
|