最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++?OpenCV實(shí)現(xiàn)二維碼檢測(cè)功能

 更新時(shí)間:2022年01月12日 10:56:40   作者:Zero___Chen  
這篇文章主要介紹了如何利用C++?OpenCV實(shí)現(xiàn)二維碼檢測(cè)功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

前言

本文將使用OpenCV C++ 進(jìn)行二維碼檢測(cè)。

一、二維碼檢測(cè)

首先我們要先將圖像進(jìn)行預(yù)處理,通過灰度、濾波、二值化等操作提取出圖像輪廓。在這里我還添加了形態(tài)學(xué)操作,消除噪點(diǎn),有效將矩形區(qū)域連接起來。

	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)開運(yùn)算消除邊緣毛刺
	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));
	Mat open;
	morphologyEx(bin, open, MORPH_OPEN, kernel);

	//通過Size(21,1)閉運(yùn)算能夠有效地將矩形區(qū)域連接 便于提取矩形區(qū)域
	Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1));
	Mat close;
	morphologyEx(open, close, MORPH_CLOSE, kernel1);

 

如圖為經(jīng)過一系列圖像處理之后得到的效果。之后我們需要對(duì)該圖進(jìn)行輪廓提取,找到二維碼所在的矩形區(qū)域。

	//使用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]);

		//通過面積閾值找到二維碼所在矩形區(qū)域
		if (area > 6000 && area < 100000)
		{
			//計(jì)算最小外接矩形
			RotatedRect MaxRect = minAreaRect(MaxContours[i]);
			//計(jì)算最小外接矩形寬高比
			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);
				//將矩形區(qū)域從原圖摳出來
				Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));

				ROI.copyTo(mask(MaxBox));

				ROI_Rect.push_back(mask);

			}

		}

	}

由以下代碼段我們就可以很好的找出二維碼所在的矩形區(qū)域,并將這些區(qū)域摳出來保存以便進(jìn)行下面的識(shí)別工作。

//找到二維碼所在的矩形區(qū)域
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)開運(yùn)算消除邊緣毛刺
	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));
	Mat open;
	morphologyEx(bin, open, MORPH_OPEN, kernel);
	//通過Size(21,1)閉運(yùn)算能夠有效地將矩形區(qū)域連接 便于提取矩形區(qū)域
	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]);

		//通過面積閾值找到二維碼所在矩形區(qū)域
		if (area > 6000 && area < 100000)
		{
			//計(jì)算最小外接矩形
			RotatedRect MaxRect = minAreaRect(MaxContours[i]);
			//計(jì)算最小外接矩形寬高比
			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);
				//將矩形區(qū)域從原圖摳出來
				Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));

				ROI.copyTo(mask(MaxBox));

				ROI_Rect.push_back(mask);

			}

		}

	}

}

如圖所示,這是找到的二維碼矩形。這里只展示其中之一。

二、二維碼識(shí)別

通過findContours找到輪廓層級(jí)關(guān)系

	//用于存儲(chǔ)檢測(cè)到的二維碼
	vector<vector<Point>>QR_Rect;
	
	//遍歷所有找到的矩形區(qū)域
	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找到輪廓之間的層級(jí)關(guān)系
		vector<vector<Point>>contours;
		vector<Vec4i>hierarchy;
		findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);

		//父輪廓索引
		int ParentIndex = -1;
		int cn = 0;

		//用于存儲(chǔ)二維碼矩形的三個(gè)“回”
		vector<Point>rect_points;
		for (int i = 0; i < contours.size(); i++)
		{
			//hierarchy[i][2] != -1 表示該輪廓有子輪廓  cn用于計(jì)數(shù)“回”中第幾個(gè)輪廓
			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級(jí)子輪廓?jiǎng)t認(rèn)定找到‘回'
			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);

			}

		}
	}

以上代碼段的整體思路為:首先經(jīng)過圖像預(yù)處理進(jìn)行輪廓檢測(cè),

通過hierarchy、RETR_TREE找到輪廓之間的層級(jí)關(guān)系。根據(jù)hierarchy[i][2]是否為-1判斷該輪廓是否有子輪廓。若該輪廓存在子輪廓,則統(tǒng)計(jì)有幾個(gè)子輪廓。如果該輪廓存在子輪廓,且有2級(jí)子輪廓?jiǎng)t認(rèn)定找到‘回’ 。關(guān)于輪廓的層級(jí)關(guān)系,大家可以自行百度查找資料,理解一下其中原理。

//對(duì)找到的矩形區(qū)域進(jìn)行識(shí)別是否為二維碼
int Dectect_QR_Rect(Mat src,Mat &canvas,vector<Mat>&ROI_Rect)
{
	//用于存儲(chǔ)檢測(cè)到的二維碼
	vector<vector<Point>>QR_Rect;
	
	//遍歷所有找到的矩形區(qū)域
	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找到輪廓之間的層級(jí)關(guān)系
		vector<vector<Point>>contours;
		vector<Vec4i>hierarchy;
		findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);

		//父輪廓索引
		int ParentIndex = -1;
		int cn = 0;

		//用于存儲(chǔ)二維碼矩形的三個(gè)“回”
		vector<Point>rect_points;
		for (int i = 0; i < contours.size(); i++)
		{
			//hierarchy[i][2] != -1 表示該輪廓有子輪廓  cn用于計(jì)數(shù)“回”中第幾個(gè)輪廓
			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級(jí)子輪廓?jiǎng)t認(rèn)定找到‘回'
			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();

}

由以上代碼段,我們就可以識(shí)別出二維碼。效果如圖所示。

三、二維碼繪制

	//框出二維碼所在位置
	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;


//找到二維碼所在的矩形區(qū)域
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)開運(yùn)算消除邊緣毛刺
	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1));
	Mat open;
	morphologyEx(bin, open, MORPH_OPEN, kernel);
	//通過Size(21,1)閉運(yùn)算能夠有效地將矩形區(qū)域連接 便于提取矩形區(qū)域
	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]);

		//通過面積閾值找到二維碼所在矩形區(qū)域
		if (area > 6000 && area < 100000)
		{
			//計(jì)算最小外接矩形
			RotatedRect MaxRect = minAreaRect(MaxContours[i]);
			//計(jì)算最小外接矩形寬高比
			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);
				//將矩形區(qū)域從原圖摳出來
				Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br()));

				ROI.copyTo(mask(MaxBox));

				ROI_Rect.push_back(mask);

			}

		}

	}

}


//對(duì)找到的矩形區(qū)域進(jìn)行識(shí)別是否為二維碼
int Dectect_QR_Rect(Mat src,Mat &canvas,vector<Mat>&ROI_Rect)
{
	//用于存儲(chǔ)檢測(cè)到的二維碼
	vector<vector<Point>>QR_Rect;
	
	//遍歷所有找到的矩形區(qū)域
	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找到輪廓之間的層級(jí)關(guān)系
		vector<vector<Point>>contours;
		vector<Vec4i>hierarchy;
		findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);

		//父輪廓索引
		int ParentIndex = -1;
		int cn = 0;

		//用于存儲(chǔ)二維碼矩形的三個(gè)“回”
		vector<Point>rect_points;
		for (int i = 0; i < contours.size(); i++)
		{
			//hierarchy[i][2] != -1 表示該輪廓有子輪廓  cn用于計(jì)數(shù)“回”中第幾個(gè)輪廓
			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級(jí)子輪廓?jiǎng)t認(rèn)定找到‘回'
			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 << "檢測(cè)到" << flag << "個(gè)二維碼。" << 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;
}

總結(jié)

本文使用OpenCV C++進(jìn)行二維碼檢測(cè),關(guān)鍵步驟有以下幾點(diǎn)。

1、圖像預(yù)處理,篩選出二維碼所在的矩形區(qū)域,并將該區(qū)域摳出來進(jìn)行后續(xù)的識(shí)別工作。

2、對(duì)篩選出的矩形區(qū)域進(jìn)行輪廓檢測(cè),判斷它們之前的層級(jí)關(guān)系,以此來識(shí)別二維碼。

3、最后根據(jù)檢測(cè)到的二維碼“回”字,將其繪制出來就可以了。

以上就是C++ OpenCV實(shí)現(xiàn)二維碼檢測(cè)功能的詳細(xì)內(nèi)容,更多關(guān)于C++ OpenCV二維碼檢測(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++矩陣運(yùn)算的實(shí)現(xiàn)簡(jiǎn)單

    C++矩陣運(yùn)算的實(shí)現(xiàn)簡(jiǎn)單

    本文主要介紹了C++矩陣運(yùn)算的實(shí)現(xiàn)簡(jiǎn)單,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C語言用遞歸函數(shù)實(shí)現(xiàn)漢諾塔

    C語言用遞歸函數(shù)實(shí)現(xiàn)漢諾塔

    大家好,本篇文章主要講的是C語言用遞歸函數(shù)實(shí)現(xiàn)漢諾塔,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • C語言音樂播放器實(shí)例代碼

    C語言音樂播放器實(shí)例代碼

    文章給大家分享了用C語言音樂播放器的實(shí)例代碼,對(duì)此有需要的朋友參考學(xué)習(xí)下。
    2018-07-07
  • 一文搞懂C++中繼承的概念與使用

    一文搞懂C++中繼承的概念與使用

    我們都知道面向?qū)ο笳Z言的三大特點(diǎn)是:**封裝,繼承,多態(tài)。**之前在類和對(duì)象部分,我們提到了C++中的封裝,那么今天呢,我們來學(xué)習(xí)一下C++中的繼承
    2022-07-07
  • C語言 自增自減運(yùn)算的區(qū)別詳解及實(shí)例

    C語言 自增自減運(yùn)算的區(qū)別詳解及實(shí)例

    這篇文章主要介紹了C語言中的++a和a++的區(qū)別詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • C語言輸出唯一的子串

    C語言輸出唯一的子串

    這篇文章主要介紹了C語言輸出唯一的子串,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2021-12-12
  • C++反射的一種實(shí)現(xiàn)方法詳解

    C++反射的一種實(shí)現(xiàn)方法詳解

    這篇文章主要給大家介紹了關(guān)于C++反射的一種實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • C語言實(shí)現(xiàn)掃雷程序

    C語言實(shí)現(xiàn)掃雷程序

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)掃雷程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • C++實(shí)現(xiàn)LeetCode(769.可排序的最大塊數(shù))

    C++實(shí)現(xiàn)LeetCode(769.可排序的最大塊數(shù))

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(769.可排序的最大塊數(shù)),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語言分治法實(shí)現(xiàn)歸并排序

    C語言分治法實(shí)現(xiàn)歸并排序

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)歸并排序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08

最新評(píng)論

城口县| 肃北| 安阳市| 彭水| 霍邱县| 若羌县| 灵宝市| 志丹县| 福安市| 英超| 加查县| 唐海县| 南丰县| 林西县| 新绛县| 渑池县| 安义县| 新龙县| 谷城县| 江都市| 乌兰察布市| 稻城县| 蓬莱市| 红安县| 曲周县| 什邡市| 新丰县| 祁阳县| 保山市| 晋城| 奈曼旗| 犍为县| 虞城县| 泰州市| 信宜市| 津南区| 龙口市| 仁怀市| 长岛县| 博爱县| 乌兰察布市|