当前位置: 首页 > news >正文

opencv 去畸变

opencv迭代去畸变算法

  • 函数简介
    opencv中函数undistortPoints()用于对图像点坐标进行去畸变,以下为该函数解释:
void undistortPoints(InputArray src, OutputArray dst, InputArray cameraMatrix, InputArray distCoeffs, InputArray R=noArray(), InputArray P=noArray())

src-原图像坐标;dst-输出图像坐标;cameraMatrix-相机内参矩阵;distCoeffs-畸变系数,有四种畸变模型,分别含有4,5,8个元素,通常使用具有4/5个参数的模型,如果该向量为NULL,那么设定该图像没有畸变;R-相机坐标系的矫正矩阵(即对相机坐标系的位姿调整,见stereoRectify函数中的Rl,Rr),如果矩阵为空,那么默认使用单位矩阵;P-新的相机矩阵(3x3)或者新的投影矩阵(3x4,包含相机坐标系相对世界坐标系的相对位姿,见stereoRectify函数中的Pl,Pr),如该矩阵为空的话,将设置该矩阵为单位阵。

  • 源码分析
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共享内存指针 } else cvSetIdentity(&_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.01 break; 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; } } }

该函数所实现的迭代算法很简单,只要了解相机的畸变模型就可以很容易理解函数的实现过程了。

http://www.jsqmd.com/news/814385/

相关文章:

  • Web3开发者技能图谱:从智能合约到dApp全栈实战指南
  • 【RT-DETR实战】024、NCNN框架部署RT-DETR实战:从模型导出到端侧推理的踩坑实录
  • 为AI助手打造企业级FTP/SFTP操作引擎:告别重复脚本,实现智能文件部署
  • MiGPT小爱音箱AI升级终极指南:5步快速接入ChatGPT和豆包大模型
  • 2026年压力机行业东莞市锐联智能装备有限公司:深耕多年的优选服务商 - 速递信息
  • 新手必看:PCB设计全流程详解
  • 驾驶式工业扫地车哪家好?从客户案例看品牌真实口碑 - 速递信息
  • 2026 济南名牌手表回收全攻略|靠谱商家+避坑技巧+无损检测 - 奢侈品回收测评
  • 如何在Mac上免费实现NTFS磁盘完整读写:终极解决方案指南
  • 2026年高薪IT证书盘点:CDA数据分析师如何突破35岁职业瓶颈
  • 社交媒体运营实战指南:从策略定位到数据分析的完整闭环
  • 在 GitHub 之前:那些塑造了现代软件开发的版本控制往事
  • 哈尔滨保险拒赔律师推荐 新沃李晓伟团队12年专业经验 - 铅笔写好字
  • Kaggle竞赛实战技能库:模块化工具箱提升数据科学工作流
  • 保姆级教程:华为AC+瘦AP旁挂组网,从交换机配置到WiFi下发全流程(含Option 43详解)
  • 如何用JPEXS Flash反编译工具轻松解密和编辑SWF文件:5个必备技巧
  • 【YOLO目标检测全栈实战】23 小目标检测的“显微镜”:用超分头+联合训练突破像素极限
  • 3步掌握Blender 3MF插件:构建高效3D打印工作流
  • PCB线路板多少钱 - 工业设备
  • LinkSwift网盘直链下载助手:八大平台高速下载解决方案
  • Ai2Psd:AI转PSD图层转换的终极解决方案
  • Windows窗口置顶工具PinWin:5分钟实现高效多任务处理的终极指南
  • 多效蒸发器哪个品牌评价好又受欢迎?2026质量与实力双强企业盘点 - 品牌推荐大师1
  • ClawShelf:打造精准可控的本地媒体库元数据管理方案
  • 从前装定点到系统重构:深度解析RF RACER碳陶制动标准化体系 - RF_RACER
  • Zotero PDF Translate:一站式文献翻译终极指南,让外文阅读不再困难
  • LLM-PDF开源工具:高质量文档解析与结构化处理实战指南
  • 开源记忆增强工具claudemem:基于向量数据库的开发者知识库实践
  • 2026年液压登车桥性价比排名,靠谱的登车桥厂家 - 工业设备
  • 多效蒸发器知名生产厂家|行业标杆企业与推荐制造商,知名品牌排名解读 - 品牌推荐大师1