【Zbar+OpenCV3识别并二维码】C++详细实现教程

时间:2024-03-17 12:01:56

实属抱歉由于开学准备功课加上其他项目,时间方面不允许,暂时先更新到这里,若有时间在多加补充了,抱拳.jpg。如有其他问题,亦可联系QQ:56672035

一、二维码概述

1. 简介

二维码又称二维条码,常见的二维码为QR Code。“QR”是“Quick Response”的缩写,它指的就是可以对隐藏在二维码中的数据实现快速读取。QR码之所以能够快速的普及,就是因为这项技术是开源的。也就是说每个人都可以使用。QR码相对传统条形码的优势是数据存储量大和高容错性。
【Zbar+OpenCV3识别并二维码】C++详细实现教程

2. 原理

在代码编制上使用01编制流,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理。

二维条码/二维码可以分为堆叠式/行排式二维条码和矩阵式二维条码。 堆叠式/行排式二维条码形态上是由多行短截的一维条码堆叠而成;矩阵式二维条码以矩阵的形式组成,在矩阵相应元素位置上用“点”表示二进制“1”, 用“空”表示二进制“0”,“点”和“空”的排列组成代码。
【Zbar+OpenCV3识别并二维码】C++详细实现教程

3. 结构组成

一个二维码主要包括功能组成以及编码区域。
功能图形起到定位的作用

位置探测图形:由三个黑白相间的大正方形嵌套组成,分别位于二维码左上角、右上角、左下角,目的是为了确定二维码的大小和位置。

定位图形 :由两条黑白相间的直线组成,便于确定二维码的角度,纠正扭曲。

校正图形 :仅在版本2以上存在,由三个黑白相间的小正方形嵌套组成,便于确定中心,纠正扭曲。

数据区记录了具体的数据信息,纠错信息与版本信息。

数据和纠错码:记录了数据信息和相应的纠错码,纠错码的存在使得当二维码的数据出现允许范围内的错误时,也可以正确解码。

版本信息 :仅在版本7以上存在,记录具体的版本信息。

格式信息 :记录使用的掩码和纠错等级。

此外二维码的外围还留有一圈空白区,主要是为了便于识别而存在。
【Zbar+OpenCV3识别并二维码】C++详细实现教程

这里介绍大家看另一位博主的文章,QR码的结构是如何组成的~

4. 分类

QRCode 分为 Model 1、Model 2、Micro QR 三类:

  • Model 1 :是 Model 2 和 Micro QR 的原型,有 Version 1 到 Version 14 共14种尺寸。
  • Model 2 :是 Model 1 的改良版本,添加了对齐标记,有 Version 1 到 Version 40 共40种尺寸。
  • Micro QR :只有一个定位标记,最小尺寸是 11*11 modules 。

二、二维码定位检测

#include "pch.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>


using namespace cv;
using namespace std;


Mat src; Mat src_gray;


RNG rng(12345);
//Scalar colorful = CV_RGB(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255));


Point Center_cal(vector<vector<Point> > contours, int i)//找到所提取轮廓的中心点
{
	int centerx = 0, centery = 0, n = contours[i].size();
	//在提取的小正方形的边界上每隔周长个像素提取一个点的坐标,求所提取四个点的平均坐标(即为小正方形的大致中心)
	centerx = (contours[i][n / 4].x + contours[i][n * 2 / 4].x + contours[i][3 * n / 4].x + contours[i][n - 1].x) / 4;
	centery = (contours[i][n / 4].y + contours[i][n * 2 / 4].y + contours[i][3 * n / 4].y + contours[i][n - 1].y) / 4;
	Point point1 = Point(centerx, centery);
	return point1;
}

int main(int argc, char** argv[])
{


	src = imread("H://图像处理//Image Gallery//QR_Code//WeChat.png", 1);
	Mat src_all = src.clone();

	cvtColor(src, src_gray, CV_BGR2GRAY);
	//  src_gray = Scalar::all(255) - src_gray;
	blur(src_gray, src_gray, Size(3, 3));
	equalizeHist(src_gray, src_gray);
	cvNamedWindow("滤波后", 0);
	imshow("滤波后", src_gray);

	Scalar color = Scalar(1, 1, 255);
	Mat threshold_output;
	vector<vector<Point> > contours, contours2;
	vector<Vec4i> hierarchy;
	Mat drawing = Mat::zeros(src.size(), CV_8UC3);
	Mat drawing2 = Mat::zeros(src.size(), CV_8UC3);
	threshold(src_gray, threshold_output, 112, 255, THRESH_BINARY);
	//Canny(src_gray,threshold_output,136,196,3);
	//imshow("预处理后:",threshold_output);
	//寻找轮廓 
	//第一个参数是输入图像 2值化的
	//第二个参数是内存存储器,FindContours找到的轮廓放到内存里面。
	//第三个参数是层级,**[Next, Previous, First_Child, Parent]** 的vector
	//第四个参数是类型,采用树结构
	//第五个参数是节点拟合模式,这里是全部寻找
	findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));
	//CHAIN_APPROX_NONE全体,CV_CHAIN_APPROX_SIMPLE,,,RETR_TREE    RETR_EXTERNAL    RETR_LIST   RETR_CCOMP

	int c = 0, ic = 0, k = 0, area = 0;

	//程序的核心筛选
	int parentIdx = -1;
	for (int i = 0; i < contours.size(); i++)
	{
		if (hierarchy[i][2] != -1 && ic == 0)
		{
			parentIdx = i;
			ic++;
		}
		else if (hierarchy[i][2] != -1)
		{
			ic++;
		}
		else if (hierarchy[i][2] == -1)
		{
			ic = 0;
			parentIdx = -1;
		}

		if (ic >= 2)
		{
			contours2.push_back(contours[parentIdx]);
			drawContours(drawing, contours, parentIdx, CV_RGB(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), 1, 8);
			ic = 0;
			parentIdx = -1;
			area = contourArea(contours[i]);//得出一个二维码定位角的面积,以便计算其边长(area_side)(数据覆盖无所谓,三个定位角中任意一个数据都可以)
		}
		//cout<<"i= "<<i<<" hierarchy[i][2]= "<<hierarchy[i][2]<<" parentIdx= "<<parentIdx<<" ic= "<<ic<<endl;
	}


	for (int i = 0; i < contours2.size(); i++)
		drawContours(drawing2, contours2, i, CV_RGB(rng.uniform(100, 255), rng.uniform(100, 255), rng.uniform(100, 255)), -1, 4, hierarchy[k][2], 0, Point());


	Point point[3];
	for (int i = 0; i < contours2.size(); i++)
	{
		point[i] = Center_cal(contours2, i);
	}
	area = contourArea(contours2[1]);//为什么这一句和前面一句计算的面积不一样呢
	int area_side = cvRound(sqrt(double(area)));
	for (int i = 0; i < contours2.size(); i++)
	{
		line(drawing2, point[i%contours2.size()], point[(i + 1) % contours2.size()], color, area_side / 4, 8);
	}


	imshow("提取后", drawing2);
	printf("%d\n", contours.size());
	//imshow( "Contours", drawing );

	//接下来要框出这整个二维码
	Mat gray_all, threshold_output_all;
	vector<vector<Point> > contours_all;
	vector<Vec4i> hierarchy_all;
	cvtColor(drawing2, gray_all, CV_BGR2GRAY);


	threshold(gray_all, threshold_output_all, 45, 255, THRESH_BINARY);

	//表示只寻找最外层轮廓
	findContours(threshold_output_all, contours_all, hierarchy_all, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));//RETR_EXTERNAL表示只寻找最外层轮廓

	//求最小包围矩形,斜的也可以哦
	RotatedRect rectPoint = minAreaRect(contours_all[0]);
	Point2f fourPoint2f[4];

	//将rectPoint变量中存储的坐标值放到 fourPoint的数组中
	rectPoint.points(fourPoint2f);


	for (int i = 0; i < 4; i++)
	{
		line(src_all, fourPoint2f[i % 4], fourPoint2f[(i + 1) % 4], Scalar(20, 21, 237), 3);
	}

	imshow("二维码", src_all);
	
	waitKey(0);
	return(0);
}

三、Zbar库和OpenCV的配置

这里尤其要注意官方Zbar库只能只能在32位下才能运行,同时为保障OpenCV适用,也需配置X86版

四、二维码信息读取

#include "pch.h"
#include <iostream>
#include <zbar.h>
#define STR(s) #s
#include <opencv2/opencv.hpp>

using namespace std;
using namespace zbar;
using namespace cv;

int main()

{
	ImageScanner scanner;

	scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);

	cv::Mat img = cv::imread("H://图像处理//Image Gallery//QR_Code//Music.png");

	cv::Mat imgGray;

	imgGray.create(img.size(), CV_8UC1);

	cvtColor(img, imgGray, CV_BGR2GRAY);

	int width = imgGray.cols;

	int height = imgGray.rows;

	Image image(width, height, "Y800", imgGray.data, width *height);

	int n = scanner.scan(image);

	for (Image::SymbolIterator symbol = image.symbol_begin();

		symbol != image.symbol_end();

		++symbol) {

		cout << "decoded " << symbol->get_type_name()

			<< " symbol \"" << symbol->get_data() << '"' << endl;

	}

	image.set_data(NULL, 0);



	return(0);

}

五、摄像头实时读取

#include "pch.h"
#include "zbar.h"        
#include "cv.h"        
#include "highgui.h"        
#include "iostream"
#include "fstream"
#include "opencv2/core/core.hpp" 
#include "opencv2/highgui/highgui.hpp" 
#include "opencv2/imgproc/imgproc.hpp" 

using namespace std;
using namespace zbar;      
using namespace cv;

int main(int argc, char*argv[])
{

	VideoCapture VC(0);
	while (true)
	{
		ImageScanner scanner;
		scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);

		Mat image;
		VC.read(image);
		imshow("Source Image", image);
		if (!image.data)
		{
			cout << "请确认摄像头正确采集" << endl;
			system("pause");
			return 0;
		}
		Mat imageGray;
		cvtColor(image, imageGray, CV_RGB2GRAY);
		int width = imageGray.cols;
		int height = imageGray.rows;
		uchar *raw = (uchar *)imageGray.data;
		Image imageZbar(width, height, "Y800", raw, width * height);
		scanner.scan(imageZbar); //扫描条码      
		Image::SymbolIterator symbol = imageZbar.symbol_begin();
		if (imageZbar.symbol_begin() == imageZbar.symbol_end())
		{
			cout << "查询条码失败,请检查图片!" << endl;
		}
		else
		{
			for (; symbol != imageZbar.symbol_end(); ++symbol)
			{
				cout << "类型:" << endl << symbol->get_type_name() << endl << endl;
				cout << "条码:" << endl << symbol->get_data() << endl << endl;
				//将检测的结果写到result.txt中方便查阅,追加方式写入的,
				ofstream fout("result.txt", ios::app);
				fout << "类型:" << symbol->get_type_name() << endl << "条码:" << symbol->get_data() << endl << endl;
				fout.close();
				int key = cvWaitKey();
				if (key == 27) return 0;
			}
		}

		imageZbar.set_data(NULL, 0);
		int key = cvWaitKey(100);
		if (key == 27) return 0;
	}
	waitKey();
	return 0;
}

六、运行测试

最后面恬不知耻的放上一张自己的打赏码
【Zbar+OpenCV3识别并二维码】C++详细实现教程


引用:

1.二维码_百度百科https://baike.baidu.com/item/二维码/2385673?fr=aladdin
2.
3.
4.
5.

文章正在更新中~~~
如有不当欢迎指正!