opencv摄像机标定之对极几何、深度地图

1.对极几何

(1)简单原理

  • 在我们使用针孔相机时,我们会丢失大量重要的信心,比如说图像的深度,或者说图像上的点和摄像机的距离,因这是一个从3D 到2D 的转换。因此一个重要的问题就产生了,使用这样的摄像机我们能否计算除深度信息呢?答就是使用多个相机。我们的眼睛就是这样工作的,使用两个摄像机(两个眼睛),这被称为立体视觉。
  • 如果只是用一台摄像机我们不可能知道3D 空间中的X 点到图像平面的距离,因为OX 连线上的每个点投影到图像平面上的点都是相同的。但是如果我们也考虑上右侧图像的话,直线OX 上的点将投影到右侧图像上的不同位置。所以根据这两幅图像,我们就可以使用三角测量计算出3D 空间中的点到摄像机的距离(深度)。
    在这里插入图片描述
  • 直线OX 上的不同点投射到右侧图像上形成的线l′ 被称为与x 点对应的极线。也就是说,我们可以在右侧图像中沿着这条极线找到x 点。它可能在这条直线上某个位置(这意味着对两幅图像间匹配特征的二维搜索就转变成了沿着极线的一维搜索。这不仅节省了大量的计算,还允许我们排除许多导致虚假匹配的点)。这被称为对极约束。与此相同,所有的点在其他图像中都有与之对应的极线。平面XOO’ 被称为对极平面。
  • O 和O’ 是摄像机的中心。从上面的示意图可以看出,右侧摄像机的中心O’ 投影到左侧图像平面的e 点,这个点就被称为极点。极点就是摄像机中心连线与图像平面的交点。因此点e’ 是左侧摄像机的极点。有些情况下,我们可能不会在图像中找到极点,它们可能落在了图像之外(这说明这两个摄像机不能拍摄到彼此)。
  • 所有的极线都要经过极点。所以为了找到极点的位置,我们可以先找到多条极线,这些极线的交点就是极点。本节我们的重点就是找到极线和极点。为了找到它们,我们还需要两个元素,本征矩阵(E)和基础矩阵(F)。本征矩阵包含了物理空间中两个摄像机相关的旋转和平移信息。
  • 简单来说,基础矩阵F 将一副图像中的点映射到另一幅图像中的线(极线)上。这是通过匹配两幅图像上的点来实现的。要计算基础矩阵至少需要8 个点(使用8 点算法)。

(2)代码速记

  • cv2.xfeatures2d.SIFT_create()
  • sift.detectAndCompute()
  • cv2.FlannBasedMatcher()
  • flann.knnMatch()
  • cv2.findFundamentalMat()
  • cv2.computeCorrespondEpilines()

参数:

#计算基础矩阵
F, mask = cv2.findFundamentalMat(pts1, pts2, cv2.FM_LMEDS)#points1,points2,method
#计算极线
lines1 = cv2.computeCorrespondEpilines(pts2.reshape(-1, 1, 2), 2, F)#输入参数:points,whichImage,F,lines

(3)实战

# 绘制极线的函数
def drawlines(img1, img2, lines, pts1, pts2):
    r, c = img1.shape
    img1 = cv2.cvtColor(img1, cv2.COLOR_GRAY2BGR)
    img2 = cv2.cvtColor(img2, cv2.COLOR_GRAY2BGR)
    for r, pt1, pt2 in zip(lines, pts1, pts2):
        color = tuple(np.random.randint(0, 255, 3).tolist())
        x0, y0 = map(int, [0, -r[2] / r[1]])  # 映射成整数值
        x1, y1 = map(int, [c, -(r[2] + r[0] * c) / r[1]])
        cv2.line(img1, (x0, y0), (x1, y1), color, 1)
        cv2.circle(img1, tuple(pt1), 5, color, -1)
        cv2.circle(img2, tuple(pt2), 5, color, -1)
    return img1, img2

def match(self):
    img_L = cv2.imread("../images/left.jpg", 0)
    img_R = cv2.imread("../images/right.jpg", 0)

    sift = cv2.xfeatures2d.SIFT_create()

    kp1, des1 = sift.detectAndCompute(img_L, None)
    kp2, des2 = sift.detectAndCompute(img_R, None)

	# FLANN parameters
    FLANN_INDEX_KDTREE = 0
    index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
    search_params = dict(checks=50)

    # 利用FLANN匹配器
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    matches = flann.knnMatch(des1, des2, k=2)

    good = []
    pts1 = []
    pts2 = []

    # 寻找匹配点对
    for i, (m, n) in enumerate(matches):
        if m.distance < 0.8 * n.distance:
            good.append(m)
            pts1.append(kp1[m.queryIdx].pt)
            pts2.append(kp2[m.trainIdx].pt)

    pts1 = np.int32(pts1)
    pts2 = np.int32(pts2)
    # 根据匹配点对计算基础矩阵
    F, mask = cv2.findFundamentalMat(pts1, pts2, cv2.FM_LMEDS)

    # 寻找内部点
    pts1 = pts1[mask.ravel() == 1]
    pts2 = pts2[mask.ravel() == 1]

    # 计算并绘制两幅图像中的极线
    # Find epilines corresponding to points in right image(second image) 
    # drawing its lines on left image
    lines1 = cv2.computeCorrespondEpilines(pts2.reshape(-1, 1, 2), 2, F)
    lines1 = lines1.reshape(-1, 3)
    img5, img6 = drawlines(img_L, img_R, lines1, pts1, pts2)
	# Find epilines corresponding to points in left image (first image) and
	# drawing its lines on right image
    lines2 = cv2.computeCorrespondEpilines(pts1.reshape(-1, 1, 2), 1, F)
    lines2 = lines2.reshape(-1, 3)
    img3, img4 = drawlines(img_R, img_L, lines2, pts2, pts1)

    plt.subplot(121), plt.imshow(img5), plt.title('leftImage'), plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(img3), plt.title('rightImage'), plt.xticks([]), plt.yticks([])
    plt.show()

在这里插入图片描述
记录一下这里遇到的2个坑:
1.测试图像是有要求的,必须是两个不同的角度拍摄的同一物体。镜像图像是没用的。
2.极线会经过匹配点的,如果匹配点正常,极线不正常,可以看下绘图函数是否出了问题。

2.立体图像中的深度地图

我们可以得到图像中所有点的深度图。

(1)代码速记

  • cv2.StereoBM_create()
  • stereo.compute()

(2)实战

def depth(self):
    imgL = cv2.imread('../images/tsukuba_l.png', 0)
    imgR = cv2.imread('../images/tsukuba_r.png', 0)
    stereo = cv2.StereoBM_create(numDisparities=16, blockSize=5)
    disparity = stereo.compute(imgL, imgR)
    plt.subplot(131), plt.imshow(imgL,'gray'), plt.title('leftImage'), plt.xticks([]), plt.yticks([])
    plt.subplot(132), plt.imshow(imgR,'gray'), plt.title('rightImage'), plt.xticks([]), plt.yticks([])
    plt.subplot(133), plt.imshow(disparity,'gray'), plt.title('disparity'), plt.xticks([]), plt.yticks([])
    plt.show()

在这里插入图片描述

发布了195 篇原创文章 · 获赞 59 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_36622009/article/details/104919996
今日推荐