您当前的位置:首页 > 计算机 > 编程开发 > 数据结构与算法

OpenCV 二维码定位与识别

时间:03-27来源:作者:点击数:

因为二维码本身含有信息,因此可以作为产品的信息载体,如:产品特征。在工业领域常用在产品入库、分拣和包装上。但常常会因为二维码图像污点、光照不均匀以及二维码图像倾斜等原因,使得二维码的识别正确率低,针对这些问题,通过学习贾老师OpenCV课程以及其他作者的经验,实现了基于OpenCV的二维码定位与识别,但仍有一些问题需要进一步改进,如:背景复杂的情况下,应该采用“1 : 1:3 : 1:1”的特点,进一步判断三个定位角的位置。

在这里插入图片描述

1、步骤

  1. 通过灰度化、滤波、直方图均衡化、图像增强等操作预处理图像;
  2. 利用二维码 “ 回 ”形定位角定位二维码位置;
  3. 找到规则二维码的左上角点为透视变换的起点,和其他三个点;
  4. 构造变换后的点,透视变换,一一对应,获取规则的二维码图像;
  5. 使用Zbar工具进行二维码识别。

2、代码实现

使用findContours函数中的hierarchy参数获取“回”形定位角点,然后基于左上角位于直角的特点定位到该点,因为相机相对于二维码平面可能不是垂直关系,因此使用透视变换,而不是仿射变换。

Mat imageContours = Mat::ones(img.size(), CV_8UC1); //最小外接矩形画布
	vector<vector<Point>>contours, conts;
	vector<Vec4i>hierarchy;

	findContours(img, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point());

	int flag = 0, c = 0;
	for (int i = 0; i < contours.size(); i++)
	{
		if (hierarchy[i][2] != -1 && flag == 0)
		{
			flag++;
			c = i;

		}

		else if (hierarchy[i][2] == -1)
		{
			flag = 0;
		}

		else if (hierarchy[i][2] != -1)
		{
			flag++;
		}

		if (flag >= 2)
		{
			flag = 0;
			conts.push_back(contours[c]);
		}
	}

	int count = conts.size();
	cout << count << endl;
	vector<Point> pointthree;
	for (int i = 0; i < count; i++) {
		RotatedRect rect = minAreaRect(conts[i]);
		
		Point2f P[4];
		rect.points(P);
//		circle(imageContours, P[1], 6, (255), 1, 8);
		for (int j = 0; j <= 3; j++)
		{
			line(imageContours, P[j], P[(j + 1) % 4], Scalar(255), 2);
			
		}
		imshow("MinAreaRect", imageContours);

		pointthree.push_back(rect.center);
	}
	//找到角度最大的点
	double ca[2];
	double cb[2];
	



	ca[0] = pointthree[1].x - pointthree[0].x;
	ca[1] = pointthree[1].y - pointthree[0].y;
	cb[0] = pointthree[2].x - pointthree[0].x;
	cb[1] = pointthree[2].y - pointthree[0].y;
	double angle1 = 180 / 3.1415*acos((ca[0] * cb[0] + ca[1] * cb[1]) / (sqrt(ca[0] * ca[0] + ca[1] * ca[1])*sqrt(cb[0] * cb[0] + cb[1] * cb[1])));
	double ccw1;
	if (ca[0] * cb[1] - ca[1] * cb[0] > 0) ccw1 = 0;
	else ccw1 = 1;

	ca[0] = pointthree[0].x - pointthree[1].x;
	ca[1] = pointthree[0].y - pointthree[1].y;
	cb[0] = pointthree[2].x - pointthree[1].x;
	cb[1] = pointthree[2].y - pointthree[1].y;
	double angle2 = 180 / 3.1415*acos((ca[0] * cb[0] + ca[1] * cb[1]) / (sqrt(ca[0] * ca[0] + ca[1] * ca[1])*sqrt(cb[0] * cb[0] + cb[1] * cb[1])));
	cout << sqrt(ca[0] * ca[0] + ca[1] * ca[1]) << endl;
	cout << sqrt(cb[0] * cb[0] + cb[1] * cb[1]) << endl;

	double ccw2;
	if (ca[0] * cb[1] - ca[1] * cb[0] > 0) ccw2 = 0;
	else ccw2 = 1;

	ca[0] = pointthree[1].x - pointthree[2].x;
	ca[1] = pointthree[1].y - pointthree[2].y;
	cb[0] = pointthree[0].x - pointthree[2].x;
	cb[1] = pointthree[0].y - pointthree[2].y;
	double angle3 = 180 / 3.1415*acos((ca[0] * cb[0] + ca[1] * cb[1]) / (sqrt(ca[0] * ca[0] + ca[1] * ca[1])*sqrt(cb[0] * cb[0] + cb[1] * cb[1])));
	double ccw3;
	if (ca[0] * cb[1] - ca[1] * cb[0] > 0) ccw3 = 0;
	else ccw3 = 1;

	vector<Point2f> poly(4);
	if (angle3>angle2 && angle3>angle1)
	{
		if (ccw3)
		{
			poly[1] = pointthree[1];
			poly[3] = pointthree[0];
		}
		else
		{
			poly[1] = pointthree[0];
			poly[3] = pointthree[1];
		}
		poly[0] = pointthree[2];
		Point temp(pointthree[0].x + pointthree[1].x - pointthree[2].x, pointthree[0].y + pointthree[1].y - pointthree[2].y);
		poly[2] = temp;
//		circle(img, poly[0], 6, Scalar(255, 255, 255), 1, 8);
	}
	else if (angle2>angle1 && angle2>angle3)
	{
		if (ccw2)
		{
			poly[1] = pointthree[0];
			poly[3] = pointthree[2];
		}
		else
		{
			poly[1] = pointthree[2];
			poly[3] = pointthree[0];
		}
		poly[0] = pointthree[1];
		Point temp(pointthree[0].x + pointthree[2].x - pointthree[1].x, pointthree[0].y + pointthree[2].y - pointthree[1].y);
		poly[2] = temp;
//		circle(img, poly[0], 6, Scalar(255, 255, 255), 1, 8);
	}
	else if (angle1>angle2 && angle1 > angle3)
	{
		if (ccw1)
		{
			poly[1] = pointthree[1];
			poly[3] = pointthree[2];
		}
		else
		{
			poly[1] = pointthree[2];
			poly[3] = pointthree[1];
		}
		poly[0] = pointthree[0];
		Point temp(pointthree[1].x + pointthree[2].x - pointthree[0].x, pointthree[1].y + pointthree[2].y - pointthree[0].y);
		poly[2] = temp;
//		circle(img, poly[0], 6, Scalar(255, 255, 255), 1, 8);
	}

	vector<Point2f> trans(4);
	int temp = 60;
	trans[0] = Point2f(0 + temp, 0 + temp);
	trans[1] = Point2f(0 + temp, 230 + temp);
	trans[2] = Point2f(230 + temp, 230 + temp);
	trans[3] = Point2f(230 + temp, 0 + temp);

	//获取透视投影变换矩阵
	
	Mat m = getPerspectiveTransform(poly, trans);

	//计算变换结果
	Mat result;
	warpPerspective(img,result,m,Size(350, 350),INTER_LINEAR);

	rectangle(result, Rect(10, 10, 330, 330), Scalar(0, 0, 0), 1, 8);

获取到规则二维码图像之后,调用Zbar工具实现二维码的识别。

clock_t start = clock(); // 记录程序开始时间,用于计算扫描二维码耗时
zbar::ImageScanner scanner;
scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);


int width = result.cols;
int height = result.rows;
Image image(width, height, "Y800", result.data, width * height);  // 图片格式转换
scanner.scan(image);
Image::SymbolIterator symbol = image.symbol_begin();
if (image.symbol_begin() == image.symbol_end())
{
	cout << "查询条码失败,请检查图片!" << endl;
}
for (; symbol != image.symbol_end(); ++symbol)
{
	cout << "类型:" << endl << symbol->get_type_name() << endl << endl;
	cout << "条码:" << endl << symbol->get_data() << endl << endl;
}
image.set_data(nullptr, 0);

clock_t finish = clock();  // 记录程序结束时间
double time_length = (double)(finish - start) / CLOCKS_PER_SEC; //根据两个时刻的差,计算出扫描的时间  
cout << "扫描耗时 " << time_length << " seconds." << endl;

3、结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

透视变换的图像并没有获取到方正的图像,应该是变换角点坐标选取和长宽值不适合的原因,后面再改进。Zbar对于英文、数字等识别效果很好,但是识别中文会出现乱码现象,查阅文章发现是编码和解码格式问题,下次解决。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门