1. 相机畸变模型
    我们计算畸变都是在归一化平面上进行的,下面的(x,y), (x_distort,y_distort)都是在归一化坐标,相机坐标(X,Y,Z)的归一化坐标(X/Z, Y/Z, 1)
    在这里插入图片描述
    1、径向畸变
    由透镜形状引起的,畸变系数k1, k2, k3,畸变模型:
    在这里插入图片描述
    在这里插入图片描述

    2、切向畸变
    由镜片安装和成像平面不平行引起的,畸变系数p1, p2,切向畸变模型:
    在这里插入图片描述
    3、总的畸变模型
    在这里插入图片描述

  2. 求解思路
    我们得到的原图是畸变后的图像(x_distort,y_distort),要计算畸变之前的真实图像(x,y),不是用逆运算,太难麻烦了,而是计算真是图像畸变后会投影在哪,对应过去。先把原图像设置为一个空的图像,把一个个像素畸变投影过去,找到和畸变后图像像素点的对应关系(可以参考下面代码方法1理解)。

  3. 代码示例

     注意,OpenCV畸变系数矩阵的默认顺序 [k1, k2, p1, p2, k3]
    
    #include <opencv2/opencv.hpp>
    #include <chrono>
    using namespace std;
    
    int main(int argc, char **argv)
    {
      // 内参
      double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;
      /**内参矩阵K
       * fx  0  cx
       * 0  fy  cy
       * 0   0   1
       */
      // 畸变参数
      double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;
    
      cv::Mat image = cv::imread(argv[1], 0); // 图像是灰度图,CV_8UC1
      int rows = image.rows, cols = image.cols;
      cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1); // 方法1去畸变以后的图
      cv::Mat image_undistort2 = cv::Mat(rows, cols, CV_8UC1);  // 方法2 OpenCV去畸变以后的图
    
      chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
      //! 方法1. 自己写计算去畸变后图像的内容
      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; //这里最好用插值法
          }
        }
      }
      chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
      chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
      cout << "time = " << time_used.count() << endl;
    
      //! 方法2. OpenCV自带的undistort函数,更快速
      cv::Mat K = cv::Mat::eye(3, 3, CV_32FC1); //内参矩阵
      K.at<float>(0, 0) = fx;
      K.at<float>(1, 1) = fy;
      K.at<float>(0, 2) = cx;
      K.at<float>(1, 2) = cy;
      cv::Mat distort_coeffs = cv::Mat::zeros(1, 5, CV_32FC1); //畸变系数矩阵 顺序是[k1, k2, p1, p2, k3]
      distort_coeffs.at<float>(0, 0) = k1;
      distort_coeffs.at<float>(0, 1) = k2;
      distort_coeffs.at<float>(0, 2) = p1;
      distort_coeffs.at<float>(0, 3) = p2;
      cout << "K = " << endl
           << K << endl;
      cout << "distort_coeffs = " << endl
           << distort_coeffs << endl;
    
      t1 = chrono::steady_clock::now();
      cv::undistort(image, image_undistort2, K, distort_coeffs); //去畸变
      t2 = chrono::steady_clock::now();
      time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
      cout << "time = " << time_used.count() << endl;
    
      // 展示去畸变后图像
      cv::imshow("distorted", image);
      cv::imshow("undistorted", image_undistort);
      cv::imshow("image_undistort2", image_undistort2);
      cv::waitKey(0);
      return 0;
    }
    
GitHub 加速计划 / opencv31 / opencv
77.38 K
55.71 K
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:2 个月前 )
48668119 dnn: use dispatching for Winograd optimizations 1 天前
3dace76c flann: remove unused hdf5 header 1 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐