opencv练习---识别二维码

前言

刚刚学完OpenCv的基础部分,上手来一个简单的练习,定位识别二维码。

二维码有三个定位点,通过定位三个定位点来定位二维码的位置与大小。

处理步骤

  1. 灰度处理
  2. 滤波
  3. 二值化
  4. 发现轮廓,并导出子轮廓大于两个的父轮廓
  5. 筛选出三个轮廓,条件:有一个垂直角
  6. 计算倾斜角度并原图,将二维码抠出

成果展示

将原图标记出定位点:

输出图像均为

由输出可看出,任何角度的二维码,都可以正确的识别并取出。

代码展示

#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
using namespace std;
using namespace cv;

int threshold_value = 80;
int threshold_max = 255;
Mat src, dst;


int main()
{
	src = imread("C:/Users/LBJ/Desktop/二维码识别/二维码1.png");
	if (!src.data)
	{
		cout << "The iamge is empty" << endl;
		return -1;
	}

	src.copyTo(dst);
	namedWindow("output_image", WINDOW_AUTOSIZE);
	namedWindow("Input_Image", WINDOW_AUTOSIZE);
	
	cvtColor(src, src, CV_BGR2GRAY);
	blur(src, src, Size(3, 3), Point(-1, -1));;

	Mat bin_output;
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	vector<Point> convexs;       //用来储存三个标记框的点集,画最小矩形,即为二维码的区域
	vector<vector<Point>> Qrprint;
    threshold(src, bin_output, threshold_value, threshold_max, THRESH_BINARY);

/**************************************通过轮廓信息查找二维码的标记框**********************************************/
	findContours(bin_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

	for (size_t i = 0; i < contours.size(); i++)
	{
		int j = i, temp = 0;
		while (hierachy[j][2] != -1)
		{
			temp++;
			j = hierachy[j][2];
		}
		if (temp >= 2)
		{
	

			Qrprint.push_back(contours[i]);   //将识别到的定位点放入数组中

			drawContours(dst, contours, i, Scalar(0, 0, 255), -1, 8, hierachy, 0, Point(0, 0));
			cout << contours[i] << endl;
		}

	}
/********************************************************************************************************************/

/*************************通过两两垂直的关系,筛选出正确的三个识别框,储存子Q中************************************************/
	vector<RotatedRect> Qr_Rect(Qrprint.size());
	vector<Point> Center_Point(Qrprint.size());

	for (size_t i = 0; i < Qrprint.size(); i++)
	{
		Qr_Rect[i] = minAreaRect(Qrprint[i]);
		Center_Point[i] = Qr_Rect[i].center;
		circle(dst, Center_Point[i], 5, Scalar(0, 255, 0), -1, 8);
	}

	int Q[3];
	for (size_t i = 0; i < Qrprint.size(); i++)
	{
		for (size_t j = 0; j < Qrprint.size(); j++)
		{
			for (size_t k = 0; k < Qrprint.size(); k++)
			{
				if ( i!=j && i!=k && j!=k )
				{
					double k1, k2, k3;
					double dx, dy;
					dx = Center_Point[i].x - Center_Point[j].x;
					dy = Center_Point[i].y - Center_Point[j].y;
					k1 = dy / dx;
					dx = Center_Point[i].x - Center_Point[k].x;
					dy = Center_Point[i].y - Center_Point[k].y;
					k2 = dy / dx;
					dx = Center_Point[j].x - Center_Point[k].x;
					dy = Center_Point[j].y - Center_Point[k].y;
					k3 = dy / dx;

					if ((k1*k2 > -1.1&&k1*k2 < -0.9)) {
						Q[0] = i; Q[1] = j; Q[2] = k;
					}
					else if ((k1*k3 > -1.1&&k1*k3 < -0.9)) {
						Q[0] = j; Q[1] = i; Q[2] = k;
					}
					else if ((k3*k2 > -1.1&&k3*k2 < -0.9)) {
						Q[0] = k; Q[1] = j; Q[2] = i;
					}
					else { continue; }
					//q1储存了直角点的信息
				}

			}

		}

	}

	/**************************************在原图里标记出二维码的位置***************************************/
	for (size_t i = 0; i < 3; i++)
	{
		convexs.insert(convexs.end(), Qrprint[Q[i]].begin(), Qrprint[Q[i]].end());
	}
	Point2f ptr[4];
	RotatedRect rect = minAreaRect(convexs);
	rect.points(ptr);
	for (int i = 0; i < 4; i++)
	{
		line(dst, ptr[i], ptr[(i + 1) % 4], Scalar(0, 255, 0), 5, 8);

	}

	/**************************通过直角点位置计算旋转角度并且将二维码抠出********************************/
	double angel = rect.angle;; //计算旋转角度
	if (Center_Point[Q[0]].x< Center_Point[Q[1]].x && Center_Point[Q[0]].x < Center_Point[Q[2]].x)
	{
	}
	else if (Center_Point[Q[0]].y > Center_Point[Q[1]].y && Center_Point[Q[0]].y > Center_Point[Q[2]].y)
	{
		angel -= 90;

	}
	else if (Center_Point[Q[0]].x > Center_Point[Q[1]].x && Center_Point[Q[0]].x > Center_Point[Q[2]].x)
	{
		angel -= 180;
	}
	else if (Center_Point[Q[0]].y < Center_Point[Q[1]].y && Center_Point[Q[0]].y < Center_Point[Q[2]].y)
	{
		angel -= 270;

	}

	Mat  Rotation = getRotationMatrix2D(rect.center, angel, 1.0);
	warpAffine(src, src, Rotation, src.size());

	Mat Result = src(Rect(rect.center.x - (rect.size.width / 2), rect.center.y - (rect.size.height / 2), rect.size.width, rect.size.height));

	imshow("Input_Image", dst);
	imshow("output_image", Result);

	

	waitKey(0);
	return 0;
}

可优化部分

1、识别二维码三个定位点的方法是寻找子轮廓数大于二的父轮廓,改成寻找子轮廓数大于二的轮廓的最小子轮廓,效果可能会好一点。

2、对于畸变后的二维码无法识别,后期可以尝试改进。

OpenCV+zbar开源库实现摄像头识别二维码,测试验证识别率非常高,已实现简单的应用。 打包源码在VS2013下可以完全编译成功,附加包含OpenCV库及zbar-0.10-setup.exe,zbar-0.10.tar.bz2 下载Demo后需要安装 zbar-0.10-setup.exe 以下代码可以可以完成整个流程的开发,也可以贡献积分下载资源包。 1、 环境准备 (1) OpenCV库2.49 (2) ZBar开源库 (3) VS2013 2、 VS2013环境配置 (1) 配置附加包含目录 C/C++ -- 附加包含目录 include\opencv\include\ include\opencv\include\opencv include\opencv\include\opencv2 include (2) 配置链接器 链接器 -- 附加库目录 lib32\opencv\lib lib32 (3) 配置链接器 链接器--输入--附加依赖项 opencv_core249d.lib opencv_highgui249d.lib opencv_imgproc249d.lib libzbar-0.lib 3、 代码开发 (1)包含头文件 include include include include include include using namespace std; using namespace zbar; using namespace cv; (2)实现函数 void MatToCImage(cv::Mat &mat, CImage &cImage) { //create new CImage int width = mat.cols; int height = mat.rows; int channels = mat.channels(); cImage.Destroy(); //clear cImage.Create(width, height, 8 * channels); //默认图像像素单通道占用1个字节 //copy values uchar* ps; uchar* pimg = (uchar*)cImage.GetBits(); //A pointer to the bitmap buffer int step = cImage.GetPitch(); for (int i = 0; i (i)); for (int j = 0; j GetDlgItem(IDC_STATIC_IMG)->GetClientRect(▭); cv::VideoCapture capture(0);//从摄像头读入图像 while (!m_bCloseCamera) { cv::Mat frame; capture >> frame; cv::Mat newframe; cv::Size ResImgSiz = cv::Size(rect.Width(), rect.Height()); cv::resize(frame, newframe, ResImgSiz, CV_INTER_CUBIC); MatToCImage(newframe, imgDst); imgDst.Draw(pThis->GetDlgItem(IDC_STATIC_IMG)->GetDC()->GetSafeHdc(), rect); ImageScanner scanner; scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1); Mat imageGray; cvtColor(frame, 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()) { } else { iIndex++; if (iIndex > 999999) { iIndex = 0; } for (; symbol != imageZbar.symbol_end(); ++symbol) { char szInfo[1024]; memset(szInfo, 0, sizeof(szInfo)); sprintf(szInfo, "[d]类型:%s\r\n条码:%s\r\n", iIndex , symbol->get_type_name().c_str(), symbol->get_data().c_str()); pThis->GetDlgItem(IDC_EDIT1)->SetWindowText(szInfo); } } imageZbar.set_data(NULL, 0); } imgDst.Destroy(); capture.release(); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值