转载https://blog.youkuaiyun.com/weixin_38009585/article/details/82356022
1.摄像机成像原理简述
成像的过程实质上是几个坐标系的转换。首先空间中的一点由 世界坐标系 转换到 摄像机坐标系 ,然后再将其投影到成像平面 ( 图像物理坐标系 ) ,最后再将成像平面上的数据转换到图像平面 ( 图像像素坐标系 ) 。
详细的可以参考我之前的博客[图像]摄像机标定(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是整数,因为它是我们要组成图像的像素坐标位置,以这正常图像的坐标位置去求在畸变图像中的坐标位置,取出对应的像素值,这也是正常图像的像素值)。 但是畸变的像素往往不是整数,所以需要通过插值来进行求解,详细见我之前的博客 [图像]图像缩放算法-双线性内插法 。
3.2. 代码实现
这里给出的是c++版的代码,最后采用的是最近邻插值方法,还可以使用其他的线性插值方法
-
#include <opencv2/opencv.hpp>
-
#include <string>
-
#include <math.h>
-
using namespace std;
-
string image_file = "../test.png"; // 请确保路径正确
-
int main(int argc, char **argv) {
-
// 本程序需要你自己实现去畸变部分的代码。尽管我们可以调用OpenCV的去畸变,但自己实现一遍有助于理解。
-
// 畸变参数
-
double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;
-
// 内参
-
double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;
-
cv::Mat image = cv::imread(image_file,0); // 图像是灰度图,CV_8UC1
-
int rows = image.rows, cols = image.cols;
-
cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1); // 去畸变以后的图
-
// 计算去畸变后图像的内容
-
for (int v = 0; v < rows; v++)
-
for (int u = 0; u < cols; u++) {
-
double u_distorted = 0, v_distorted = 0;
-
// TODO 按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted) (~6 lines)
-
// start your code here
-
//image_undistort中含有非畸变的图像坐标
-
//将image_undistort的坐标通过内参转换到归一化坐标系下,此时得到的归一化坐标是对的
-
//将得到的归一化坐标系进行畸变处理
-
//将畸变处理后的坐标通过内参转换为图像坐标系下的坐标
-
//这样就相当于是在非畸变图像的图像坐标和畸变图像的图像坐标之间建立了一个对应关系
-
//相当于是非畸变图像坐标在畸变图像中找到了映射
-
//对畸变图像进行遍历之后,然后赋值(一般需要线性插值,因为畸变后图像的坐标不一定是整数的),即可得到矫正之后的图像
-
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;
-
// end your code here
-
// 赋值 (最近邻插值)
-
if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {
-
image_undistort.at<uchar>(v, u) = image.at<uchar>((int) v_distorted, (int) u_distorted);
-
} else {
-
image_undistort.at<uchar>(v, u) = 0;
-
}
-
}
-
// 画图去畸变后图像
-
cv::imshow("image undistorted", image_undistort);
-
cv::waitKey();
-
return 0;
-
}