OpenCV 【十一】—— 图像去畸变,对极约束之undistort,initUndistortRectifyMap,undistort
目錄
0.極限約束,對極校正
1.攝像機成像原理簡述
2.成像畸變
2.1. 畸變數學模型
2.2. 公式推導
3.畸變校正
3.1. 理論推導
4. 圖像去畸變**
5. 圖像尺度縮放與內參的關系**
5.1 undistortPoints()
5.2 initUndistortRectifyMap()
5.3 undistort()
6.UndistortPoints源碼
?
0.極限約束,對極校正
?
?
1.攝像機成像原理簡述
成像的過程實質上是幾個坐標系的轉換。首先空間中的一點由 世界坐標系 轉換到 攝像機坐標系 ,然后再將其投影到成像平面 ( 圖像物理坐標系 ) ,最后再將成像平面上的數據轉換到圖像平面 ( 圖像像素坐標系 ) 。
圖像像素坐標系 (uOv坐標系) 下的無畸變坐標 (U, V),經過 經向畸變 和 切向畸變 后落在了uOv坐標系 的 (Ud, Vd) 上。即就是說,真實圖像 imgR 與 畸變圖像 imgD 之間的關系為: imgR(U, V) = imgD(Ud, Vd) 。
2.成像畸變
2.1. 畸變數學模型
攝像頭成像畸變的數學模型 (符合的對應關系有問題,可能會造成一些干擾,公式主要看后面推導的過程)
?
2.2. 公式推導
公式推導:
3.畸變校正
3.1. 理論推導
我們已知的是畸變后的圖像,要得到沒有畸變的圖像就要通過畸變模型推導其映射關系。 真實圖像 imgR 與 畸變圖像 imgD 之間的關系為: imgR(U, V) = imgD(Ud, Vd) 。通過這個關系,找出所有的 imgR(U, V) 。(U, V) 映射到 (Ud, Vd) 中的 (Ud, Vd) 往往不是整數 (U和V是整數,因為它是我們要組成圖像的像素坐標位置,以這正常圖像的坐標位置去求在畸變圖像中的坐標位置,取出對應的像素值,這也是正常圖像的像素值)。 但是畸變的像素往往不是整數,所以需要通過插值來進行求解,詳細見我之前的博客 [圖像]圖像縮放算法-雙線性內插法 。
?
?
?
4. 圖像去畸變**
圖像去畸變的思路是:對于目標圖像(無畸變)上的每個像素點,轉換到normalize平面,再進行畸變并投影到源圖像(帶畸變), 獲取原圖對應位置的像素值作為目標圖像該點的像素值。
這里容易有一個誤解,以為去畸變是對畸變圖像進行畸變逆變換得到無畸變圖像,實際不是的,畸變模型太復雜了,很難求逆變換,所以是將無畸變圖像進行畸變變換到原圖像去獲得對應像素值。
?
圖像去畸變流程如下:
注意:源相機和目標相機使用的內參矩陣不一定是一樣的。如果是調用opencv的undistort()函數,cameraMatrix是源相機的內參矩陣,newCameraMatrix是目標相機的內參矩陣,如果不設置newCameraMatrix,則默認與源相機內參一樣,即去畸變后,相機的內參矩陣不變。
5. 圖像尺度縮放與內參的關系**
結論:圖像分辨率縮放比例k, 相機焦距光心等比例縮放k, 畸變系數不變。
證明:圖像縮放k倍后,圖像平面所有的像素點坐標變為:
而圖像畸變是發生在normalize平面,不管圖像分辨率如何改變,normalize平面(只取決于焦距光心)是不變的,所以畸變系數不變。
?
5.1 undistortPoints()
1.1功能: 從觀測點坐標計算理想點坐標。
| void cv::undistortPoints | ( | InputArray | src, |
|---|---|---|---|
| ? | ? | OutputArray | dst, |
| ? | ? | InputArray | cameraMatrix, |
| ? | ? | InputArray | distCoeffs, |
| ? | ? | InputArray | R = noArray(), |
| ? | ? | InputArray | P = noArray() |
| ? | ) | ? | ? |
?
5.2 initUndistortRectifyMap()
2.1功能 Computes the undistortion and rectification transformation map. 計算去畸變和校正變換映射。
| void cv::initUndistortRectifyMap | ( | InputArray | cameraMatrix, |
|---|---|---|---|
| ? | ? | InputArray | distCoeffs, |
| ? | ? | InputArray | R, |
| ? | ? | InputArray | newCameraMatrix, |
| ? | ? | Size | size, |
| ? | ? | int | m1type, |
| ? | ? | OutputArray | map1, |
| ? | ? | OutputArray | map2 |
| ? | ) | ? | ? |
模型見4去畸變
5.3 undistort()
| void cv::undistort | ( | InputArray | src, |
|---|---|---|---|
| ? | ? | OutputArray | dst, |
| ? | ? | InputArray | cameraMatrix, |
| ? | ? | InputArray | distCoeffs, |
| ? | ? | InputArray | newCameraMatrix = noArray() |
| ? | ) | ? | ? |
3.1 功能 Transforms an image to compensate for lens distortion. , 對圖像進行變換以補償鏡頭失真。
The function transforms an image to compensate radial and tangential lens distortion.
The function is simply a combination of cv::initUndistortRectifyMap (with unity R ) and cv::remap (with bilinear interpolation). See the former function for details of the transformation being performed.
6.UndistortPoints源碼
?
void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvMat* _cameraMatrix,const CvMat* _distCoeffs,const CvMat* matR, const CvMat* matP, cv::TermCriteria criteria)
{// 判斷迭代條件是否有效CV_Assert(criteria.isValid());// 定義中間變量--A相機內參數組,和matA共享內存;RR-矯正變換數組,和_RR共享內存// k-畸變系數數組double A[3][3], RR[3][3], k[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0};CvMat matA=cvMat(3, 3, CV_64F, A), _Dk;CvMat _RR=cvMat(3, 3, CV_64F, RR);cv::Matx33d invMatTilt = cv::Matx33d::eye();cv::Matx33d matTilt = cv::Matx33d::eye();// 檢查輸入變量是否有效CV_Assert( CV_IS_MAT(_src) && CV_IS_MAT(_dst) &&(_src->rows == 1 || _src->cols == 1) &&(_dst->rows == 1 || _dst->cols == 1) &&_src->cols + _src->rows - 1 == _dst->rows + _dst->cols - 1 &&(CV_MAT_TYPE(_src->type) == CV_32FC2 || CV_MAT_TYPE(_src->type) == CV_64FC2) &&(CV_MAT_TYPE(_dst->type) == CV_32FC2 || CV_MAT_TYPE(_dst->type) == CV_64FC2));CV_Assert( CV_IS_MAT(_cameraMatrix) &&_cameraMatrix->rows == 3 && _cameraMatrix->cols == 3 );cvConvert( _cameraMatrix, &matA );// _cameraMatrix <--> matA / A// 判斷輸入的畸變系數是否有效if( _distCoeffs ){CV_Assert( CV_IS_MAT(_distCoeffs) &&(_distCoeffs->rows == 1 || _distCoeffs->cols == 1) &&(_distCoeffs->rows*_distCoeffs->cols == 4 ||_distCoeffs->rows*_distCoeffs->cols == 5 ||_distCoeffs->rows*_distCoeffs->cols == 8 ||_distCoeffs->rows*_distCoeffs->cols == 12 ||_distCoeffs->rows*_distCoeffs->cols == 14));_Dk = cvMat( _distCoeffs->rows, _distCoeffs->cols,CV_MAKETYPE(CV_64F,CV_MAT_CN(_distCoeffs->type)), k);// _Dk和數組k共享內存指針cvConvert( _distCoeffs, &_Dk );if (k[12] != 0 || k[13] != 0){cv::detail::computeTiltProjectionMatrix<double>(k[12], k[13], NULL, NULL, NULL, &invMatTilt);cv::detail::computeTiltProjectionMatrix<double>(k[12], k[13], &matTilt, NULL, NULL);}}if( matR ){CV_Assert( CV_IS_MAT(matR) && matR->rows == 3 && matR->cols == 3 );cvConvert( matR, &_RR );// matR和_RR共享內存指針}elsecvSetIdentity(&_RR);if( matP ){double PP[3][3];CvMat _P3x3, _PP=cvMat(3, 3, CV_64F, PP);CV_Assert( CV_IS_MAT(matP) && matP->rows == 3 && (matP->cols == 3 || matP->cols == 4));cvConvert( cvGetCols(matP, &_P3x3, 0, 3), &_PP );// _PP和數組PP共享內存指針cvMatMul( &_PP, &_RR, &_RR );// _RR=_PP*_RR 放在一起計算比較高效}const CvPoint2D32f* srcf = (const CvPoint2D32f*)_src->data.ptr;const CvPoint2D64f* srcd = (const CvPoint2D64f*)_src->data.ptr;CvPoint2D32f* dstf = (CvPoint2D32f*)_dst->data.ptr;CvPoint2D64f* dstd = (CvPoint2D64f*)_dst->data.ptr;int stype = CV_MAT_TYPE(_src->type);int dtype = CV_MAT_TYPE(_dst->type);int sstep = _src->rows == 1 ? 1 : _src->step/CV_ELEM_SIZE(stype);int dstep = _dst->rows == 1 ? 1 : _dst->step/CV_ELEM_SIZE(dtype);double fx = A[0][0];double fy = A[1][1];double ifx = 1./fx;double ify = 1./fy;double cx = A[0][2];double cy = A[1][2];int n = _src->rows + _src->cols - 1;// 開始對所有點開始遍歷for( int i = 0; i < n; i++ ){double x, y, x0 = 0, y0 = 0, u, v;if( stype == CV_32FC2 ){x = srcf[i*sstep].x;y = srcf[i*sstep].y;}else{x = srcd[i*sstep].x;y = srcd[i*sstep].y;}u = x; v = y;x = (x - cx)*ifx;//轉換到歸一化圖像坐標系(含有畸變)y = (y - cy)*ify;//進行畸變矯正if( _distCoeffs ) {// compensate tilt distortion--該部分系數用來彌補沙氏鏡頭畸變??// 如果不懂也沒管,因為普通鏡頭中沒有這些畸變系數cv::Vec3d vecUntilt = invMatTilt * cv::Vec3d(x, y, 1);double invProj = vecUntilt(2) ? 1./vecUntilt(2) : 1;x0 = x = invProj * vecUntilt(0);y0 = y = invProj * vecUntilt(1);double error = std::numeric_limits<double>::max();// error設定為系統最大值// compensate distortion iteratively// 迭代去除鏡頭畸變// 迭代公式 ? x′= (x?2p1 xy?p2 (r^2 + 2x^2))∕( 1 + k1*r^2 + k2*r^4 + k3*r^6)// ? ? ? ? ? ? y′= (y?2p2 xy?p1 (r^2 + 2y^2))∕( 1 + k1*r^2 + k2*r^4 + k3*r^6)for( int j = 0; ; j++ ){if ((criteria.type & cv::TermCriteria::COUNT) && j >= criteria.maxCount)// 迭代最大次數為5次break;if ((criteria.type & cv::TermCriteria::EPS) && error < criteria.epsilon)// 迭代誤差閾值為0.01break;double r2 = x*x + y*y;double icdist = (1 + ((k[7]*r2 + k[6])*r2 + k[5])*r2)/(1 + ((k[4]*r2 + k[1])*r2 + k[0])*r2);double deltaX = 2*k[2]*x*y + k[3]*(r2 + 2*x*x)+ k[8]*r2+k[9]*r2*r2;double deltaY = k[2]*(r2 + 2*y*y) + 2*k[3]*x*y+ k[10]*r2+k[11]*r2*r2;x = (x0 - deltaX)*icdist;y = (y0 - deltaY)*icdist;// 對當前迭代的坐標加畸變,計算誤差error用于判斷迭代條件if(criteria.type & cv::TermCriteria::EPS){double r4, r6, a1, a2, a3, cdist, icdist2;double xd, yd, xd0, yd0;cv::Vec3d vecTilt;r2 = x*x + y*y;r4 = r2*r2;r6 = r4*r2;a1 = 2*x*y;a2 = r2 + 2*x*x;a3 = r2 + 2*y*y;cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6;icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6);xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;vecTilt = matTilt*cv::Vec3d(xd0, yd0, 1);invProj = vecTilt(2) ? 1./vecTilt(2) : 1;xd = invProj * vecTilt(0);yd = invProj * vecTilt(1);double x_proj = xd*fx + cx;double y_proj = yd*fy + cy;error = sqrt( pow(x_proj - u, 2) + pow(y_proj - v, 2) );}}}// 將坐標從歸一化圖像坐標系轉換到成像平面坐標系double xx = RR[0][0]*x + RR[0][1]*y + RR[0][2];double yy = RR[1][0]*x + RR[1][1]*y + RR[1][2];double ww = 1./(RR[2][0]*x + RR[2][1]*y + RR[2][2]);x = xx*ww;y = yy*ww;if( dtype == CV_32FC2 ){dstf[i*dstep].x = (float)x;dstf[i*dstep].y = (float)y;}else{dstd[i*dstep].x = x;dstd[i*dstep].y = y;}}
}
簡化版ubdistortpoint
//for (size_t u = 0; u < ir_image_height; u++)//{// for (size_t v = 0; v < ir_image_width; v++)// {//(u,v) undistort// float x = (u - cx) * fx_inv;// float y = (v - cy) * fy_inv;
?// float r2 = (x*x + y*y);// float r = std::sqrt(r2);// float r4 = r2 * r2;// float x_distort = x*(1 + k1*r2 + k2 * r4) + 2 * p1*x*y + p2*(r2 + 2 * x*x);// float y_distort = y*(1 + k1*r2 + k2 * r4) + p1*(r2 + 2 * y*y) + 2 * p2*x*y;
?// float X = ir_depth_rx.at<float>(0, 0) * x_distort + ir_depth_rx.at<float>(0, 1)*y_distort +ir_depth_rx.at<float>(0, 2) * 1;// float Y = ir_depth_rx.at<float>(1, 0) * x_distort + ir_depth_rx.at<float>(1, 1)*y_distort +ir_depth_rx.at<float>(1, 2) * 1;// float W = ir_depth_rx.at<float>(2, 0) * x_distort + ir_depth_rx.at<float>(2, 1)*y_distort +ir_depth_rx.at<float>(2, 2) * 1;// // float x_camera = X / W;// float y_camera = Y / W;// // float u_distort = fx*x_camera + cx;// float v_distort = fy*y_camera + cy;
?// calib_params->updated_ir_depth_forward_map_x->operator()(v, u) = u_distort;// calib_params->updated_ir_depth_forward_map_y->operator()(v, u) = v_distort;
?// }//}
?
?
總結
以上是生活随笔為你收集整理的OpenCV 【十一】—— 图像去畸变,对极约束之undistort,initUndistortRectifyMap,undistort的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最便宜的电动车多少钱
- 下一篇: 求一个阳字在尾好听的名字。