相机校准 - OpenCV:从平移和旋转中获取透视矩阵



我正在尝试验证我的相机校准,所以我想校正校准图像。我预计这将涉及使用对warpPerspective的调用,但我没有看到一个明显的函数,它使用相机矩阵、旋转和平移向量来生成该调用的透视矩阵。

本质上,我想做这里描述的过程(尤其是最后的图像),但从已知的相机模型和姿势开始。

是否有一个简单的函数调用可以获取相机的内在和外在参数,并计算用于warpPerspective的透视矩阵?

在对图像调用undistort之后,我将调用warpPerspective

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

在我的搜索中,我发现很难浏览所有的立体校准结果——我只有一台相机,但我想在我只看到平面测试图案的限制下校正图像。

实际上不需要涉及正交摄影机。以下是如何获得适当的透视变换。

如果使用cv::calibrateCamera校准相机,则会获得相机矩阵K,即相机的镜头畸变系数向量D,对于使用的每个图像,还会获得旋转向量rvec(可以使用cv::rodrigues,doc将其转换为3x3矩阵R)和平移向量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是所考虑图像的校正透视变换。

这是我所说的"求解方程组"(在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..

对于在遵循@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

最新更新