二维码定位

使用opencv确定图片中二维码的位置

背景

确定二维码的位置并识别,有很多开源的例子,但是毕竟不是自己做的,还是想自己一步步学习一下,这里只做了一下工作:

1.确定二维码在图片中的位置,输出其像素坐标。

2.使用透视变换对识别的二维码矩形进行修正,方便之后的二维码识别(这里暂不做识别,有意的同学可以使用Zbar,zxing进行识别)。

在opencv的学习上我还是一个小白,有些考虑不周的地方还请多多指教。

流程图

首先上一张二维码的示意图,这里用abcd指代小矩形,下文会用到。
2

下面是流程图
img8

实现

对每一帧的图像的预处理详见附件,这里直接从Canny提取轮廓之后开始讲述

1. 边缘检测–Canny
Canny(blurImage, edgeTemp, canny_thread_min, canny_thread_max, 3);
2. 提取轮廓–findContours (文末有备注)
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
//输入图像,轮廓(点向量形式),轮廓数量,轮廓检索模式,轮廓逼近 
findContours(edgeTemp, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
3. 绘制嵌套层次大于5的轮廓 (详情请点击此处了解hierarchy的含义)
vector<int> vector_contours_filter;
for (int k, c, i = 0; i < contours.size(); i++)
{
	k = i;
	c = 0;
	while (hierarchy[k][2] != -1)//表示不是最外面的轮廓
	{
		k = hierarchy[k][2];
		c = c + 1;
	}
	if (c >= 5)
	{
		vector_contours_filter.push_back(i);
	}
}
4. 利用abc三个小矩形的12个顶点做最小包围矩形
//这里直接使用三个小矩形的12个顶点来绘制最小包围矩形
Point2f point_minRect12[4];
RotatedRect rect12 = minAreaRect(vertex_minRect4);	//vertex_minRect4 -> 装有abc三个小矩形的12个顶点的容器
rect12.points(point_minRect12);//返回矩形的四个顶点给vertex  
for (int j = 0; j < 4; j++)
{
	line(src1, point_minRect12[j], point_minRect12[(j + 1) % 4], Scalar(0, 0, 255), 3, 8);//非常巧妙的表达式
}

Point2f midPoint_rect;
for (int j = 0; j < 4; j++)
{
	midPoint_rect.x += (point_minRect12[j].x / 4);
	midPoint_rect.y += (point_minRect12[j].y / 4);
}
circle(src1, midPoint_rect, 10, Scalar(0, 255, 0), 3, 8);	//外接矩形的的中心
imshow("结果图", src1);

经过这次处理之后,基本上就只剩下abc三个小矩形了。一般情况下,利用这三个小矩形的各个顶点,绘制包围他们的最小矩形(约定包围整个二维码的最小矩形为矩形A),然后求得矩形A的中心点O,这样就得到了像素坐标下的二维码的位置。效果如下图所示:
img3

但是,偶尔会有这种特殊的情况发生
img4 img5

############### 这就很尴尬了。。。##################

虽然这只是个别的情况,但也说明了只利用abc这三个小矩形来确定二维码的位置并不可靠。所以很有必要确定第四个小矩形d的位置,利用其把这个别情况下的二维码的最小包围矩形框纠正回来。

5. 画出第四个小矩形d

已知三个小矩形abc之后,第四个矩形d也就比较容易了。

img6

在第4步中的代码注释中提到vertex_minRect4是装有矩形abc总计12个顶点的容器,遍历这12个顶点,求两点之间的距离,找出距离最远的两个点,就是点a-1和c-3,然后利用求线段中点的公式,如下:

midPoint.x = (P_1.x + P_2.x)/2;

midPoint.y = (P_1.y + P_2.y)/2;

来获取中点O的坐标,然后再分别求矩形b的四个点关于中点O的对称点的坐标,就是求线段中点的逆过程嘛。于是乎,矩形d的四个顶点我们就get到了。最后利用这四个矩形abcd的16个顶点,画它们的最小外接矩形,再通过矩形的四个顶点求取二维码的中点,

到这里,二维码在图像中的位置就确定了,是像素坐标哦

上述的那个特殊情况就不存在了Yeah!

6. 提取二维码

使用透视变换,将二维码投影到另一个平面,方便以后的识别

  1. 二维码的四个顶点的坐标:上一步利用abcd四个矩形的16个顶点做最小包围矩形时,可以获得矩形的四个顶点,数组vertexs_minRect_QR表示。

  2. 新的投影点的坐标:新的投影平面中,二维码的坐标可以自己定义,比如

//lenth是二维码的变长
Point2f vertex_warp[4];
vertex_warp[0] = Point2f(0, float(lenth-1));
vertex_warp[1] = Point2f(0, 0);
vertex_warp[2] = Point2f(float(lenth-1), 0);
vertex_warp[3] = Point2f(float(lenth-1), float(lenth));
  1. 求解透视变换的矩阵
    可以使用opencv库中的getPerspectiveTransform函数获得
//! returns 3x3 perspective transformation for the corresponding 4 point pairs.
CV_EXPORTS Mat getPerspectiveTransform( const Point2f src[], const Point2f dst[] );

操作代码如下,由此获得变换矩阵transform

Mat transform = getPerspectiveTransform(vertexs_minRect_QR, vertex_warp);
  1. 透视变换的实现
    奉上透视变换的函数warpPerspective
//! warps the image using perspective transformation
CV_EXPORTS_W void warpPerspective( InputArray src, OutputArray dst,
                                   InputArray M, Size dsize,
                                   int flags=INTER_LINEAR,
                                   int borderMode=BORDER_CONSTANT,
                                   const Scalar& borderValue=Scalar());

实现

warpPerspective(src, dst, transform, Size(lenth, lenth)); 

结果图
img7
左是识别的效果图,右上是使用透视变换投影到另一个平面的效果,右下是二维码中心在原图中的像素坐标

备注:关于提取轮廓findContours函数的参数说明

函数原型如下:

//! retrieves contours and the hierarchical information from black-n-white image.
CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset=Point());

其中的参数mode有以下可供选择,在识别二维码中,只能使用CV_RETR_TREE

RETR_EXTERNAL=CV_RETR_EXTERNAL, //!< retrieve only the most external (top-level) contours
RETR_LIST=CV_RETR_LIST, //!< retrieve all the contours without any hierarchical information
RETR_CCOMP=CV_RETR_CCOMP, //!< retrieve the connected components (that can possibly be nested)
RETR_TREE=CV_RETR_TREE, //!< retrieve all the contours and the whole hierarchy
RETR_FLOODFILL=CV_RETR_FLOODFILL

详情请点击此处了解
最后附上支付宝红包二维码,欢迎客官扫一扫~
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值