• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

c++ 基于opencv 识别、定位二维码

c++ 搞代码 4年前 (2022-01-06) 31次浏览 已收录 0个评论
文章目录[隐藏]

这篇文章主要介绍了c++ 基于opencv 识别、定位二维码,帮助大家更好的理解和学习使用c++,感兴趣的朋友可以了解下

前言

因工作需要,需要定位图片中的二维码;我遂查阅了相关资料,也学习了opencv开源库。通过一番努力,终于很好的实现了二维码定位。本文将讲解如何使用opencv定位二维码。

定位二维码不仅仅是为了识别二维码;还可以通过二维码对图像进行水平纠正以及相邻区域定位。定位二维码,不仅需要图像处理相关知识,还需要分析二维码的特性,本文先从二维码的特性讲起。

1 二维码特性

二维码在设计之初就考虑到了识别问题,所以二维码有一些特征是非常明显的。

二维码有三个“回“”字形图案,这一点非常明显。中间的一个点位于图案的左上角,如果图像偏转,也可以根据二维码来纠正。

思考题:

为什么是三个点,而不是一个、两个或四个点。

一个点:特征不明显,不易定位。不易定位二维码倾斜角度。

两个点:两个点的次序无法确认,很难确定二维码是否放正了。

四个点:无法确定4个点的次序,从而无法确定二维码是否放正了。

识别二维码,就是识别二维码的三个点,逐步分析一下这三个点的特性

1 每个点有两个轮廓。就是两个口,大“口”内部有一个小“口”,所以是两个轮廓。

2 如果把这个“回”放到一个白色的背景下,从左到右,或从上到下画一条线。这条线经过的图案黑白比例大约为:黑白比例为1:1:3:1:1。

3 如何找到左上角的顶点?这个顶点与其他两个顶点的夹角为90度。

通过上面几个步骤,就能识别出二维码的三个顶点,并且识别出左上角的顶点。

2 使用opencv识别二维码

1)查找轮廓,筛选出三个二维码顶点

opencv一个非常重要的函数就是查找轮廓,就是可以找到一个图中的缩所有的轮廓,“回”字形图案是一个非常的明显的轮廓,很容易找到。

 int QrParse::FindQrPoint(Mat& srcImg, vector<vector>& qrPoint) { //彩色图转灰度图 Mat src_gray; cvtColor(srcImg, src_gray, CV_BGR2GRAY); namedWindow("src_gray"); imshow("src_gray", src_gray); //二值化 Mat threshold_output; threshold(src_gray, threshold_output, 0, 255, THRESH_BINARY | THRESH_OTSU); Mat threshold_output_copy = threshold_output.clone(); namedWindow("Threshold_output"); imshow("Threshold_output", threshold_output); //调用查找轮廓函数 vector<vector > contours; vector hierarchy; findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0)); //通过黑色定位角作为父轮廓,有两个子轮廓的特点,筛选出三个定位角 int parentIdx = -1; int ic = 0; for (int i = 0; i <contours.size(); i++) { if (hierarchy[i][2] != -1 && ic == 0) <strong style="color:transparent">来源gaodai#ma#com搞@代~码网</strong>{ parentIdx = i; ic++; } else if (hierarchy[i][2] != -1) { ic++; } else if (hierarchy[i][2] == -1) { ic = 0; parentIdx = -1; } { bool isQr = QrParse::IsQrPoint(contours[parentIdx], threshold_output_copy); //保存找到的三个黑色定位角 if (isQr) qrPoint.push_back(contours[parentIdx]); ic = 0; parentIdx = -1; } } return 0; }

找到了两个轮廓的图元,需要进一步分析是不是二维码顶点,用到如下函数:

 bool QrParse::IsQrPoint(vector& contour, Mat& img) { //最小大小限定 RotatedRect rotatedRect = minAreaRect(contour); if (rotatedRect.size.height <10 || rotatedrect.size.width </div><p>黑白比例判断函数:</p><div class="gaodaimacode"><pre class="prettyprint linenums"> //横向和纵向黑白比例判断 bool QrParse::IsQrColorRate(cv::Mat& image, int flag) { bool x = IsQrColorRateX(image, flag); if (!x) return false; bool y = IsQrColorRateY(image, flag); return y; } //横向黑白比例判断 bool QrParse::IsQrColorRateX(cv::Mat& image, int flag) { int nr = image.rows / 2; int nc = image.cols * image.channels(); vector vValueCount; vector vColor; int count = 0; uchar lastColor = 0; uchar* data = image.ptr(nr); for (int i = 0; i  0) color = 255; if (i == 0) { lastColor = color; count++; } else { if (lastColor != color) { vValueCount.push_back(count); count = 0; } count++; lastColor = color; } } if (count != 0) vValueCount.push_back(count); if (vValueCount.size() <5) return false; //横向黑白比例1:1:3:1:1 int index = -1; int maxCount = -1; for (int i = 0; i  maxCount) { index = i; maxCount = vValueCount[i]; } } } //左边 右边 都有两个值,才行 if (index <2) return false; if ((vValueCount.size() - index) <3) return false; //黑白比例1:1:3:1:1 float rate = ((float)maxCount) / 3.00; cout << "flag:" << flag << " "; float rate2 = vValueCount[index - 2] / rate; cout << rate2 << " "; if (!IsQrRate(rate2)) return false; rate2 = vValueCount[index - 1] / rate; cout << rate2 << " "; if (!IsQrRate(rate2)) return false; rate2 = vValueCount[index + 1] / rate; cout << rate2 << " "; if (!IsQrRate(rate2)) return false; rate2 = vValueCount[index + 2] / rate; cout << rate2 << " "; if (!IsQrRate(rate2)) return false; return true; } //纵向黑白比例判断 省略 bool QrParse::IsQrColorRateY(cv::Mat& image, int flag)
 bool QrParse::IsQrRate(float rate) { //大概比例 不能太严格 return rate > 0.6 && rate <1.9; }

2)确定三个二维码顶点的次序

 通过如下原则确定左上角顶点:二维码左上角的顶点与其他两个顶点的夹角为90度。

 // pointDest存放调整后的三个点,三个点的顺序如下 // pt0----pt1 // // pt2 bool QrParse::AdjustQrPoint(Point* pointSrc, Point* pointDest) { bool clockwise; int index1[3] = { 2,1,0 } ; int index2[3] = { 0,2,1 } ; int index3[3] = { 0,1,2 } ; for (int i = 0; i  80 && angle <99) { pointDest[0] = pointSrc[n[2]]; if (clockwise) { pointDest[1] = pointSrc[n[0]]; pointDest[2] = pointSrc[n[1]]; } else { pointDest[1] = pointSrc[n[1]]; pointDest[2] = pointSrc[n[0]]; } return true; } } return true; }

3)通过二维码对图片矫正。

图片有可能是倾斜的,倾斜夹角可以通过pt0与pt1连线与水平线之间的夹角确定。二维码的倾斜角度就是整个图片的倾斜角度,从而可以对整个图片进行水平矫正。

 //二维码倾斜角度 Point hor(pointAdjust[0].x+300,pointAdjust[0].y); //水平线 double qrAngle = QrParse::Angle(pointAdjust[1], hor, pointAdjust[0], clockwise); //以二维码左上角点为中心 旋转 Mat drawingRotation = Mat::zeros(Size(src.cols,src.rows), CV_8UC3); double rotationAngle = clockwise? -qrAngle:qrAngle; Mat affine_matrix = getRotationMatrix2D(pointAdjust[0], rotationAngle, 1.0);//求得旋转矩阵 warpAffine(src, drawingRotation, affine_matrix, drawingRotation.size());

4)二维码相邻区域定位

一般情况下,二维码在整个图中的位置是确定的。识别出二维码后,根据二维码与其他图的位置关系,可以很容易的定位别的图元。

后记

作者通过查找大量资料,仔细研究了二维码的特征,从而找到了识别二维码的方法。网上也有许多识别二维码的方法,但是不够严谨。本文是将二维码的多个特征相结合来识别,这样更准确。这种识别方法已应用在公司的产品中,识别效果还是非常好的。

以上就是c++ 基于opencv 识别、定位二维码的详细内容,更多关于c++ opencv 识别、定位二维码的资料请关注gaodaima搞代码网其它相关文章!

以上就是c++ 基于opencv 识别、定位二维码的详细内容,更多请关注gaodaima搞代码网其它相关文章!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:c++ 基于opencv 识别、定位二维码

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址