【问题标题】:How do you redistort a point?你如何重新扭曲一个点?
【发布时间】:2020-12-02 05:01:58
【问题描述】:

我有一张扭曲的图像,我在上面使用了Cv2.Undistort() 来纠正它。在此之后,我在图像中找到了一些点。我需要得到扭曲图像中这些点的位置。

我尝试了Cv2.ProjectPoints(),但未能获得合适的坐标。它们超出了图像的范围。

这就是我这样做的方式:

List<float> arr = new List<float>();
foreach (var i in points)
{
    arr.Add(i.X);
    arr.Add(i.Y);
    arr.Add(0);
}
Mat pointArr = new Mat(3, points.Count, MatType.CV_32FC1, arr.ToArray());
float[] rotArr = {  1, 0, 0,
                    0, 1, 0,
                    0, 0, 1};

float[] transArr = { 0, 0, 0 };

Mat rot = new Mat(3, 1, MatType.CV_32F, rotArr);
Mat trans = new Mat(3, 1, MatType.CV_32F, transArr);


Cv2.ProjectPoints(pointArr, rot, trans, camMatrix, dist, outputPoints);
List<Point2f> point2Fs = new List<Point2f>();
var lngt = outputPoints.Rows * outputPoints.Cols * outputPoints.Channels();
var matarr = new List<float>();
for (int i = 0; i < outputPoints.Rows; i++)
{
    point2Fs.Add(new Point2f(outputPoints.ExtractChannel(0).At<float>(0, i), outputPoints.ExtractChannel(1).At<float>(0, i)));
}

points - 我想在原始图像中找到的未失真图像中的点

有什么建议吗?

谢谢!

【问题讨论】:

  • 这能回答你的问题吗? Reverse of OpenCV projectPoints
  • 没有。我没有要查找的点的真实世界坐标。我有未失真图像中点的像素坐标。

标签: c# .net opencv computer-vision camera-calibration


【解决方案1】:

不幸的是,OpenCV 没有提供直接做你想做的事情的功能,即将失真系数应用于未失真的 2D 点。你可以:

选项 1

您使用 projectPoints() 走在正确的轨道上。理解它如何工作的诀窍是,未失真图像中的一个点“存在”在图像平面中,看起来像这样(图片来自https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html):

图像平面位于距原点f(焦距)的位置,xy 坐标相对于 cxcy.

您需要做的是从未失真的 2D 点 (xu, yu) 构建您的 3D 点 (x,y,z),如下所示:

x = (x_u - c_x) / f_x
y = (y_u - c_y) / f_y
z = 1

而不是在你的第一个 foreach 循环中使用 (xu, yu, 0)。

然后调用 projectPoints()。

您正确地使用了身份旋转和 0 平移。 fx、fy、cx和cy,在cameraMatrix中:

|f_x  0  c_x|
| 0  f_y c_y|
| 0   0   1 |

选项 2:

您可以直接应用失真方程 (https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html):

在第一步中,您将 x' 和 y' 替换为:

x' = (x_u - c_x) / f_x
y' = (y_u - c_y) / f_y

然后应用其余的方程式。这就是 projectPoints() 所做的,没有旋转/平移部分。

【讨论】:

  • 感谢您的回答!我已经尝试了您建议的第一个选项,但得到了不可接受的值,例如超出图像边界或负值。
【解决方案2】:

我最终使用了Milo 提出的第二个选项。我重写了没有旋转和平移部分的项目点方法,并从 x' 和 y' 开始方程路径,其中:

x' = (x_u - c_x) / f_x
y' = (y_u - c_y) / f_y

然后我应用了提供的其余方程式。

这里是源代码:

Point2f DistortPoint(Point2f point)
{
   var tempPt = new Point2f((point.X-intr.cx)/intr.fx, (point.Y-intr.cy)/intr.fy);
   var r2 = Math.Pow(tempPt.X, 2) + Math.Pow(tempPt.Y, 2);
   var xtmp = tempPt.X * ((1 + intr.k1 * r2 + intr.k2 * Math.Pow(r2, 2) + intr.k3 * Math.Pow(r2, 3)) /
                (1 + intr.k4 * r2 + intr.k5 * Math.Pow(r2, 2) + intr.k6 * Math.Pow(r2, 3))) + 2 * intr.p1 * tempPt.X * tempPt.Y + intr.p2 * (r2 + 2 * Math.Pow(tempPt.X, 2));
   var ytmp = tempPt.Y * ((1 + intr.k1 * r2 + intr.k2 * Math.Pow(r2, 2) + intr.k3 * Math.Pow(r2, 3)) /
                (1 + intr.k4 * r2 + intr.k5 * Math.Pow(r2, 2) + intr.k6 * Math.Pow(r2, 3))) + 2 * intr.p2 * tempPt.X * tempPt.Y + intr.p1 * (r2 + 2 * Math.Pow(tempPt.Y, 2));
   return new Point2f((float)(intr.fx * xtmp + intr.cx), (float)(intr.fy * ytmp + intr.cy));
}

【讨论】:

    【解决方案3】:

    我通过使用从cv2.initUndistortRectifyMap() 返回的索引mapxmapy 来做到这一点。

     x_distored = mapx[y_rectified, x_rectified]
     y_distored = mapy[y_rectified, x_rectified]
    

    此方法的局限性在于索引必须在范围内。

    即使在规范化x_rectifiedy_rectified 之后,我也无法使用Cv2.ProjectPoints() 将校正后的位置投影到失真。我的问题是无法将RT(我从cv2.stereoRectify() 得到它们。它们不是全零。)转换为Cv2.ProjectPoints() 所需的rvectvec 作为输入。有成功经验的请分享一下,谢谢!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-11
      • 1970-01-01
      • 1970-01-01
      • 2021-01-24
      • 1970-01-01
      • 1970-01-01
      • 2017-06-04
      相关资源
      最近更新 更多