发布时间:2025-06-24 20:24:33  作者:北方职教升学中心  阅读量:260



// 画图去畸变后图像  cv::imshow("distorted", image);  cv::imshow("undistorted", image_undistort);  cv::waitKey();  return 0;}
  • cv::imshow("distorted", image);:使用 OpenCV 的 cv::imshow 函数创建一个名为 "distorted" 的窗口,并在该窗口中显示原始的畸变图像,也就是之前读取并存储在 cv::Mat 类型变量 image 中的图像数据。当然,也可以传入一个具体的正整数参数,表示等待的毫秒数,例如 cv::waitKey(5000) 表示等待 5000 毫秒(即 5 秒)后程序继续执行,可根据实际需求灵活调整等待时间。y 轴方向与图像平面坐标轴平行的平面上,且坐标值无量纲,通常在 -1 到 1 之间等范围,这里的 cx

k2、外层循环控制图像的纵坐标(通常可以理解为行数,对应 v 变量),从图像的最上方(v = 0)开始,逐行向下遍历,直到最后一行(v = rows - 1);内层循环控制图像的横坐标(通常理解为列数,对应 u 变量),从图像的最左边(u = 0)开始,逐列向右遍历,直到最右边(u = cols - 1),这样就能依次访问到原始图像 image 的每一个像素点。不同的相机镜头有不同的畸变参数值,这些参数对于准确进行图像去畸变操作至关重要。
  • double r = sqrt(x * x + y * y);:计算归一化平面坐标下的点到原点的距离 r,这个距离值在后续的径向畸变计算中会起到关键作用,因为径向畸变主要是基于该点距离光心的远近程度来对坐标进行扭曲变形的。
  • cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1);:创建了一个新的 cv::Mat 类型的矩阵 image_undistort,其大小与读取的原始图像 image 相同(行数为 rows,列数为 cols),数据类型也是 CV_8UC1,用于存储后续经过去畸变处理后的图像数据。如果在有效范围内,说明可以从原始图像中找到对应的像素点来进行赋值操作。最近邻插值的基本思想就是直接取距离目标位置最近的像素值作为插值结果,它计算简单,但可能会导致图像在某些情况下出现锯齿等不太平滑的现象。
  • int rows = image.rows, cols = image.cols;:通过访问 cv::Mat 对象 image 的成员变量 rows 和 cols,获取到读取的图像的行数(也就是图像的高度)和列数(即图像的宽度),方便后续在进行去畸变操作以及创建相应大小的结果图像矩阵时使用这些尺寸信息。
  • double u_distorted = fx * x_distorted + cx; 和 double v_distorted = fy * y_distorted + cy;:在得到归一化平面上的畸变坐标 (x_distorted, y_distorted) 后,再通过相机内参将其转换回图像坐标(以像素为单位),也就是计算出在畸变图像中对应的坐标 (u_distorted, v_distorted),这样就完成了从原始图像坐标到畸变图像坐标的完整转换过程。尽管每个环节在功能和代码实现上还有进一步提升的空间,但整体为理解和实践图像去畸变相关操作提供了一个很好的示例框架,通过不断地优化和扩展各部分功能,可以满足更复杂的实际应用需求。定义畸变参数和内参、
  • cv::imshow("undistorted", image_undistort);:同样调用 cv::imshow 函数,创建名为 "undistorted" 的窗口,用于展示经过去畸变处理后得到的图像,该图像存储在 cv::Mat 变量 image_undistort 中。

            这段 C++ 代码旨在实现图像去畸变的功能,虽然 OpenCV 本身提供了现成的去畸变函数,但通过自己手动实现该过程有助于深入理解图像畸变及去畸变的原理。p2 进行相应的数学计算,得到畸变后的坐标。通过 (u - cx) / fx 和 (v - cy) / fy 分别计算出 x 和 y 坐标,这是后续进行畸变计算的基础。此时,这个矩阵中的元素初始值是未定义的,需要通过具体的去畸变算法来填充相应的像素值。实现图像去畸变的核心算法,到最后的图像展示及正常结束程序,构成了一个较为完整的基于 OpenCV 的图像去畸变处理流程。它使得图像显示窗口能够一直保持在屏幕上,直到用户进行了按键操作,这样就给用户留出了足够的时间来仔细观察和对比两个图像的细节内容。

  • double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;:这几个变量分别定义了相机的畸变参数,其中 k1 和 k2 通常是径向畸变系数,用于描述由于镜头光学特性导致的图像径向方向上的变形情况;p1 和 p2 是切向畸变系数,反映了图像在切向方向上的畸变情况。这里通过 image.at<uchar>((int) v_distorted, (int) u_distorted) 从原始图像 image 中获取畸变图像坐标 (u_distorted, v_distorted) 对应的像素值(由于坐标需要转换为整数类型才能正确索引图像中的像素,所以进行了强制类型转换 (int)),然后将该像素值赋给去畸变后图像 image_undistort 中当前正在处理的像素位置 (v, u)fy 就是前面定义的相机内参)。这个函数会自动根据图像的数据类型等信息将图像以合适的形式呈现出来,方便用户直观地查看原始图像的样貌以及其中存在的畸变情况。v 坐标)转换为归一化平面坐标(也就是将图像坐标转换到以相机光心为原点,x
    • cv::Mat image = cv::imread(image_file, 0);:调用 OpenCV 的 imread 函数从指定的 image_file 路径读取图像,第二个参数 0 表示将图像以灰度模式读取,即读取后的图像数据类型为 CV_8UC1(8 位无符号单通道,也就是常见的灰度图像格式),并将读取到的图像存储在 cv::Mat 类型的变量 image 中。通过将原始图像和去畸变后的图像分别在不同窗口中展示,用户可以清晰地对比二者之间的差异,直观地看到去畸变操作所带来的效果,比如图像边缘部分原本因畸变而弯曲变形的地方在去畸变后是否恢复到正常的形状等情况。
    • cv::waitKey();:这个函数的作用是暂停程序的运行,等待用户按下键盘上的任意一个按键。fx、代码首先定义了畸变参数和相机内参,接着读取了一幅灰度图像,然后创建了一个用于存储去畸变后图像的空矩阵,后续应该是要基于给定的参数和读取的图像来实现具体的去畸变算法逻辑。
    • image_undistort.at<uchar>(v, u) = image.at<uchar>((int) v_distorted, (int) u_distorted);:当坐标在有效范围内时,采用最近邻插值的方法进行像素赋值。
    • double x = (u - cx) / fx, y = (v - cy) / fy;:这一步首先根据相机内参,将图像坐标(以像素为单位的 u

      #include <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);   // 图像是灰度图,CV_8UC1  int rows = image.rows, cols = image.cols;  cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1);   // 去畸变以后的图
      • string image_file = "./distorted.png";:定义了一个 string 类型的变量 image_file,用于指定要读取的图像文件的路径。如果图像读取成功,image 就包含了相应的灰度图像像素信息;若读取失败(比如文件不存在、
      • if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {:首先进行一个条件判断,检查计算得到的畸变图像坐标 (u_distorted, v_distorted) 是否在原始图像的有效范围之内(即横坐标大于等于 0 且小于图像宽度 cols,纵坐标大于等于 0 且小于图像高度 rows)。
      • else { 和 image_undistort.at<uchar>(v, u) = 0;:如果计算得到的畸变图像坐标超出了原始图像的有效范围,说明在原始图像中找不到对应的像素点,此时将去畸变后图像 image_undistort 中当前像素位置 (v, u) 的值设为 0,也就是用黑色像素来填充这些超出范围对应的位置。
      • double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;:这几个变量定义了相机的内参,fx 和 fy 分别是在 x 轴和 y 轴方向上的焦距(以像素为单位),它们反映了相机将三维世界中的物体投影到二维图像平面上的缩放比例关系;cx 和 cy 是图像中心的坐标(通常以像素为单位),也就是光轴与图像平面交点对应的像素位置,相机内参对于将图像坐标和实际的物理世界坐标进行转换等操作起着关键作用。其中,前面部分 x * (1 + k1 * r * r + k2 * r * r * r * r) 和 y * (1 + k1 * r * r + k2 * r * r * r * r) 体现了径向畸变的影响,后面的 2 * p1 * x * y + p2 * (r * r + 2 * x * x) 以及 p1 * (r * r + 2 * y * y) + 2 * p2 * x * y 部分体现了切向畸变的影响,这里综合考虑了径向畸变和切向畸变因素,通过给定的畸变参数 k1、尽管我们可以调用OpenCV的去畸变,但自己实现一遍有助于理解。特别地,当前路径是相对路径(相对于可执行程序所在的目录),实际应用中根据情况也可以使用绝对路径。

                整个程序从图像读取、这里需要确保路径的正确性,否则后续的图像读取操作会失败。


      // 计算去畸变后图像的内容  for (int v = 0; v < rows; v++) {    for (int u = 0; u < cols; u++) {      // 按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted)      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;     }    }  }
      • for (int v = 0; v < rows; v++) { 和 for (int u = 0; u < cols; u++) {:这两层嵌套的 for 循环构成了遍历原始图像所有像素点的结构。p1、格式不支持等原因),image 会是一个空的 cv::Mat 对象。
      • 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;:这两行代码是根据畸变模型公式来计算在存在畸变情况下,归一化平面上的坐标 (x, y) 经过畸变后对应的坐标 (x_distorted, y_distorted)cy