OpenCV中的getOptimalNewCameraMatrix函数是用来做什么的?

20
我正在按照教程校准我的相机。我理解通过棋盘找到相机的内参和畸变系数的整个过程。但我不明白为什么在此之后,我们要调用getOptimalNewCameraMatrix函数?特别是使用alpha参数时。我已经阅读了文档,但可能因为缺乏相机校准知识,我真的无法理解它。

这是原始图像。 Original image

以下是上述图像的去畸变图像示例(使用OpenCV的undistort)。

对于这个示例,我只是直接使用获得的相机内部参数和畸变系数进行了图像去畸变处理。 Directly undistorted image

对于这个问题,我在去畸变之前使用了 alpha=0(左边)和 alpha=1(右边)来调用 getOptimalNewCameraMatrixDistorted image with alpha 从我所看到的,getOptimalNewCameraMatrix 保留了原始图像而没有丢失信息?我希望有人能解释一下这个函数的作用。
如果我想使用来自该相机的照片构建结构光学三维模型 (SfM),我需要先调用 getOptimalNewCameraMatrix 吗?
谢谢。
4个回答

21
我也曾有同样的问题。我在网上搜索时找不到清晰明确的答案,但是经过一些测试后,我对cv2.getOptimalNewCameraMatrix函数实际为用户做了什么有了一个想法。
我使用了openCV校准教程中提到的相同图像(你可以在这里找到它们,它们被命名为left01.jpg到left14.jpg(实际上,left10.jpg似乎丢失了,但没关系)。)在校准步骤之后-假设正方形尺寸为30毫米-我得到了这个相机矩阵:
mtx = [[534.07088364   0.         341.53407554]
       [  0.         534.11914595 232.94565259]
       [  0.           0.           1.        ]]

使用命令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.        ]]

注意这里的alpha=1,但它可以是[0,1]之间的其他值。使用另一个值将给出不同的newcameramtx

到目前为止还没有什么新东西。然后,我对校准过程中使用的所有图像进行了“去畸变”处理,并将它们保存到两个新文件夹中。其中一个文件夹(我们称其为第1个文件夹)直接使用获取的内部相机(即mtx)和畸变系数(dist)填充了未失真的图像,另一个文件夹(我们称其为第2个文件夹)使用newcameramtx和相同的畸变系数(dist)填充了未失真的图像。明确地说,我分别使用命令 cv2.undistort(img, mtx, dist, None, mtx)cv2.undistort(img, mtx, dist, None, newcameramtx) 并将结果图像保存到上述两个文件夹中。在保存之前,我没有使用roi裁剪未失真的图像。结果图像就像您在问题中发现并给出的那样。

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

mtx_1stFolder = [[533.72669262   0.         341.96641109]
                 [  0.         533.73880924 232.9742336 ]
                 [  0.           0.           1.        ]]

newcameramtx_1stFolder = [[533.41644287   0.         341.35446736]
                          [  0.         532.47808838 232.56343633]
                          [  0.           0.           1.        ]]

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

mtx_2ndFolder = [[458.04299944   0.         342.94915275]
                 [  0.         456.31672142 233.39481945]
                 [  0.           0.           1.        ]]

newcameramtx_2ndFolder = [[458.24960327   0.         342.20507346]
                          [  0.         455.25088501 232.99452259]
                          [  0.           0.           1.        ]]

现在,请注意mtxmtx_1stFoldernewcameramtx_1stFolder之间的相似程度。类似地,也请注意newcameramtxmtx_2ndFoldernewcameramtx_2ndFolder之间的相似程度。

基于这些观察结果,我认为直接使用获取的内部相机和失真系数对图像进行去畸变不会影响相机矩阵,并且它按原尺寸返回相同大小的图像,但是这些结果图像会失去一些像素点。如果您可以接受这种损失,那么即使不使用cv2.getOptimalNewCameraMatrix,也可以安全地使用。然而,如果您需要失去的像素中的信息,则可以设置在alpha参数之间[0,1],使其按相同大小或任何所需大小的方式进行图像去畸变。然而,此操作肯定会破坏相机矩阵,您需要对新的未失真图像进行新的校准。cv2.getOptimalNewCameraMatrix函数提供了一个新的相机矩阵的估计值,无需重新校准即可获得。这解释了为什么newcameramtxmtx_2ndFolder非常接近。

这也似乎解释了为什么mtx_2ndFoldernewcameramtx_2ndFolder相互接近,因为没有太多内容需要进行去畸变。(同样适用于mtx_1stFoldernewcameramtx_1stFolder)

到目前为止,我没有提到的一件事是每个校准过程的失真系数(dist)。这实际上让我无法确定我的想法是否正确,因此我对我的想法持开放态度。直观地说,我希望在第一次校准中找到一些失真系数,因为图像显然已失真。如预期所述,失真系数向量如下:

dist = [[-0.29297164  0.10770696  0.00131038 -0.0000311   0.0434798 ]]

另一方面,我本以为对未畸变图像进行校准会得到更少的系数,理想情况下应该是0,因为所有畸变都被完美地去除了。然而,对第一个文件夹进行校准所得到的结果是:

dist = [[ 0.00760897 -0.07313015  0.0002162   0.0003522   0.1605208 ]]

第二个文件夹的校准结果为:

dist = [[ 0.00135068 -0.02390678  0.0001996   0.0003413   0.0580141 ]]

对于前三项,结果如预期般朝着0逐渐变小,但并不会急剧接近0。 对于后两项,系数增加了,这与我的预期相反。 如果有人知道为什么会出现这种情况,我将不胜感激。


你没有考虑的一个方面是 centerPrincipalPoint 标志。如果你从 undistort 函数返回的图像和 alpha=0 的无畸变图像进行比较,它们会显示不同的结果(alpha=0 图像中的正方形看起来不是正方形)。这是因为默认情况下,当不使用新矩阵时,使用图像中心而不是通过 getOptimalNewCameraMatrix 重新计算主点。将此标志设置为 true 可以获得与不使用最优矩阵时 alpha=0 相同的结果。 - IndefiniteBen

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]

这应该会获取您的最后一张图像(右侧的针垫图像)并给出最可用的图像。

我还将undistort方法分成了两个部分,因为只要新收到的图像具有相同的分辨率,就可以不断调用remap


4
你对alpha参数的解释是错误的。当alpha=1时,所有源像素都不会失真,这可能会导致非矩形图像区域 - 在黑色的“枕形失真”中可见。由于图像在内存中必须是矩形的,因此这些间隙用黑色像素填充。将alpha设置为1有效地增加了焦距,以获得一个矩形的失真图像,其中所有像素对应于原始像素中的一个像素。你会失去来自周围的数据,但你不会有任何填充的无效像素。 - DoMakeSayThink
我的评论后半部分应该是“设置alpha=0...” - DoMakeSayThink

1
  • 使用原始相机矩阵“mtx”对图像进行去畸变时,去畸变输出图像的视场/焦距与原始图像保持相同,即在本例中约为534。由于这个原因,一些边界像素会丢失。
  • 通过指定一个自由比例参数>0,我们实际上要求算法缩小一些,以便包括边界像素。
  • 这导致视场更大,去畸变图像的焦距更小,与源图像相比。
  • 因此,正如在新矩阵(newcameramtx)中可以注意到的那样,焦距已经减少到约457(从约534开始),从而产生缩小效果。

0

正如您自己正确地认识到的那样,原则上不必调用getOptimalNewCameraMatrix函数。然而,使用此函数具有一些优点:

  1. Alpha = 1。当alpha = 1时调用,所有扭曲图像的原始区域也包含在矫正后的图像中。新相机矩阵是这样确定的:扭曲图像的角点映射到矫正图像的角点。这是该函数的设计准则。但是,使用alpha = 1的缺点是矫正图像还包含无效区域,在您的示例中为黑色。如果您不想使用这些区域,可以使用函数返回的ROI值。

  2. Alpha = 0。当alpha = 0时调用,您将获得一个没有无效区域并尽可能保留原始图像内容的图像,同时考虑到这个条件。原则上,它包含可以在alpha = 1的图像中选择的矩形区域,以便仅确定没有无效区域。使用alpha = 0生成的图像与中心区域略有扭曲的原始图像非常相似。如果可以不使用外围角落区域中的信息,则建议使用alpha = 0。

enter image description here

另请参阅https://answers.ros.org/question/392890/why-the-left-3x3-portion-of-projection-matrixp-is-different-from-the-intrinsic-camera-matrixk/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接