OpenCV:从平移和旋转中获取透视矩阵

14
我正在尝试验证我的相机校准,因此我想纠正校准图像。 我期望这将涉及使用一个调用到 warpPerspective ,但我没有找到明显的函数来使用相机矩阵和旋转以及平移向量来生成透视投影矩阵。
基本上,我想要执行此处所述的过程(特别是朝向结尾的图片),但是由于已知相机模型和姿态,因此需要开始。
是否有一个简单的函数调用,可以使用相机内部和外部参数计算透视矩阵,用于warpPerspective使用?
在调用了对图像进行undistort之后,我将调用warpPerspective
原则上,我可以通过解决 OpenCV 相机校准文档顶部定义的方程组的系统,在指定约束Z = 0后得出解决方案,但我认为必须有一种常规例程,可以让我直接对测试图像进行直交矫正。
在我的搜索中,我发现很难浏览所有立体校准结果-- 我只有一个相机,但希望校正仅在查看平面测试模式时进行。

那么,通过“矫正”您是指消除旋转效应吗?也就是说,您只有一个视图,而不是相对于彼此的两个视图,您想要矫正(对齐对极线)。 - David Nilosek
@DavidNilosek 是的,我有一个校准圆阵列的斜视图像,我想恢复“俯视”视角。 - Dave
我正在努力思考,但不幸的是时间不够了。如果使用旋转矩阵的逆作为warpPerspective的透视矩阵,那么我可以写一些更好解释的内容。 - David Nilosek
你想将被透视相机观察到的图像转换为被正交相机观察到的图像,我认为这不能使用逆旋转矩阵来完成。一个简单的方法是使用cv::getPerspectiveTransform和4个适当的点。然而,可能可以直接从相机校准中推导出变换,我会进一步研究。 - BConic
3个回答

20

实际上不需要使用正交相机,以下是您可以获取适当透视变换的方法。

如果您使用cv::calibrateCamera校准了相机,则获得了相机矩阵K、镜头畸变系数向量D,以及对于每个使用的图像,旋转向量rvec(您可以使用cv::rodrigues将其转换为3x3矩阵Rdoc)和平移向量T。考虑其中一个图像和相关的RT。在使用失真系数调用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是C沿着Oz轴的分量。

最后,根据以上定义,H = Ht * Hr是考虑图像的矫正透视变换。


1
为了让图像居中,我必须执行以下操作:让u0=-K*C/Cz(上面的Ht的翻译部分);将其移动半个输入图像大小:u[0] = u0[0] - image_size[0]u[1] = u0[1]-image_size[1]u[2]=u0[2],然后在构建Ht时使用这个移动的u向量代替-K*C/Cz。这与我的世界坐标系原点位于校准网格中心有关。 - Dave
@Dave:为了得到正确的重新投影,我需要反转“u[2]”。 - Toyo
@AlduurDisciple,您能否解释一下最终Ht方程中Cz的含义是什么? - Abhijit Balaji
@AbhijitBalaji 看看我的修改。这是向量C的第三个元素。 - BConic
@AldurDisciple 我按照你的步骤操作,但是得到的是全黑的图像。能否请您澄清一下?谢谢。 - Abhijit Balaji
显示剩余3条评论

1
这是我所说的“解方程组”(用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..

我怀疑这是一种不太优雅的方式,实现与@AldurDisciple答案相同的效果。 - Dave

0
对于那些在按照@BConic的答案时遇到图像对齐问题的人来说,一个实用的解决方案是使用Hr来扭曲图像的角点,并定义Ht来偏移结果。
Hr = K @ R.T @ np.linalg.pinv(K)

# warp image corner points:
w, h = image_size
points = [[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]
points = np.array(points, np.float32).reshape(-1, 1, 2)

warped_points = cv2.perspectiveTransform(points, Hr).squeeze()

# get size and offset of warped corner points:
xmin, ymin = warped_points.min(axis=0)
xmax, ymax = warped_points.max(axis=0)

# size:
warped_image_size = int(round(xmax - xmin)), int(round(ymax - ymin))

# offset:
Ht = np.eye(3)
Ht[0, 2] = -xmin
Ht[1, 2] = -ymin

H = Ht @ Hr

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