MENU

单目相机模型与畸变模型

August 1, 2022 • Read: 369 • 开发,学习

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


单目相机模型

OIP-C.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;
}

运行效果

distorted.png

undistorted.png

Leave a Comment

2 Comments
  1. 真给修正过来了。

    1. @如是乎都是很浅显的道理@(哈哈)