之前用Python+Opencv 写过答题卡识别的算法。
这篇博客用的C++和Opencv来完成答题卡的识别,相关算法的思想是不变的。
(一)定位答题卡的四个边角锚点
因为标准答题卡的四角都有黑色的圈圈,将这些圈圈的位置定位出来,就相当于定位出四个边角锚点。
定位黑色圆圈,有两种方式:
(1)霍夫圆检测,找到圆心的位置,这个方法我在之前的Python版本中已经用过。但是我用C++来做的时候,霍夫圆检测速度比较慢,而且占很大的内存。所以改用了第二种方法。
(2)模板匹配,事先做好和边角黑圈大小差不多的模板图片。然后对输入图片进行模板匹配。
模板匹配:通过在输入图像上滑动模板图片,对两者进行对比,找出相似度最高的ROI区域。
实现模板匹配的函数:matchTeplate()。它的参数如下:
第一个参数:InputArray image,8位或者32位浮点型输入图像。
第二个参数:InputArray template,和源图片有一样的数据类型,而且尺寸不能大于原图。
第三个参数:OutArray result,比较结果的映射图像。这里的result是32位的单通道图像,输入图像的尺寸是(W,H),模板图像的尺寸是(w,h),那么它的大小是(W-w+1)*(H-h+1)。
第四个参数:int类型的method。Opencv提供了6种图像匹配的方法。
匹配后的结果是32位,需要做个归一化处理。
实现归一化的函数:normalize()。它的参数如下:
第一个参数:输入图像。第二个参数:输出图像。Mat类的对象即可。
第三个参数:double类型alpha。第四个参数:double类型beta。
第五个参数:norm_type的类型。
第六个参数:int类型的dtype,默认是-1。当这个参数取负数的时候,输出矩阵和src拥有同样的类型,否则就是和src相同的通道数。
第七个参数:可选的操作掩模。
模板匹配的代码思想:
(1)将整张图片分成四个部分。
这里的Rect使用注意:
1,第一个参数和第二个参数,表示划分图片的左上角坐标,第三和第四个参数表示划分区域的宽和高。
2,所以获取的最大位置的坐标是相对裁剪部分而言,所以返回坐标的时候需要做相应的调整,使其能够对应到原图的位置。
Mat matLT = matSrc(Rect(0, 0, col / 2, row / 2));
Mat matLD = matSrc(Rect(0, row/2, col / 2, row / 2));
Mat matRT = matSrc(Rect(col/2 ,0, col / 2, row / 2));
Mat matRD = matSrc(Rect(col/2, row/2, col / 2, row / 2));
(2)进行四次模板匹配,得到四张result结果图。
结果图保存在matTemp中,其中参数5表示采用第五种匹配方法:系数匹配法TM_CCOOEFF。
matchTemplate(matLD, matTemplate, matTemp, 5);
(3)对matTemp进行归一化。
采用区间归一化,将像素值映射到[0,1]这个范围内。
normalize(matTemp, matTemp, 0, 1, NORM_MINMAX, -1, Mat());
(4)获取结果图中最大值的位置,并返回该位置。
minMaxLoc是用获取图像最大值,最小值,以及对应的位置。这里只需要获取最大值的位置,其他的可以设置成NULL。
minMaxLoc(matTemp, NULL, NULL, NULL, &maxLocLD,Mat());
anchorLD = Point(maxLocLD.x , maxLocLD.y+row/2);
anchorLT = maxLocLT;
anchorRT = Point(maxLocRT.x+col/2, maxLocRT.y);
anchorRD = Point(maxLocRD.x + col / 2, maxLocRD.y + row / 2);
(5)在原图中将最大的位置画出来。
//将获取的锚点画出来。
circle(matStand, anchorLT, 5, Scalar(0, 0, 255), 5);
circle(matStand, anchorLD, 5, Scalar(0, 0, 255), 5);
circle(matStand, anchorRT, 5, Scalar(0, 0, 255), 5);
circle(matStand, anchorRD, 5, Scalar(0, 0, 255), 5);
看一下整个模板匹配的效果。这里看一张正的图片和一张倾斜图片的匹配效果。
看一下局部匹配出来的效果图。这是matTemp的展示,边角最亮的应该就是匹配度最高的点。
matTemp的属性如下:W-w+1 =210-24+1=187.
H-h+1 = 185-24+1 = 162
所以matTemp的大小就是187*162。根据打印的matTemp的像素值,为32位的浮点型,但是带符号。
归一化后,就是0.0-1.0的范围。
总结下来,模板匹配的两步基本工作:匹配和归一化。