基于标记的AR的opencv实现(一)

最近学习AR,买了本Mastering  OpenCV,这书上有两个AR的例子,这里先分析的是第二章基于标识的AR,书中是使用Xcode给iphone或者ipad写的,本文是在linux系统上vim实现的,终端模式。
先推荐两个前辈的博客,本文参考了二者和书进行理解源码。
http://blog.csdn.net/jinshengtao/article/details/48604435  taotao1233写的,大部分是基于他的博客,称转载也可以。
http://blog.csdn.net/acorld/article/details/8747813  missjuan写的,就是分析在xcode上进行实现的。

我的程序源码已经上传到http://download.csdn.net/detail/chuhang_zhqr/9298975,有需要的请下载。
以下开始进行分析:
程序大体框架:
1:对输入图像帧进行标记检测,这里包括,灰度化,找到图像中轮廓,搜索可能的标记;检测和解码标记,
2:估计标记的三维姿态,这里包括提前对摄像机进行相机标定,获取相机内参数和失真系数,根据这个计算出标记的旋转矩阵和平移矩阵。
3:由相机内参数和标记的旋转矩阵和平移矩阵,用OpenGL进行渲染三维物体;
以上是实现AR的必经之路,我认为OpenCV实现就已经很底层了,再底层那就太麻烦了,在科研的道路上,工程化实现未尝不可。
因为是刚把源码实现了,没来及完善工程,决定趁热打铁先记录一下心路历程。

现在开始分析源码,从第一步开始,输入图像:输入图像帧无非三种源,图片,视频,摄像头。和书上一样先使用图片吧。就是这个图片源图片
书上的不是640x480的,我修了一下。src = imread("1.jpg");
1):灰度化处理:
必须将输入图像转换为灰度图,因为标识仅包含黑白块,这使得更容易在灰度图像上操作这些块。

  //彩色转换成灰色图像
  cvtColor(src,grayscale,CV_BGRA2GRAY);
}

这个没什么好说的,这里写图片描述
2):执行二值化阈值操作:
将图像的每个像素变成黑色或白色,为检测轮廓做准备,使用合理的自适应阈值法,减小光照条件和软强度变化影响。以需要二值化的像素为中心,将给定半径内的所有像素的平均强度作为该像素的强度,使轮廓检测更具有鲁棒性。

adaptiveThreshold(grayscale,//Input Image
                    thresholdImg,//Result binary image
                    255,
                    ADAPTIVE_THRESH_GAUSSIAN_C,
                    THRESH_BINARY_INV,
                    7,
                    7
                    );
  /*输入图像  
  //输出图像  
  //使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值  
  //自适应阈值算法使用:CV_ADAPTIVE_THRESH_MEAN_C 或 CV_ADAPTIVE_THRESH_GAUSSIAN_C   
  //取阈值类型:必须是下者之一  
  //CV_THRESH_BINARY,  
  //CV_THRESH_BINARY_INV  
  //用来计算阈值的象素邻域大小: 3, 5, 7, ...  
  */  

二值化图片
3):轮廓检测:
使用findContours检测输入图像的轮廓。

//检测所输入的二值图像的轮廓,返回一个多边形列表,其每个多边形标识一个轮廓,小轮廓不关注,不包括标记...
void MarkerDetector::findContour(cv::Mat& thresholdImg, ContoursVector& contours, int minContourPointsAllowed) const
{
  ContoursVector allContours;
  /*输入图像image必须为一个2值单通道图像  
  //检测的轮廓数组,每一个轮廓用一个point类型的vector表示  
  //轮廓的检索模式  

     CV_RETR_EXTERNAL表示只检测外轮廓 
     CV_RETR_LIST检测的轮廓不建立等级关系 
     CV_RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。 
     CV_RETR_TREE建立一个等级树结构的轮廓。具体参考contours.c这个demo 

  //轮廓的近似办法  

     CV_CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1 
     CV_CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息 
     CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法 
     offset表示代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。 
   */  
  findContours(thresholdImg, allContours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);

  contours.clear();
  for(size_t i=0;i<allContours.size();i++)
    {
      int contourSize = allContours[i].size();
      if(contourSize > minContourPointsAllowed)
        {
          contours.push_back(allContours[i]);
        }
    }
 //Mat result(src.size(),CV_8U,Scalar(0)); 
//drawContours(result,detectedMarkers,-1,Scalar(255),2);
  //imshow("AR based marker...",result);
  }

其函数返回值为一个多边形列表,其每个多边形都表示一个轮廓。若轮廓包含的像素数比minContourPointsAllowed还小,则是一个小轮廓,这里不感兴趣,直接去除,这些小轮廓可能并没有包含标记。
检测轮廓

4):搜索候选标记:
在找到轮廓后,将开始进行多边形逼近,这样做为了减少轮廓的像素。因为标记总是被四个顶点的多边形包含,如果不是四个,就不是我们想要的标记。筛选出非标记区域。
用Opencv内置API检测多边形,通过判断多边形定点数量是否为4,四边形各顶点之间相互距离是否满足要求(四边形是否足够大),过滤非候选区域。然后再根据候选区域之间距离进一步筛选,得到最终的候选区域,并使得候选区域的顶点坐标逆时针排列。
a. 四边形顶点之间距离
这里写图片描述
对每个四边形S,计算其相邻顶点之间的距离:
上式中i,j为相邻的两个顶点,若顶点之间的最小值仍大于阈值,则保留该四边形S进行下一步判断。

这里写图片描述
b. 四边形之间距离

求四边形S和S’之间的距离,即计算四个顶点之间的平均距离:

这里写图片描述

若dist小于阈值,则四边形S和S’距离较近,记录到tooNearCandidates向量里。接来下perimeter函数分别求四边形S和S’四个顶点之间的距离和,保留距离较大的,将距离较小的放入removalMask数组中,下式中i,j为四边形内相邻顶点
这里写图片描述

c. 行列式的几何意义—逆时针排序

行列式是由一些数据排列成的方阵经过规定的计算方法而得到的一个数。那它的几何意义是什么呢?有两种解释:

一个是行列式就是行列式中的行或列向量所构成的超平行多面体的有向面积或有向体积;
另一个是矩阵A的行列式detA就是线性变化A下的图形面积或体积的伸缩因子;
接下来的代码中,由于在approxPolyDP寻找多边形时,顶点摆放次序有逆时针和顺时针两种,我们希望这些顶点按照逆时针摆放。因此,对于四边形而言,我们只讨论2*2行列式对应的有向面积。

一个2×2矩阵A的行列式,是A的行向量(或列向量)决定的平行四边形的有向面积。用几何观点来看,二阶行列式D是XOY平面上以行向量a=(a1,a2),b=(b1,b2)为邻边的平行

这里写图片描述

四边形的有向面积。若这个平行四边形是由向量a沿逆时针方向转到b而得到的,面积取正值;若这个平行四边形是由向量a沿顺时针方向转到而得到的,面积取负值。本例中,对于顺时针摆放的顶点0,1,2,3,咱可以通过计算有0-1,0-2构成的向量计算其有向面积。如果是顺时针摆放,那么该有向面积一定是负数,只要交换1,3位置即可。
这里写图片描述

//由于我们的标记是四边形,当找到图像所有轮廓细节后,本文用Opencv内置API检测多边形,通过判断多边形定点数量是否为4,四边形各顶点之间相互距离是否满足要求(四边形是否足够大),过滤非候选区域。然后再根据候选区域之间距离进一步筛选,得到最终的候选区域,并使得候选区域的顶点坐标逆时针排列。
void MarkerDetector::findCandidates(const ContoursVector& contours,vector<Marker>& detectedMarkers)
{
  vector<Point> approxCurve;//返回结果为多边形,用点集表示//相似形状
  vector<Marker> possibleMarkers;//可能的标记

 
  • 12
    点赞
  • 104
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
基于标记AR(Augmented Reality)是一种将虚拟信息与现实世界进行结合的技术,通过识别特定的标记,将虚拟信息叠加到标记上,使其看起来像是标记本身具有的属性。在OpenCV实现基于标记AR可以分为以下几个步骤: 1. 检测标记:使用相机采集图像,并通过图像处理技术来检测标记OpenCV提供了基于角点检测和二维码检测等多种方法来检测标记。 2. 计算相机姿态:将检测到的标记与其模型进行匹配,计算相机在三维空间中的姿态。OpenCV提供了多种方法来计算相机姿态,如EPnP算法、PnP算法等。 3. 渲染虚拟信息:根据相机姿态和标记模型,将虚拟信息渲染到图像上。OpenCV提供了OpenGL接口来进行渲染。 下面是一个基于标记AROpenCV实现的示例代码: ```python import cv2 import numpy as np # 定义标记模型顶点坐标 marker_coords = np.float32([[-1, 1, 0], [1, 1, 0], [1, -1, 0], [-1, -1, 0]]) # 定义标记模型面 marker_faces = np.array([[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 2, 3]], dtype=np.int32) # 加载标记图像 marker_image = cv2.imread('marker.png', cv2.IMREAD_GRAYSCALE) # 定义相机内参矩阵 camera_matrix = np.array([[1000, 0, 640], [0, 1000, 360], [0, 0, 1]]) # 定义畸变系数 dist_coeffs = np.zeros((4, 1)) # 创建OpenGL窗口 cv2.namedWindow('AR', cv2.WINDOW_OPENGL) # 初始化OpenGL cv2.setOpenGlContext('AR') cv2.opengl_init(1280, 720) # 加载虚拟模型 obj_model = cv2.imread('obj_model.png', cv2.IMREAD_UNCHANGED) while True: # 采集图像 ret, frame = cv2.VideoCapture(0).read() # 检测标记 marker_corners = cv2.findChessboardCorners(marker_image, (4, 4))[1] if marker_corners is not None: # 计算相机姿态 _, rvec, tvec = cv2.solvePnP(marker_coords, marker_corners, camera_matrix, dist_coeffs) # 渲染虚拟信息 obj_points, _ = cv2.projectPoints(marker_coords, rvec, tvec, camera_matrix, dist_coeffs) obj_points = obj_points.astype(np.int32) cv2.drawContours(frame, [obj_points[marker_faces[i]], obj_points[marker_faces[i + 1]], obj_points[marker_faces[i + 2]]], -1, (0, 255, 0), thickness=cv2.FILLED) # 将虚拟模型渲染到OpenGL窗口 cv2.opengl_draw_texture(obj_model, (0, 0, 640, 360)) # 显示图像 cv2.imshow('AR', frame) # 等待按键 key = cv2.waitKey(1) if key == 27: break # 关闭窗口 cv2.destroyAllWindows() ``` 需要注意的是,这只是一个基本的示例代码,实际应用中还需要对检测标记和计算相机姿态等步骤进行优化和改进,以提高精度和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值