C++ OpenCV实现二维码检测功能

目录
  • 前言
  • 一、二维码检测
  • 二、二维码识别
  • 三、二维码绘制
  • 四、源码
  • 总结

前言

本文将使用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);

			}

		}

	}

}

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

二、二维码识别

通过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、最后根据检测到的二维码“回”字,将其绘制出来就可以了。

以上就是C++ OpenCV实现二维码检测功能的详细内容,更多关于C++ OpenCV二维码检测的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java使用opencv识别二维码的完整步骤

    目录 前言 OpenCV的安装 开发环境配置 识别二维码 总结 前言 OpenCV的全称是Open Source Computer Vision Library,它是一个跨平台的计算机视觉库,由英特尔公司发起并参与开发,以BSD许可证授权发行,可以在商业和研究领域中免费使用.OpenCV本身由C++语言开发,但也具有各种其他语言的接口,并支持windows.linux.ios.android等各种操作系统.本文将介绍如何使用OpenCV与Java语言进行二维码识别. OpenCV的安装 目前Op

  • Android基于OpenCV实现QR二维码检测

    目录 QR二维码 QR二维码格式 QR二维码结构 API QRCodeDetector类结构 检测QR二维码 识别QR二维码 检测并识别QR二维码 操作 结果 源码 QR二维码 QR码(英语:Quick Response Code:全称为快速响应矩阵图码)是二维码的一种,于1994年由日本DENSO WAVE公司发明.QR来自英文Quick Response的缩写,即快速反应,因为发明者希望QR码可以快速解码其内容.QR码使用四种标准化编码模式(数字.字母数字.字节(二进制)和日文(Shift_

  • Android调用OpenCV2.4.10实现二维码区域定位

    Android上使调用OpenCV 2.4.10 实现二维码区域定位(Z-xing 码),该文章主要用于笔者自己学习中的总结,暂贴出代码部分,待以后有时间再补充算法的详细细节. Activity class Java 文件 package cn.hjq.android_capture; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOExce

  • c++ 基于opencv 识别、定位二维码

    前言 因工作需要,需要定位图片中的二维码:我遂查阅了相关资料,也学习了opencv开源库.通过一番努力,终于很好的实现了二维码定位.本文将讲解如何使用opencv定位二维码. 定位二维码不仅仅是为了识别二维码:还可以通过二维码对图像进行水平纠正以及相邻区域定位.定位二维码,不仅需要图像处理相关知识,还需要分析二维码的特性,本文先从二维码的特性讲起. 1 二维码特性 二维码在设计之初就考虑到了识别问题,所以二维码有一些特征是非常明显的. 二维码有三个"回""字形图案,这一点非常

  • 使用python写的opencv实时监测和解析二维码和条形码

    今天,我实现了一个很有趣的demo,它可以在视频里找到并解析二维码,然后把解析的内容实时在屏幕上显示出来. 然后我们直入主题,首先你得确保你装了opencv,python,zbar等环境.然后这个教程对于学过opencv的人可能更好理解,但是没学过也无妨,到时候也可以直接用. 比如我的电脑上的环境是opencv2.4.x,python2.7,和最新的zbar,在Ubuntu 12.12的系统下运行的 假设你的opencv已经安装好了,那么我们就可以安装zbar 你可以先更新一下 sudo apt

  • C++ OpenCV实现二维码检测功能

    目录 前言 一.二维码检测 二.二维码识别 三.二维码绘制 四.源码 总结 前言 本文将使用OpenCV C++ 进行二维码检测. 一.二维码检测 首先我们要先将图像进行预处理,通过灰度.滤波.二值化等操作提取出图像轮廓.在这里我还添加了形态学操作,消除噪点,有效将矩形区域连接起来. Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); Mat blur; GaussianBlur(gray, blur, Size(3, 3), 0); Mat bin;

  • Android开发实现模仿360二维码扫描功能实例详解

    本文实例讲述了Android开发实现模仿360二维码扫描功能的方法.分享给大家供大家参考,具体如下: 一.效果图: 二.框架搭建 1.首先,下载最新zxing开源项目. 下载地址:http://code.google.com/p/zxing/ 或 点击此处本站下载. 2.分析项目结构,明确扫描框架需求.在zxing中,有很多其他的功能,项目结构比较复杂:针对二维码QRCode扫描,我们需要几个包: (1)com.google.zxing.client.android.Camera 基于Camer

  • Android实现基于ZXing快速集成二维码扫描功能

    二维码扫描现在是一直比较多的应用场景,android的开源项目ZXing提供了完整.成熟的解决方案,如果仅仅是出于快速开发的目的,可以根据自己的项目需要,把ZXing官方提供的项目稍加裁剪,就可以快速的集成到自己的项目中.下面详细演示和介绍如何实现基于ZXing官方提供的源码,快速集成二维码扫描功能到自己项目中的解决方案. (第1步):到ZXing官方主页下载最新的项目代码包,ZXing在github的官方主页:https://github.com/zxing,下载后解压.解压后根目录下有若干项

  • Laravel使用PHPQRCODE实现生成带有LOGO的二维码图片功能示例

    本文实例讲述了Laravel使用PHPQRCODE实现生成带有LOGO的二维码图片功能.分享给大家供大家参考,具体如下: /** * 利用phpqrcode来实现生成带有logo的二维码图片 */ public function getQrCode(Request $request){ $type = $request->input('type');//传递的类型 ios | android require_once(dirname(__FILE__) . '/phpqrcode/' . 'ph

  • 基于barcodescanner实现Android二维码扫描功能

    二维码扫描现在成为一种非常常见的APP基础功能,附录1是我曾经用过的二维码/条形码扫描开源项目,但是附录1的项目集成和二次定制比较繁琐和麻烦,因此可以发现不少人基于ZXing做了二次的开发,并贡献出这些项目,发到github上,其中barcodescanner就是这样的项目,barcodescanner的github项目地址:https://github.com/dm77/barcodescanner ,barcodescanner简化了ZXing的集成和二次定制难度,方便快速集成和开发,并且扫

  • C#基于QRCode实现动态生成自定义二维码图片功能示例

    本文实例讲述了C#基于QRCode实现动态生成自定义二维码图片功能.分享给大家供大家参考,具体如下: 二维码早就传遍大江南北了,总以为它是个神奇的东西,其实细细研究之后发现也没想象的那么神秘,碰巧最近项目中需要动态生成二维码,解决完实际问题之后,简单总结整理一下.项目中除了动态生成二维码之外,还实现了动态生成自定义图片,二维码可以是其中的元素. 设置图片的数据源为动态图片 <body> <form id="form1" runat="server"

  • php+laravel 扫码二维码签到功能

    简介 为满足公司签到业务场景 最终敲定使用微信二维码来实现 微信公众号相关配置 在微信公众平台登陆上去后,点开开发中的基本配置看到的基本信息 框架及拓展包 laravel overtrue/laravel-wechat 安装方式:composer require "overtrue/laravel-wechat:^6.0" 详细了解请看:laravel-wechat 配置文件及对应信息 config/wechat.php /* * 公众号 */ 'official_account' =

  • java实现识别二维码图片功能

    本文实例为大家分享了java实现识别二维码图片功能,供大家参考,具体内容如下 所需maven依赖 <dependency>    <groupId>com.google.zxing</groupId>    <artifactId>javase</artifactId>    <version>3.2.1</version> </dependency> <dependency>     <gr

随机推荐