本文原始地址: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照相機定標和三維重建
下一節繼續學習