OpenCV C++案例实战三《二维码检测》

  • 前言
  • 一、二维码检测
  • 二、二维码识别
    • 1.通过findContours找到轮廓层级关系
  • 三、二维码绘制
  • 四、源码
  • 总结

前言

本文将使用OpenCV C++ 进行二维码检测。

一、二维码检测


首先我们要先将图像进行预处理,通过灰度、滤波、二值化等操作提取出图像轮廓。在这里我还添加了形态学操作,消除噪点,有效将矩形区域连接起来。

 Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);Mat blur;GaussianBlur(gray, blur, Size(3, 3), 0);Mat bin;threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);//通过Size(5,1)开运算消除边缘毛刺Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));Mat open;morphologyEx(bin, open, MORPH_OPEN, kernel);//通过Size(21,1)闭运算能够有效地将矩形区域连接 便于提取矩形区域Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1));Mat close;morphologyEx(open, close, MORPH_CLOSE, kernel1);


如图为经过一系列图像处理之后得到的效果。之后我们需要对该图进行轮廓提取,找到二维码所在的矩形区域。

 //使用RETR_EXTERNAL找到最外轮廓vector<vector<Point>>MaxContours;findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);for (int i = 0; i < MaxContours.size(); i++){Mat mask = Mat::zeros(src.size(), CV_8UC3);mask = Scalar::all(255);double area = contourArea(MaxContours[i]);//通过面积阈值找到二维码所在矩形区域if (area > 6000 && area < 100000){//计算最小外接矩形RotatedRect MaxRect = minAreaRect(MaxContours[i]);//计算最小外接矩形宽高比double ratio = MaxRect.size.width / MaxRect.size.height;if (ratio > 0.8 && ratio < 1.2){Rect MaxBox = MaxRect.boundingRect();//rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2);//将矩形区域从原图抠出来Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));ROI.copyTo(mask(MaxBox));ROI_Rect.push_back(mask);}}}

由以下代码段我们就可以很好的找出二维码所在的矩形区域,并将这些区域抠出来保存以便进行下面的识别工作。

//找到二维码所在的矩形区域
void Find_QR_Rect(Mat src, vector<Mat>&ROI_Rect)
{Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);Mat blur;GaussianBlur(gray, blur, Size(3, 3), 0);Mat bin;threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);//通过Size(5,1)开运算消除边缘毛刺Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));Mat open;morphologyEx(bin, open, MORPH_OPEN, kernel);//通过Size(21,1)闭运算能够有效地将矩形区域连接 便于提取矩形区域Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1));Mat close;morphologyEx(open, close, MORPH_CLOSE, kernel1);//使用RETR_EXTERNAL找到最外轮廓vector<vector<Point>>MaxContours;findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);for (int i = 0; i < MaxContours.size(); i++){Mat mask = Mat::zeros(src.size(), CV_8UC3);mask = Scalar::all(255);double area = contourArea(MaxContours[i]);//通过面积阈值找到二维码所在矩形区域if (area > 6000 && area < 100000){//计算最小外接矩形RotatedRect MaxRect = minAreaRect(MaxContours[i]);//计算最小外接矩形宽高比double ratio = MaxRect.size.width / MaxRect.size.height;if (ratio > 0.8 && ratio < 1.2){Rect MaxBox = MaxRect.boundingRect();//rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2);//将矩形区域从原图抠出来Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));ROI.copyTo(mask(MaxBox));ROI_Rect.push_back(mask);}}}}


如图所示,这是找到的二维码矩形。这里只展示其中之一。

二、二维码识别

1.通过findContours找到轮廓层级关系

 //用于存储检测到的二维码vector<vector<Point>>QR_Rect;//遍历所有找到的矩形区域for (int i = 0; i < ROI_Rect.size(); i++){Mat gray;cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY);Mat bin;threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);//通过hierarchy、RETR_TREE找到轮廓之间的层级关系vector<vector<Point>>contours;vector<Vec4i>hierarchy;findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);//父轮廓索引int ParentIndex = -1;int cn = 0;//用于存储二维码矩形的三个“回”vector<Point>rect_points;for (int i = 0; i < contours.size(); i++){//hierarchy[i][2] != -1 表示该轮廓有子轮廓  cn用于计数“回”中第几个轮廓if (hierarchy[i][2] != -1 && cn == 0){ParentIndex = i;cn++;}else if (hierarchy[i][2] != -1 && cn == 1){cn++;}else if (hierarchy[i][2] == -1){//初始化ParentIndex = -1;cn = 0;}//如果该轮廓存在子轮廓,且有2级子轮廓则认定找到‘回’if (hierarchy[i][2] != -1 && cn == 2){drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1);RotatedRect rect;rect = minAreaRect(contours[ParentIndex]);rect_points.push_back(rect.center);}}}

以上代码段的整体思路为:首先经过图像预处理进行轮廓检测,
通过hierarchy、RETR_TREE找到轮廓之间的层级关系。根据hierarchy[i][2]是否为-1判断该轮廓是否有子轮廓。若该轮廓存在子轮廓,则统计有几个子轮廓。如果该轮廓存在子轮廓,且有2级子轮廓则认定找到‘回’ 。关于轮廓的层级关系,大家可以自行百度查找资料,理解一下其中原理。

//对找到的矩形区域进行识别是否为二维码
int Dectect_QR_Rect(Mat src,Mat &canvas,vector<Mat>&ROI_Rect)
{//用于存储检测到的二维码vector<vector<Point>>QR_Rect;//遍历所有找到的矩形区域for (int i = 0; i < ROI_Rect.size(); i++){Mat gray;cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY);Mat bin;threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);//通过hierarchy、RETR_TREE找到轮廓之间的层级关系vector<vector<Point>>contours;vector<Vec4i>hierarchy;findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);//父轮廓索引int ParentIndex = -1;int cn = 0;//用于存储二维码矩形的三个“回”vector<Point>rect_points;for (int i = 0; i < contours.size(); i++){//hierarchy[i][2] != -1 表示该轮廓有子轮廓  cn用于计数“回”中第几个轮廓if (hierarchy[i][2] != -1 && cn == 0){ParentIndex = i;cn++;}else if (hierarchy[i][2] != -1 && cn == 1){cn++;}else if (hierarchy[i][2] == -1){//初始化ParentIndex = -1;cn = 0;}//如果该轮廓存在子轮廓,且有2级子轮廓则认定找到‘回’if (hierarchy[i][2] != -1 && cn == 2){drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1);RotatedRect rect;rect = minAreaRect(contours[ParentIndex]);rect_points.push_back(rect.center);}}//将找到地‘回’连接起来for (int i = 0; i < rect_points.size(); i++){line(canvas, rect_points[i], rect_points[(i + 1) % rect_points.size()], Scalar::all(255), 5);}QR_Rect.push_back(rect_points);}return QR_Rect.size();}

由以上代码段,我们就可以识别出二维码。效果如图所示。

三、二维码绘制

 //框出二维码所在位置Mat gray;cvtColor(canvas, gray, COLOR_BGR2GRAY);vector<vector<Point>>contours;findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);Point2f points[4];for (int i = 0; i < contours.size(); i++){RotatedRect rect = minAreaRect(contours[i]);rect.points(points);for (int j = 0; j < 4; j++){line(src, points[j], points[(j + 1) % 4], Scalar(0, 255, 0), 2);}}


最终效果如图所示。


四、源码

#include<iostream>
#include<opencv2/core.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/highgui.hpp>
using namespace std;
using namespace cv;//找到二维码所在的矩形区域
void Find_QR_Rect(Mat src, vector<Mat>&ROI_Rect)
{Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);Mat blur;GaussianBlur(gray, blur, Size(3, 3), 0);Mat bin;threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);//通过Size(5,1)开运算消除边缘毛刺Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));Mat open;morphologyEx(bin, open, MORPH_OPEN, kernel);//通过Size(21,1)闭运算能够有效地将矩形区域连接 便于提取矩形区域Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1));Mat close;morphologyEx(open, close, MORPH_CLOSE, kernel1);//使用RETR_EXTERNAL找到最外轮廓vector<vector<Point>>MaxContours;findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);for (int i = 0; i < MaxContours.size(); i++){Mat mask = Mat::zeros(src.size(), CV_8UC3);mask = Scalar::all(255);double area = contourArea(MaxContours[i]);//通过面积阈值找到二维码所在矩形区域if (area > 6000 && area < 100000){//计算最小外接矩形RotatedRect MaxRect = minAreaRect(MaxContours[i]);//计算最小外接矩形宽高比double ratio = MaxRect.size.width / MaxRect.size.height;if (ratio > 0.8 && ratio < 1.2){Rect MaxBox = MaxRect.boundingRect();//rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2);//将矩形区域从原图抠出来Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));ROI.copyTo(mask(MaxBox));ROI_Rect.push_back(mask);}}}}//对找到的矩形区域进行识别是否为二维码
int Dectect_QR_Rect(Mat src,Mat &canvas,vector<Mat>&ROI_Rect)
{//用于存储检测到的二维码vector<vector<Point>>QR_Rect;//遍历所有找到的矩形区域for (int i = 0; i < ROI_Rect.size(); i++){Mat gray;cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY);Mat bin;threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU);//通过hierarchy、RETR_TREE找到轮廓之间的层级关系vector<vector<Point>>contours;vector<Vec4i>hierarchy;findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);//父轮廓索引int ParentIndex = -1;int cn = 0;//用于存储二维码矩形的三个“回”vector<Point>rect_points;for (int i = 0; i < contours.size(); i++){//hierarchy[i][2] != -1 表示该轮廓有子轮廓  cn用于计数“回”中第几个轮廓if (hierarchy[i][2] != -1 && cn == 0){ParentIndex = i;cn++;}else if (hierarchy[i][2] != -1 && cn == 1){cn++;}else if (hierarchy[i][2] == -1){//初始化ParentIndex = -1;cn = 0;}//如果该轮廓存在子轮廓,且有2级子轮廓则认定找到‘回’if (hierarchy[i][2] != -1 && cn == 2){drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1);RotatedRect rect;rect = minAreaRect(contours[ParentIndex]);rect_points.push_back(rect.center);}}//将找到地‘回’连接起来for (int i = 0; i < rect_points.size(); i++){line(canvas, rect_points[i], rect_points[(i + 1) % rect_points.size()], Scalar::all(255), 5);}QR_Rect.push_back(rect_points);}return QR_Rect.size();}int main()
{Mat src = imread("6.png");if (src.empty()){cout << "No image data!" << endl;system("pause");return 0;}vector<Mat>ROI_Rect;Find_QR_Rect(src, ROI_Rect);Mat canvas = Mat::zeros(src.size(), src.type());int flag = Dectect_QR_Rect(src, canvas, ROI_Rect);//imshow("canvas", canvas);if (flag <= 0){cout << "Can not detect QR code!" << endl;    system("pause");return 0;}cout << "检测到" << flag << "个二维码。" << endl;//框出二维码所在位置Mat gray;cvtColor(canvas, gray, COLOR_BGR2GRAY);vector<vector<Point>>contours;findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);Point2f points[4];for (int i = 0; i < contours.size(); i++){RotatedRect rect = minAreaRect(contours[i]);rect.points(points);for (int j = 0; j < 4; j++){line(src, points[j], points[(j + 1) % 4], Scalar(0, 255, 0), 2);}}imshow("source", src);waitKey(0);destroyAllWindows();system("pause");return 0;
}

总结

本文使用OpenCV C++进行二维码检测,关键步骤有以下几点。
1、图像预处理,筛选出二维码所在的矩形区域,并将该区域抠出来进行后续的识别工作。
2、对筛选出的矩形区域进行轮廓检测,判断它们之前的层级关系,以此来识别二维码。
3、最后根据检测到的二维码“回”字,将其绘制出来就可以了。

以上就是我对二维码检测识别的整体思路,欢迎大家一起交流学习。若存在不足的地方欢迎大家指正。

OpenCV C++案例实战三《二维码检测》相关推荐

  1. Jetson Nano 从入门到实战(案例:Opencv配置、人脸检测、二维码检测)

    目录 1. Jetson Nano简介 2. Jetson Nano环境配置 2.1 开箱配件介绍 2.2 烧录系统 2.3 开机和基本设置 2.4 开发环境配置 2.4.1 更新源和软件 2.4.2 ...

  2. Jetson Nano 从入门到实战(转载)(案例:Opencv配置、人脸检测、二维码检测)

    目录 1. Jetson Nano简介 2. Jetson Nano环境配置 2.1 开箱配件介绍 2.2 烧录系统 2.3 开机和基本设置 2.4 开发环境配置 2.4.1 更新源和软件 2.4.2 ...

  3. OpenCV C++案例实战三十二《字符识别》

    OpenCV C++案例实战三十二<字符识别> 前言 一.结果演示 二.制作数据集 三.字符识别 四.源码 总结 前言 本案例将使用OpenCV C++ 进行字符识别.主要包括制作数据集. ...

  4. OpenCV C++案例实战十二《图像全景拼接》

    OpenCV C++案例实战十二<图像全景拼接> 前言 一.OpenCV Stitcher 1.功能源码 2.效果 二.图像全景拼接 1.特征检测 2.计算单应性矩阵 3.透视变换 4.图 ...

  5. flutter开发实战-flutter二维码条形码扫一扫功能实现

    flutter开发实战-flutter二维码条形码扫一扫功能实现 flutter开发实战-flutter二维码扫一扫功能实现,要使用到摄像头的原生的功能,使用的是插件:scan 效果图如下 一.扫一扫 ...

  6. python opencv 条形码及二维码检测识别

    目录 条形码检测识别 二维码检测识别 基于python opencv pyzbar 实现. 条形码检测识别 原图: 最后截取图: 直接上代码: import cv2 import numpy as n ...

  7. opencv contrib模块 示例 WeChat qrcode微信二维码检测解码

    在opencv 4.5.3 的 contrib中, 腾讯WeChatCV团队贡献了wechat_qrcode模块,3行代码即可在opencv中实现微信的扫码功能. 文档链接为https://docs. ...

  8. 餐饮二维码案例: 扫二维码监督后厨卫生状况

    清华大学中国经济社会数据研究中心与腾讯联合发布的<2020码上经济战疫报告>显示,2020年第一季度,"码"上经济交易额同比增加25.86%,1月23日至5月6日,政企 ...

  9. Opencv之微信二维码检测与解析

    简介 微信二维码检测器是由微信计算机视觉团队(WeChatCV)提供的一个高性能.轻量级的二维码检测与解码库.它已广泛应用于腾讯的各种应用,包括微信.微信.QQ.QQ浏览器等.微信二维码检测仪有四个主 ...

最新文章

  1. Openstack 实战讲解之-----06-计算节点配置
  2. pytorch CUDA out of memor
  3. linux iptables:安全应用,防火墙
  4. 独占设备的分配和回收模拟_系统设计硬核知识(4)——操作系统的设备管理...
  5. 【大型网站技术实践】初级篇:搭建MySQL主从复制经典架构
  6. Error while retrieving the leader gateway. Retrying to connect to akka.tcp://flink@laptop:xxxx
  7. 接口(定义,使用方法)
  8. 三维网格精简算法java版_几种常见算法的精简版-
  9. 人生路上对我影响最大的三位老师
  10. 操作系统(6)-协程
  11. 做程序员的老婆应该注意的一些事情
  12. rmarkdown+flexdashboard制作dashboard原型
  13. sql语句分析是否走索引_Mysql中SQL语句不使用索引的情况
  14. [ 安装 ] Hadoop安装步骤!
  15. 【深度学习】FPN 网络 Feature Pyramid Networks
  16. html超链接 鼠标效果,7种鼠标滑过超链接动画特效
  17. ros系统能用c语言编程,ROS操作系统学习(十一)参数的使用与编程方法
  18. 谷歌地图网页版_安卓版谷歌地图新增专用的街景图层
  19. 【小程序】小程序开发工具的主要环境设置
  20. Gitlab权限说明

热门文章

  1. CSSCSS3基础教程
  2. android studio内容提供者(查看短信的猫)点击查看短信,闪退,无法显示短信内容。(附程序源代码,以及解决办法)
  3. C#中的NewLine到底是什么字符?
  4. 新电脑如何分盘---傻瓜式教学
  5. jav中jar包的MANIFEST.MF
  6. android 自定义view实现仿QQ运动步数进度效果
  7. 游戏画质提升1《X战警金刚狼前传》画质增强修改
  8. Developing DataBase Applications Using MySQL Connector/C++ 中文文本
  9. JVM类加载、验证、准备、解析、初始化、卸载过程详解
  10. 马化腾动怒!微信数据“被共享”,山寨微信团伙被判一年!