getOptimalNewCameraMatrix 在 OpenCV 中有什么作用?

Haf*_*ian 6 opencv image camera-calibration

我正在按照教程校准我的相机。我了解了使用棋盘求相机内参和畸变系数的整个过程。我不明白的是,为什么在那之后,我们称之为getOptimalNewCameraMatrix?尤其是alpha参数。已经阅读了文档,但可能因为缺乏相机校准的知识,我真的无法理解它。

所以,这是原始图像。 原图

以下是上述图像的未失真图像示例(使用 OpenCV 的undistort)。

对于这个,我只是使用获得的内在相机和失真系数直接对图像进行失真处理。 直接不失真的图像

至于这个,在我解开它之前,我getOptimalNewCameraMatrixalpha=0(左)和alpha=1(右)来称呼它。带有 alpha 的扭曲图像

从我所见,getOptimalNewCameraMatrix是在不丢失信息的情况下保留原始图像?我希望有人能解释这个函数的真正作用。

如果我想用来自运动的结构 (SfM) 和来自这台相机的图片构建一个 3D 模型,我应该首先调用getOptimalNewCameraMatrix?

谢谢。

小智 22

我使用了openCV 校准教程中提到的相同图像(您可以在这里找到名为 left01.jpg 到 left14.jpg 的图像(实际上 left10.jpg 似乎丢失了,但这没关系))在校准步骤之后 - 假设正方形尺寸为30毫米-在教程中我得到了这个相机矩阵:

mtx = [[534.07088364   0.         341.53407554]
       [  0.         534.11914595 232.94565259]
       [  0.           0.           1.        ]]
Run Code Online (Sandbox Code Playgroud)

另一方面,使用命令获得的新相机矩阵newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))

newcameramtx = [[457.92434692   0.         342.55548195]
                [  0.         456.2421875  233.34661351]
                [  0.           0.           1.        ]]
Run Code Online (Sandbox Code Playgroud)

请注意,这里alpha=1,但它可能是 [0,1] 之间的另一个值。使用另一个值会产生不同的newcameramtx.

到目前为止,没有什么新鲜事。然后,我在校准过程中对所有使用的图像进行了“不失真”处理,并将它们保存到两个新文件夹中。

其中一个文件夹(我们称其为第一个文件夹),填充了直接使用获得的本征相机(即mtx)和畸变系数(dist)获得的未畸变图像。

另一个(我们称之为第二个文件夹)充满了使用newcameramtx和相同的失真系数 ( dist) 的未失真图像。

明确地说,我使用了以下命令:

cv2.undistort(img, mtx, dist, None, mtx)
Run Code Online (Sandbox Code Playgroud)

和:

cv2.undistort(img, mtx, dist, None, newcameramtx)
Run Code Online (Sandbox Code Playgroud)

分别将结果图像保存到上述两个文件夹中。我没有roi在保存之前使用裁剪未扭曲的图像。结果图像就像您在问题中找到并给出的一样。

然后,使用第一个文件夹中的未失真图像,我重新执行校准过程,发现以下相机矩阵:

cv2.undistort(img, mtx, dist, None, mtx)
Run Code Online (Sandbox Code Playgroud)
cv2.undistort(img, mtx, dist, None, newcameramtx)
Run Code Online (Sandbox Code Playgroud)

对第二个文件夹执行相同操作,得到以下相机矩阵:

mtx_1stFolder = [[533.72669262   0.         341.96641109]
                 [  0.         533.73880924 232.9742336 ]
                 [  0.           0.           1.        ]]
Run Code Online (Sandbox Code Playgroud)
newcameramtx_1stFolder = [[533.41644287   0.         341.35446736]
                          [  0.         532.47808838 232.56343633]
                          [  0.           0.           1.        ]]
Run Code Online (Sandbox Code Playgroud)

mtx现在,请注意这里、mtx_1stFolder、 和newcameramtx_1stFolder彼此之间的距离有多近。同样,请注意newcameramtxmtx_2ndFolder、 和newcameramtx_2ndFolder彼此之间的距离有多近。

基于这些观察,我的想法是直接使用获得的固有相机和畸变系数对图像进行去畸变,不会影响相机矩阵。它相应地返回相同大小的图像,但这些生成的图像会丢失一些像素。

如果您可以接受这种损失,那么您完全可以不使用cv2.getOptimalNewCameraMatrix。但是,如果您需要丢失像素中给出的信息,您还可以选择相应地使图像不失真,将alpha[0,1] 之间的参数设置为相同的图像尺寸或您想要的任何尺寸。

然而,此操作肯定会破坏您的相机矩阵,并且您需要对新的未失真图像进行新的校准。cv2.getOptimalNewCameraMatrix函数可以估计新的相机矩阵,而无需您进行新的校准。这就解释了为什么newcameramtxmtx_2ndFolder彼此非常接近。

这似乎也解释了为什么mtx_2ndFoldernewcameramtx_2ndFolder彼此接近,因为没有太多可以扭曲的东西(对于 和 也是如此mtx_1stFoldernewcameramtx_1stFolder

到目前为止我还没有提到的一件事是dist每个校准过程的失真系数( )。这实际上让我无法说我的想法是肯定的,所以我愿意接受对我的想法的更正。

直观上,我预计第一次校准会出现一些失真系数,因为图像明显失真。正如预期的那样,失真系数向量为:

mtx_2ndFolder = [[458.04299944   0.         342.94915275]
                 [  0.         456.31672142 233.39481945]
                 [  0.           0.           1.        ]]
Run Code Online (Sandbox Code Playgroud)

另一方面,我预计对未失真图像的校准会给出更少的系数,理想情况下为 0,因为理想情况下所有失真都被消除了。

然而,第一个文件夹的校准给出:

newcameramtx_2ndFolder = [[458.24960327   0.         342.20507346]
                          [  0.         455.25088501 232.99452259]
                          [  0.           0.           1.        ]]
Run Code Online (Sandbox Code Playgroud)

对第二个文件夹的校准给出:

dist = [[-0.29297164  0.10770696  0.00131038 -0.0000311   0.0434798 ]]
Run Code Online (Sandbox Code Playgroud)

对于前三项,结果如预期的那样,幅度逐渐减小到零,但它们并没有急剧接近 0。

另外,对于最后两项,系数增加了,这与我的预期相反。


小智 8

如果您想获得与原始图像形状相同的图像,则必须丢失一些像素。

opencv 文档中的“alpha”参数描述指出......

自由缩放参数介于 0(当未失真图像中的所有像素均有效时)和 1(当所有源图像像素保留在未失真图像中时)之间

对我来说,这意味着值为 0 时,所有像素都将进行几何变换以包含在内。径向和切向畸变将被消除,但图像的几何形状将不正确。

getOptimalNewCameraMatrix用于使用具有相同校准的同一相机的不同分辨率。

getOptimalNewCameraMatrix当我使用然后对返回的感兴趣区域应用裁剪时,我会得到最好的结果。

这是我用于此目的的 python 代码片段。

newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, dim, 5)
image = cv2.remap(image, mapx, mapy, cv2.INTER_LINEAR)

x, y, w, h = roi
image = image[y:y + h, x:x + w]
Run Code Online (Sandbox Code Playgroud)

这应该会获取您的最后一张图像(右侧的针垫图像)并为您提供最有用的图像。

我还将该undistort 方法分为两部分,因为您可以继续调用remap从相机接收到的新图像,前提是它们具有相同的分辨率

  • 您对 alpha 参数的解释是错误的。当 alpha=1 时,所有源像素都不会失真,这可能会导致非矩形图像区域 - 在黑色枕形中可见。由于图像在内存中必须是矩形,因此间隙用黑色像素填充。设置 alpha=1 可以有效地增加焦距,以获得矩形无失真图像,其中所有像素对应于原始图像中的一个像素。您会丢失外围数据,但没有任何没有有效数据的填充像素。 (4认同)