OpenCV for Ios 学习笔记(7)-标记姿态的模拟

来源:互联网 发布:cab软件下载网 编辑:程序博客网 时间:2024/05/18 09:12

本文原始地址:OpenCV for Ios 学习笔记(7)-标记姿态的模拟


在前面,我们已经获取到了精确的标记角点,我们能够模拟出相机与3维空间中标记之间的变换。这个过程中我们将会在相机与物体之间发现欧式变换-只包含旋转和转换。



其中,C是表示为相机的中心,P1-P4是世界坐标轴的3维的点,p1-p4是它们在相机图像平面的投影。我们的目的是使用内在的矩阵和在图像平面已知的点去找出3维空间已知标记的位置与相机C之间的转换。

但是我们怎么获得标记在3维空间的位置坐标呢?因为我们的标记总是正方形且所有的顶点都在同一平面,因此我们能够像下面一样定义它们的角点:



现在我们把我们的标记置于xy坐标系(即z=0),将标记的中心点置于(0,0,0)处。这是一种好的提示,因为我们左边系统的起点就是我们标记的中心点。

为了通过2维-3维的点集找出相机的位置,我们使用solvePnP。

solvePnP的一种实现:

void cv::solvePnP( InputArray _opoints, InputArray _ipoints,                  InputArray _cameraMatrix, InputArray _distCoeffs,                  OutputArray _rvec, OutputArray _tvec, bool useExtrinsicGuess ){    Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();    int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));    CV_Assert( npoints >= 0 && npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );        _rvec.create(3, 1, CV_64F);    _tvec.create(3, 1, CV_64F);    Mat cameraMatrix = _cameraMatrix.getMat(), distCoeffs = _distCoeffs.getMat();    CvMat c_objectPoints = opoints, c_imagePoints = ipoints;    CvMat c_cameraMatrix = cameraMatrix, c_distCoeffs = distCoeffs;    CvMat c_rvec = _rvec.getMat(), c_tvec = _tvec.getMat();    cvFindExtrinsicCameraParams2(&c_objectPoints, &c_imagePoints, &c_cameraMatrix,                                 c_distCoeffs.rows*c_distCoeffs.cols ? &c_distCoeffs : 0,                                 &c_rvec, &c_tvec, useExtrinsicGuess );}

_opoints是对象坐标系的对象点集数组,应该是std::vector<cv::Point3f>对象,此处我们传标记的3维坐标系(4个点的集合)。

_ipoints是对象所对应的图像点(投影)数组。参数应该是std::vector<cv::Point2f> 或者 cv::Mat- 2 x N / N x 2,其中N是点的数量,这里我们传递我们发现的标记的角点。

_cameraMatrix:是相机的内参矩阵。

_distCoeffs:这是输入4 x 1,1×4、5 x 1或1 x 5向量的畸变系数(k1,k2,p1,p2,[k3])。如果它是空的,所有的畸变系数设置为0。

_rvec是输出把点从模型坐标系转换到相机坐标系的旋转向量。

_tvec同上,这里是输出平移向量。

useExtrinsicGuess如果是true,那么这个函数就会使用_rvec和_tvec分别作为初始的近似旋转和平移向量,然后再进一步优化。


我们用这个函数去计算相机转换将最大限度减少投影误差,也就是观察到的投影和预计的投影之间的距离平方和。

估计的转换是由旋转(rotation)(_rvec)和转换(translation)组件(_tvec)构成。这也就是所谓的欧氏变换或刚性变换。

刚性变换被定义为当一个装换作用在任何向量v,产生转换向量T(v)的形式:

T(v) = R v + t

RT=R-1(即R是一个正交变换),t是原始转换的向量,一个刚性转换满足:

det(R) = 1

这意味着R不产生反射,因此它代表一个旋转(一个保持定向正交变换)。

为了获得一个3×3旋转矩阵的旋转向量,我们将使用cv::Rodrigues 。该函数通过旋转的向量转换一个旋转参数并返回其等效旋转矢量旋转矩阵。

注:因为上面的solvePnP函数找到了相机相对于3维空间中的标记的位置,因此我们必须转换我们的成果。因此我们将得到的转换将在相机坐标系中描述标记的转换,这显然对渲染引擎更加友好。

下面是estimatePosition,作用是找到上面监测到的标记的位置

void estimatePosition(std::vector<Marker>& detectedMarkers){    for (size_t i = 0; i < detectedMarkers.size(); i++)    {        Marker &m = detectedMarkers[i];        cv::Mat Revc;        cv::Mat_<float> Tvec;        cv::Mat raux,taux;        cv::solvePnP(m_markerCorners3d, m.points, camMatrix, distCoeff, Revc, Tvec);        raux.convertTo(Revc, CV_32F);        taux.convertTo(Tvec, CV_32F);                cv::Mat_<float> rotMat(3,3);        //进行旋转矩阵和旋转向量间的转换        cv::Rodrigues(Revc, rotMat);                m.transformation = Transformation();        for (int col = 0; col < 3; col++)        {            for (int row = 0; row < 3; row ++)            {                //拷贝旋转的元素                m.transformation.r().mat[row][col] = rotMat(row,col);            }            //拷贝转移的元素            m.transformation.t().data[col] = Tvec(col);        }                m.transformation = m.transformation.getInverted();    }}

渲染3维虚拟物体

此时,你已经知道如何去发现图像上的标记并计算它们在空间中相对于相机的准确位置。是时候去画点东西了。就像刚才说的,我们将会使用opengl函数渲染场景,3维虚拟化是增强现实核心的部分,opengl提供了所有基本的条件去创

建高质量的渲染。

可参考:Cv照相机定标和三维重建


下一节继续学习