2025年5月17日 星期六 乙巳(蛇)年 二月十九 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > 数据结构与算法

OpenCV 二维码定位与识别

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

因为二维码本身含有信息,因此可以作为产品的信息载体,如:产品特征。在工业领域常用在产品入库、分拣和包装上。但常常会因为二维码图像污点、光照不均匀以及二维码图像倾斜等原因,使得二维码的识别正确率低,针对这些问题,通过学习贾老师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对于英文、数字等识别效果很好,但是识别中文会出现乱码现象,查阅文章发现是编码和解码格式问题,下次解决。

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