你如何重新扭曲一个点?

How do you redistort a point?

我有一张扭曲的图像,我使用 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 - 我想在原始图像中找到的未失真图像中的点

有什么建议吗?

谢谢!

不幸的是,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) 像这样:

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

而不是像在第一个 foreach[=64 中那样使用 (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() 所做的,没有 rotation/translation 部分。

我最终使用了 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));
}

我通过使用从 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() 得到它们。它们不全是零。)转换为 rvectvec 所需的 Cv2.ProjectPoints() 作为输入。如果哪位有成功的经验请分享一下,谢谢!