问题 OpenCV:从翻译和旋转中获取透视矩阵


我正在尝试验证我的相机校准,所以我想纠正校准图像。我希望这将涉及使用呼叫 warpPerspective 但我没有看到一个明显的功能,它采用相机矩阵,旋转和平移矢量生成此调用的透视矩阵。

基本上我想做所描述的过程 这里 (特别是看到最后的图像),但从已知的相机模型和姿势开始。

是否有一个简单的函数调用,它接受相机的内在和外在参数,并计算透视矩阵以供使用 warpPerspective

我会打电话的 warpPerspective 打完电话后 undistort 在图像上。

原则上,我可以通过求解在顶部定义的方程组来推导出解 opencv相机校准文档 在指定约束之后 Z=0,但我认为必须有一个固定的例程,允许我对我的测试图像进​​行正射校正。

在我的搜索中,我发现很难通过所有的立体声校准结果 - 我只有一台摄像机,但想要在我只看平面测试模式的约束条件下纠正图像。


3414
2018-04-24 17:32


起源

那么,通过矫正你的意思是删除旋转效果?这意味着,您只需要一个视图,而不是两个相对于彼此的视图,您想要纠正这些视图(对齐极线)。 - David Nilosek
@DavidNilosek是的,我有一个校正圆阵列的倾斜图像,我想恢复“自上而下”的视图。 - Dave
我正在努力思考它,但遗憾的是我的运行时间不足。尝试使用旋转矩阵的逆矩阵作为warpPerspective的透视矩阵,如果有效,我可以写一些能够更好地解释它的东西。 - David Nilosek
您想转换由a查看的图像 透视 摄像机成为一个观看的图像 正字 相机,我不认为这可以使用逆旋转矩阵完成。一种直接的方法是使用 cv::getPerspectiveTransform 有4个适当的点。但是,有可能直接从相机校准中推导出变换,我会调查一下。 - AldurDisciple


答案:


实际上,没有必要涉及正交相机。以下是如何获得适当的透视变换。

如果您使用校准相机 cv::calibrateCamera,你获得了一个相机矩阵 K 透镜畸变系数的向量 D 对于您的相机,以及对于您使用的每个图像,旋转矢量 rvec (您可以将其转换为3x3矩阵 R 运用 cv::rodriguesDOC)和翻译矢量 T。考虑其中一个图像和相关的图像 R 和 T。你打电话后 cv::undistort 使用失真系数,图像将像投影矩阵的相机一样 K * [ R | T ]

基本上(如@DavidNilosek直觉),你想要取消旋转并获得图像,好像它是由形式的投影矩阵获取 K * [ I | -C ] 哪里 C=-R.inv()*T 是相机的位置。为此,您必须应用以下转换:

Hr = K * R.inv() * K.inv()

唯一可能的问题是扭曲的图像可能会超出图像平面的可见部分。因此,您可以使用其他翻译来解决该问题,如下所示:

     [ 1  0  |         ]
Ht = [ 0  1  | -K*C/Cz ]
     [ 0  0  |         ]

其中Cz是沿Oz轴的C分量。

最后,根据上面的定义, H = Ht * Hr 是对所考虑图像的整流透视变换。


13
2018-04-25 12:51



为了使图像居中,我必须做以下事情:让 u0=-K*C/Cz (翻译部分 Ht 以上);将它移动输入图像大小的一半: u[0] = u0[0] - image_size[0], u[1] = u0[1]-image_size[1], u[2]=u0[2] 然后用这个转移 u 矢量代替 -K*C/Cz 在Ht的建设中。这与我的世界坐标系的原点位于我的校准网格的中心有关 - Dave
我之前评论的代码片段中有类型:我移动了图像大小的1/2。 - Dave
很好,这是一个很好的解释 - David Nilosek
嗨,奥尔杜尔。你能看看我的类似问题吗? stackoverflow.com/questions/46679422/...。我也尝试将warpPerspective中使用的matPerspective更改为上面提到的H.结果是空的。谢谢。 - ricecakebear
@Dave:为了得到正确的重新投影,我需要反转'u [2]'。 - Toyo


这是“解决方程组”的意思草图(在Python中):

import cv2
import scipy  # I use scipy by habit; numpy would be fine too
#rvec= the rotation vector
#tvec = the translation *emphasized text*matrix
#A = the camera intrinsic

def unit_vector(v):
    return v/scipy.sqrt(scipy.sum(v*v))

(fx,fy)=(A[0,0], A[1,1])
Ainv=scipy.array( [ [1.0/fx, 0.0, -A[0,2]/fx],
                     [ 0.0,  1.0/fy, -A[1,2]/fy],
                     [ 0.0,    0.0,     1.0] ], dtype=scipy.float32 )
R=cv2.Rodrigues( rvec )
Rinv=scipy.transpose( R )

u=scipy.dot( Rinv, tvec ) # displacement between camera and world coordinate origin, in world coordinates


# corners of the image, for here hard coded
pixel_corners=[ scipy.array( c, dtype=scipy.float32 ) for c in [ (0+0.5,0+0.5,1), (0+0.5,640-0.5,1), (480-0.5,640-0.5,1), (480-0.5,0+0.5,1)] ]
scene_corners=[]
for c in pixel_corners:
    lhat=scipy.dot( Rinv, scipy.dot( Ainv, c) ) #direction of the ray that the corner images, in world coordinates
    s=u[2]/lhat[2]
    # now we have the case that (s*lhat-u)[2]==0,
    # i.e. s is how far along the line of sight that we need
    # to move to get to the Z==0 plane.
    g=s*lhat-u
    scene_corners.append( (g[0], g[1]) )

# now we have: 4 pixel_corners (image coordinates), and 4 corresponding scene_coordinates
# can call cv2.getPerspectiveTransform on them and so on..

1
2018-04-25 13:35



我怀疑这是达到与@ AldurDisciple的答案相同效果的不雅方式 - Dave