# 畸变矫正_超实用!图像去畸变矫正及双线性内插法

点击上方“3D视觉工坊”,选择“星标”

干货第一时间送达

ffc071415df9b3928cdcaea1b324b17d.png

通过采集的图像我们可以得到畸变后的图像,要得到没有畸变的图像要通过畸变模型推导其映射关系。 真实图像 imgR 与 畸变图像 imgD 之间的关系为: imgR(U, V) = imgD(Ud, Vd) 。遍历所有(U,V)填充为映射对应的(Ud,Vd)即可实现图像去畸变处理。 前提条件 是:已经得知相机内参K以及畸变参数k1,k2,k3,p1,p2,这部分可以由matlab工具箱实现。

ee69b730ff073d659de3ba810b0cbdab.png

接下来,我们将详细介绍如何去除图像的畸变。

1. 图像去畸变代码

br/<		Mat image = cv::imread(image_name, 0);   // 图像是灰度图,CV_8UC1>br/<		int rows = image.rows, cols = image.cols;>br/<		Mat image_undistort = Mat(rows, cols, CV_8UC1);   // 去畸变以后的图>br/< >br/<		// 计算去畸变后图像的内容image_undistort>br/<		for (int v = 0; v < rows; v  )>br/<			for (int u = 0; u < cols; u  ) {>br/<			double u_distorted = 0, v_distorted = 0;>br/< >br/<			double x1, y1, x2, y2;>br/<			//未畸变像素平面到成像平面>br/<			x1 = (u - cx) / fx;>br/<			y1 = (v - cy) / fy;>br/<			double r2;>br/<			//成像平面畸变后>br/<			r2 = pow(x1, 2)   pow(y1, 2);>br/<			x2 = x1*(1   k1*r2   k2*pow(r2, 2))   2 * p1*x1*y1   p2*(r2   2 * x1*x1);>br/<			y2 = y1*(1   k1*r2   k2*pow(r2, 2))   p1*(r2   2 * y1*y1)   2 * p2*x1*y1;>br/<			//畸变的成像平面到像素平面>br/<			u_distorted = fx*x2   cx;>br/<			v_distorted = fy*y2   cy;>br/<			// 赋值 (最近邻插值)>br/<			if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {>br/<				image_undistort.at(v, u) = image.at((int)v_distorted, (int)u_distorted);>br/<			}>br/<			else {>br/<				image_undistort.at(v, u) = 0;>br/<			}>br/<			}>br/<		// 画图去畸变后图像>br/<		char outname[100];>br/<		sprintf_s(outname, "./image1/%d.jpg", num);>br/<		cv::imwrite(outname,image_undistort);>br/<	}>br/< >br/<	cv::waitKey();>br/<	return 0;>br/< >br/#include #include #include using namespace std;using namespace cv;int main(int argc, char **argv) {// 内参double k1 = -0.3630, k2 = 0.1100, p1 = 0, p2 = 0;double fx = 1026, fy = 1019.2, cx = 922.9716, cy = 589.6080;char image_name[100];for (int num = 1; num < 302; num  )
 {
   sprintf_s(image_name,"./image/%d.jpg",num);
   Mat image = cv::imread(image_name, 0);   // 图像是灰度图,CV_8UC1int rows = image.rows, cols = image.cols;
   Mat image_undistort = Mat(rows, cols, CV_8UC1);   // 去畸变以后的图// 计算去畸变后图像的内容image_undistortfor (int v = 0; v < rows; v  )for (int u = 0; u < cols; u  ) {double u_distorted = 0, v_distorted = 0;double x1, y1, x2, y2;//未畸变像素平面到成像平面
     x1 = (u - cx) / fx;
     y1 = (v - cy) / fy;double r2;//成像平面畸变后
     r2 = pow(x1, 2)   pow(y1, 2);
     x2 = x1*(1   k1*r2   k2*pow(r2, 2))   2 * p1*x1*y1   p2*(r2   2 * x1*x1);
     y2 = y1*(1   k1*r2   k2*pow(r2, 2))   p1*(r2   2 * y1*y1)   2 * p2*x1*y1;//畸变的成像平面到像素平面
     u_distorted = fx*x2   cx;
     v_distorted = fy*y2   cy;// 赋值 (最近邻插值)if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {
       image_undistort.at(v, u) = image.at((int)v_distorted, (int)u_distorted);
     }else {
       image_undistort.at(v, u) = 0;
     }
     }// 画图去畸变后图像char outname[100];
   sprintf_s(outname, "./image1/%d.jpg", num);
   cv::imwrite(outname,image_undistort);
 }
 cv::waitKey();return 0;
}

方法二:调用 initUndistortRectifyMap和remap函数

br/<		Mat image = cv::imread(image_name, 0);   // 原图image是灰度图,CV_8UC1>br/<		int rows = image.rows, cols = image.cols;>br/<		Mat image_undistort = Mat(rows, cols, CV_8UC1);   // 去畸变以后的图>br/< >br/<		// 计算去畸变后图像的内容image_undistort>br/<		Mat map1, map2;>br/<		//initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, image.size(), 1, image.size(), 0), image.size(), CV_16SC2, map1, map2);>br/<		initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), Mat(),image.size(), CV_16SC2, map1, map2);>br/<		remap(image, image_undistort, map1, map2, INTER_LINEAR);>br/<		// 画图去畸变后图像>br/<		char outname[100];>br/<		sprintf_s(outname, "./image_indoor2/%d.jpg", num);>br/<		cv::imwrite(outname, image_undistort);>br/<	}>br/< >br/<	cv::waitKey();>br/<	return 0;>br/< >br/br/< >br/< ">#include 
#include
#include

using namespace std;
using namespace cv;


int main(int argc, char **argv) {

 // 内参
 double k1 = -0.4183, k2 = 0.1584, p1 = 0, p2 = 0;
 double fx = 1047.8, fy = 1028.6, cx = 885.1422, cy = 579.8241;
 Mat cameraMatrix = (Mat_<double>(3, 3) <<
   1047.8, 0, 885.1422,
   0, 1028.6, 579.8241,
   0,0,1);
 Mat distCoeffs = (Mat_<double>(1, 4) << -0.4183, 0.1584, 0, 0);

 char image_name[100];
 for (int num = 1; num < 500; num  )
 {
   sprintf_s(image_name, "./image_indoor/%d.jpg", num);
   Mat image = cv::imread(image_name, 0);   // 原图image是灰度图,CV_8UC1
   int rows = image.rows, cols = image.cols;
   Mat image_undistort = Mat(rows, cols, CV_8UC1);   // 去畸变以后的图

   // 计算去畸变后图像的内容image_undistort
   Mat map1, map2;
   //initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, image.size(), 1, image.size(), 0), image.size(), CV_16SC2, map1, map2);
   initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), Mat(),image.size(), CV_16SC2, map1, map2);
   remap(image, image_undistort, map1, map2, INTER_LINEAR);
   // 画图去畸变后图像
   char outname[100];
   sprintf_s(outname, "./image_indoor2/%d.jpg", num);
   cv::imwrite(outname, image_undistort);
 }

 cv::waitKey();
 return 0;

}

效果对比:

42feb942f4193db5ca3d63f5f45ced25.png

8d4ef7054acd597c75fe8047bac1c2b4.png

2. 双线性内插法去畸变

双线性内插法利用待求像素四个相邻像素的灰度在两个方向上做线性内插,在光流法求取某像素位置的灰度值时同样用到了二维线性插值。 光流法这里 返回的是某个像素位置的灰度值。

10fe6bcd65fb4b5726b8c3351cadfb7f.png

protected:
   // get a gray scale value from reference image (bilinear interpolated)
   //取得变换后的图中对应像素坐标处的灰度值,这里并不是返回一张图像的灰度值,而是就是写死了,就是类构造里传入的那张图
   inline float getPixelValue ( float x, float y )
   {
       //这里先说一下各个参数的类型:
       //image_为Mat*类型,图像指针,所以调用data时用->符号,
       //data为图像矩阵首地址,支持数组形式访问,data[]就是访问到像素的值了,此处为像素的灰度值,类型为uchar
       //关于step有点复杂,data[]中括号的式子有点复杂,总的意思就是y行乘上每行内存数,定位到行,然后在加上x,定位到像素
       //step具体解释在最后面有一些资料
       //image_->data[int(y)*image_->step   int(x)]这一步读到了x,y处的灰度值,类型为uchar,
       //但是后面由于线性插值,需要定位这个像素的位置,而不是他的灰度值,所以取其地址,赋值给data_ptr,记住它的位置,后面使用
       uchar* data_ptr = & image_->data[int(y)*image_->step   int(x)];

       //由于x,y这里有可能带小数,但是像素位置肯定是整数,所以,问题来了,(1.2, 4.5)像素坐标处的灰度值为多少呢?OK,线性插值!
       //说一下floor(),std中的cmath函数。向下取整,返回不大于x的整数。例floor(4.9)=4
       //xx和yy,就是取到小数部分。例:x=4.9的话,xx=x-floor(x)就为0.9。y同理
       float xx = x - floor ( x );
       float yy = y - floor ( y );

       //这整个return一个float值包含了线性插值,二维线性差值,这里后文有具体高清无码解释
       return float (
                  (1-xx) * ( 1-yy ) * data_ptr[0]  
                  xx* ( 1-yy ) * data_ptr[1]  
                  ( 1-xx ) *yy*data_ptr[ image_->step ]  
                  xx*yy*data_ptr[image_->step 1]
              );
   }
参考: 1.https://blog.csdn.net/weixin_41074793/article/details/88886153 2.https://blog.csdn.net/a472609409/article/details/90515742?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

推荐阅读

  • 专辑|相机标定
  • 专辑|3D点云
  • 专辑|SLAM
  • 专辑|深度学习与自动驾驶
  • 专辑|结构光
  • 专辑|事件相机
  • 专辑|OpenCV学习
  • 专辑|学习资源汇总
  • 专辑|招聘与项目对接
  • 专辑|读书笔记

重磅!3DCVer-学术论文写作投稿 交流群已成立

扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。

同时也可申请加入我们的细分方向交流群,目前主要有3D视觉CV&深度学习SLAM三维重建点云后处理自动驾驶、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、求职交流等微信群,请扫描下面微信号加群,备注:”研究方向+学校/公司+昵称“,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进去相关微信群。原创投稿也请联系。

8a800e44a5d61cec2cc866879db3b562.png ▲长按加微信群或投稿

105b44a7e537f122d10f92553b72dba7.png

▲长按关注公众号

3D视觉从入门到精通知识星球:针对3D视觉领域的知识点汇总、入门进阶学习路线、最新paper分享、疑问解答四个方面进行深耕,更有各类大厂的算法工程人员进行技术指导。与此同时,星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息,打造成集技术与就业为一体的铁杆粉丝聚集区,近1000+星球成员为创造更好的AI世界共同进步,知识星球入口:

学习3D视觉核心技术,扫描查看介绍,3天内无条件退款2dc98b5d5db24888ca691ce31ec0d8ec.png 圈里有高质量教程资料、可答疑解惑、助你高效解决问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值