单目相机模型与畸变模型

单目相机模型与畸变模型

相机将三维世界中的坐标点映射到二维图像平面中形成图像,一般将这个过程用单目相机模型(针孔模型、小孔成像原理)描述,但是由于相机上存在透镜,光线投影到平面的过程中会产生畸变,因此还要进入畸变模型 单目相机模型 与初中学过的透镜成像原理非常相似,假设现实世界的某点坐标 [X,Y,Z] 投影点坐标 [X'

相机将三维世界中的坐标点映射到二维图像平面中形成图像,一般将这个过程用单目相机模型(针孔模型、小孔成像原理)描述,但是由于相机上存在透镜,光线投影到平面的过程中会产生畸变,因此还要进入畸变模型


单目相机模型

2022_08_ (2).jpg

与初中学过的透镜成像原理非常相似,假设现实世界的某点坐标 [X,Y,Z] 投影点坐标 [X',Y',Z'],设相机焦距 f,则有以下三角相似公式成立

\frac{Z}{f} =-\frac{X}{X'} =-\frac{Y}{Y'}


因为小孔成像是成倒像,所以X,Y坐标后都在缩放后取负数。为了简化模型,可以将负号忽略,整理后得出如下公式

\left\{\begin{matrix} X' = f\frac{X}{Z} \\ Y' = f\frac{Y}{Z} \end{matrix}\right.


而投影点与图像上的像素的差别,则是差了一个一个平移和缩放,缩放很好理解,类似于是否将图像放大或缩小显示。平移则是参考的0坐标点不一样,我们通常考虑光轴上的坐标为 [0,0,Z],而屏幕上的坐标系一般则以屏幕左上角为 [0,0,Z]。假设横向缩放为 α,纵向缩放为 β,屏幕中心到屏幕左上角的偏移量为 [Cx,Cy],屏幕上的坐标称为 [u,v],则可以得到以下变换关系

\left\{\begin{matrix} u = αX' + C_{x}\\ v = βY' + C_{y} \\ \end{matrix}\right.


结合俩次变换公式,则有

\left\{\begin{matrix} u = \alpha f\frac{X}{Z} + C_{x} \\ v = \beta f\frac{Y}{Z} + C_{y} \\ \end{matrix}\right.


用矩阵表示:

\begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \frac{1}{Z} \begin{bmatrix} f_{x} & 0 & C_{x}\\ 0 & f_{y} & C_{y}\\ 0 & 0 & 1\\ \end{bmatrix} \begin{bmatrix} X \\ Y \\ Z \end{bmatrix} = \frac{1}{Z}{\color{Red} K} P


我们将中间的 矩阵K 称为内参矩阵,这个参数一般在相机出厂后就不会发生改变,而计算这个参数的过程,称为 相机标定

有内参,就会有外参,而外参则是由相机运动决定的一组欧式变换矩阵 T(由一个平移矩阵加上一个旋转矩阵构成), 1/Z则是归一化的过程(将Z坐标化为1),归一化也意味着丢失深度信息,这是相机无可避免的,但是有着别的方法重新计算深度数据(双目相机,RGBD相机)

畸变模型

由于相机镜头中存在透镜,会影响光线传播的轨迹,因而会引起图像畸变(失真),由于透镜的形状为对称的,所以畸变本身也是对称的,并且越靠近图像边缘,畸变的影响越明显,我们知道穿过透镜光心的光线并不会改变路径,因此图像中间的像素是不会变的,越靠近图像中央畸变越小。由透镜本身产生的畸变称为镜像畸变,又主要分为桶形畸变(坐标向中心偏移)和枕形畸变(坐标向外偏移),畸变函数类似一条曲线(多次多项式),设 [X,Y]为原坐标,[Xdistorted,Ydistorted]为畸变后的坐标,r为坐标到图像中心的距离 ,kn为畸变参数,则有

\left\{\begin{matrix} X_{distorted} = x(1 + k_{1}r^{2} + k_{2}r^{4} + k_{3}r^{6} + ...)\\ Y_{distorted} = y(1 + k_{1}r^{2} + k_{2}r^{4} + k_{3}r^{6} + ...) \end{matrix}\right.


除去径向畸变,还有切向畸变,一般是由于透镜或者成像平面安装不正,导致图像整体像某一个方向拉伸,距离越远影响越大,设 pn为另外一组畸变参数,则有如下畸变

\left\{\begin{matrix} X_{distorted} = x + 2p_{1}xy + p_{2}(r^{2} + 2x^{2})\\ Y_{distorted} = y + 2*p_{2}xy + p_{1}(r^{2}+ 2y^{2}) \end{matrix}\right.


综合俩个畸变公式,得到

\left\{\begin{matrix} X_{distorted} = x(1 + k_{1}r^{2} + k_{2}r^{4} + k_{3}r^{6} + ...) + 2p_{1}xy + p_{2}(r^{2} + 2x^{2})\\ Y_{distorted} = y(1 + k_{1}r^{2} + k_{2}r^{4} + k_{3}r^{6} + ...) + 2*p_{2}xy + p_{1}(r^{2}+ 2y^{2}) \end{matrix}\right.


下为一个OpenCV读取图像并手动实现畸变算法的一个实例

 #include <opencv2/opencv.hpp>
 #include <opencv4/opencv2/opencv.hpp>
 #include <string>
 ​
 using namespace std;
 ​
 string image_file = "./distorted.png"; 
 ​
 int main(int argc, char **argv) {
 ​
   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);  
   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 x = (u - cx) / fx, y = (v - cy) / fy;
       double r = sqrt(x * x + y * y);
       double x_distorted = x * (1 + k1 * r * r + k2 * r * r * r * r) + 2 * p1 * x * y + p2 * (r * r + 2 * x * x);
       double y_distorted = y * (1 + k1 * r * r + k2 * r * r * r * r) + p1 * (r * r + 2 * y * y) + 2 * p2 * x * y;
       double u_distorted = fx * x_distorted + cx;
       double v_distorted = fy * y_distorted + cy;
 ​
     
       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("distorted", image);
   cv::imshow("undistorted", image_undistort);
   cv::imwrite("undistorted.png",image_undistort);
   cv::waitKey();
   return 0;
 }

运行效果

2022_08_ (10).png

图1(带畸变)

2022_08_ (9).png

图2(去畸变)

Comment