点击上方“AI算法修炼营”,选择加星标或“置顶”
标题以下,全是干货
通过采集的图像我们可以得到畸变后的图像,要得到没有畸变的图像要通过畸变模型推导其映射关系。
真实图像 imgR 与 畸变图像 imgD 之间的关系为: imgR(U, V) = imgD(Ud, Vd) 。遍历所有(U,V)填充为映射对应的(Ud,Vd)即可实现图像去畸变处理。
前提条件是:已经得知相机内参K以及畸变参数k1,k2,k3,p1,p2,这部分可以由matlab工具箱实现。
接下来,我们将详细介绍如何去除图像的畸变。
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;
}
效果对比:
2. 双线性内插法去畸变
双线性内插法利用待求像素四个相邻像素的灰度在两个方向上做线性内插,在光流法求取某像素位置的灰度值时同样用到了二维线性插值。
光流法这里返回的是某个像素位置的灰度值。
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
目标检测系列
秘籍一:模型加速之轻量化网络
秘籍二:非极大值抑制及回归损失优化
秘籍三:多尺度检测
秘籍四:数据增强
秘籍五:解决样本不均衡问题
秘籍六:Anchor-Free
语义分割系列
一篇看完就懂的语义分割综述
最新实例分割综述:从Mask RCNN 到 BlendMask
面试求职系列
决战春招!算法工程师面试问题及资料超详细合集
一起学C++系列
内存分区模型、引用、函数重载
竞赛与工程项目分享系列
如何让笨重的深度学习模型在移动设备上跑起来
基于Pytorch的YOLO目标检测项目工程大合集
点云配准领域全面资料、课程、数据集合集分享
10万奖金天文数据挖掘竞赛!0.95高分Baseline分享
目标检测应用竞赛:铝型材表面瑕疵检测
SLAM系列
视觉SLAM前端:视觉里程计和回环检测
视觉SLAM后端:后端优化和建图模块
视觉SLAM中特征点法开源算法:PTAM、ORB-SLAM
视觉SLAM中直接法开源算法:LSD-SLAM、DSO
视觉注意力机制系列
Non-local模块与Self-attention之间的关系与区别?
视觉注意力机制用于分类网络:SENet、CBAM、SKNet
Non-local模块与SENet、CBAM的融合:GCNet、DANet
Non-local模块如何改进?来看CCNet、ANN