OpenGL与OpenCV实现增强现实

很久没有写博客了,最近在学习计算机视觉的相关知识,于是写了一个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轴向下,所以大
  • 29
    点赞
  • 137
    收藏
    觉得还不错? 一键收藏
  • 36
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值