赞
踩
很久没有写博客了,最近在学习计算机视觉的相关知识,于是写了一个AR的小Demo。
该程序通过OpenCV实现对Marker的识别和定位,然后通过OpenGL将虚拟物体叠加到摄像头图像下,实现增强现实。首先来看看我们使用的Marker:
这是众多Marker中的一个,它们都被一圈的黑色边框所包围,边框之中是编码信息,白色代表1,黑色代表0。将每一行作为一个字,那么每个字有5bits。其中,1、3、5位为校验位,2、4位为信息位。也就是说,整个Marker的信息位只有10bits,所以最多可表示1024个数(0~1023)。这种编码方式实际上是汉明码的变种,唯一区别在于它的首位是对应汉明码首位的反(比如汉明码是00000,那么Marker中的编码为10000)。这么做的目的是防止某一行全黑,从而提高识别率。汉明码还有另一大优势——不具有旋转对称性,因此程序能通过汉明码确定Marker的方向,因此从Marker中解码的信息是唯一的。
一、Marker的检测与识别
我们首先实现一个类,用于检测图像中的Marker,解码信息,并计算Marker相对于摄像头的坐标位置。
检测部分比较简单。首先将输入图像进行灰度变换,然后对灰度图像进行自适应二值化。之所以使用自适应二值化,是因为它能更好的适应光照的变化。但有一点要注意,很多朋友使用自适应二值化后表示得到的结果很像边缘检测的结果,那是因为自适应窗口过小造成的。使用自适应二值化时,窗口的大小应大于二值化目标的大小,否则得到的阈值不具有适应性。在自适应二值化之后,为了消除噪音或小块,可以加以形态学开运算。以上几部可分别得到下列图像(其中二值化的结果经过了反色处理,方便以后的轮廓提取)。
得到二值图像后,就可以使用OpenCV中的findContours来提取轮廓了。一副二值图像当中的轮廓有很多,其中有一些轮廓很小,我们通过一个阈值将这些过小的轮廓排除。排除过小轮廓后,就可以对轮廓进行多边形近似了。由于我们的Marker是正方形,其多边形近似结果应该满足以下条件:
1、只有4个顶点
2、一定是凸多边形
3、每一个边的长度不能过小
通过以上几个条件,我们可以排除绝大部分轮廓,从而找到最有可能为Marker的部分。找到这样的候选轮廓后,我们将它的多边形四个顶点保存下来,并做适当的调整,使所有顶点逆时针排序。代码如下:
- void MarkerRecognizer::markerDetect(Mat& img_gray, vector<Marker>& possible_markers, int min_size, int min_side_length)
- {
- Mat img_bin;
-
- int thresh_size = (min_size/4)*2 + 1;
- adaptiveThreshold(img_gray, img_bin, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, thresh_size, thresh_size/3);
- //threshold(img_gray, img_bin, 125, 255, THRESH_BINARY_INV|THRESH_OTSU);
- morphologyEx(img_bin, img_bin, MORPH_OPEN, Mat()); //use open operator to eliminate small patch
-
- vector<vector<Point>> all_contours;
- vector<vector<Point>> contours;
- findContours(img_bin, all_contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
-
- for (int i = 0; i < all_contours.size(); ++i)
- {
- if (all_contours[i].size() > min_size)
- {
- contours.push_back(all_contours[i]);
- }
- }
-
- vector<Point> approx_poly;
- for (int i = 0; i < contours.size(); ++i)
- {
- double eps = contours[i].size()*APPROX_POLY_EPS;
- approxPolyDP(contours[i], approx_poly, eps, true);
-
- if (approx_poly.size() != 4)
- continue;
-
- if (!isContourConvex(approx_poly))
- continue;
-
- //Ensure that the distance between consecutive points is large enough
- float min_side = FLT_MAX;
- for (int j = 0; j < 4; ++j)
- {
- Point side = approx_poly[j] - approx_poly[(j+1)%4];
- min_side = min(min_size, side.dot(side));
- }
- if (min_side < min_side_length*min_side_length)
- continue;
-
- //Sort the points in anti-clockwise
- Marker marker = Marker(0, approx_poly[0], approx_poly[1], approx_poly[2], approx_poly[3]);
- Point2f v1 = marker.m_corners[1] - marker.m_corners[0];
- Point2f v2 = marker.m_corners[2] - marker.m_corners[0];
- if (v1.cross(v2) > 0) //由于图像坐标的Y轴向下,所以大
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。