利用python学习OpenCV,个人感觉比较方便。函数的形式与C++基本相同,所以切换过来还是比较好的,对于像我这种对python不太熟练的人,使用python的集成开发环境PyCharm进行学习,可以设置断点调试,有助于我这类初学者理解掌握。
在我们使用针孔相机时,我们会丢失大量重要的信心,比如说图像的深度,或者说图像上的点和摄像机的距离,因这是一个从3D 到2D 的转换。因此一个重要的问题就产生了,使用这样的摄像机我们能否计算除深度信息呢?答案就是使用多个相机。我们的眼睛就是这样工作的,使用两个摄像机(两个眼睛),这被称为立体视觉。
在进入深度图像之前,我们要先掌握一些多视角几何的基本概念。在本节中我们要处理对极几何。下图为使用两台摄像机同时对一个一个场景进行拍摄的示意图。
如果只是用一台摄像机我们不可能知道3D 空间中的X 点到图像平面的距离,因为OX 连线上的每个点投影到图像平面上的点都是相同的。但是如果我们也考虑上右侧图像的话,直线OX 上的点将投影到右侧图像上的不同位置。所以根据这两幅图像,我们就可以使用三角测量计算出3D 空间中的点到摄像机的距离(深度)。这就是整个思路。
直线OX 上的不同点投射到右侧图像上形成的线l′ 被称为与x 点对应的极线。也就是说,我们可以在右侧图像中沿着这条极线找到x 点。它可能在这条直线上某个位置(这意味着对两幅图像间匹配特征的二维搜索就转变成了沿着极线的一维搜索。这不仅节省了大量的计算,还允许我们排除许多导致虚假匹配的点)。这被称为对极约束。与此相同,所有的点在其他图像中都有与之对应的极线。平面XOO' 被称为对极平面。
O 和O' 是摄像机的中心。从上面的示意图可以看出,右侧摄像机的中心O' 投影到左侧图像平面的e 点,这个点就被称为极点。极点就是摄像机中心连线与图像平面的交点。因此点e' 是左侧摄像机的极点。有些情况下,我们可能不会在图像中找到极点,它们可能落在了图像之外(这说明这两个摄像机不能拍摄到彼此)。
所有的极线都要经过极点。所以为了找到极点的位置,我们可以先找到多条极线,这些极线的交点就是极点。本节我们的重点就是找到极线和极点。为了找到它们,我们还需要两个元素,本征矩阵(E)和基础矩阵(F)。本征矩阵包含了物理空间中两个摄像机相关的旋转和平移信息。如下图所示。
基础矩阵F 除了包含E 的信息外还包含了两个摄像机的内参数。由于F包含了这些内参数,因此它可以它在像素坐标系将两台摄像机关联起来(如果使用是校正之后的图像并通过除以焦距进行了归一化,F=E)。简单来说,基础矩阵F 将一副图像中的点映射到另一幅图像中的线(极线)上。这是通过匹配两幅图像上的点来实现的。要计算基础矩阵至少需要8 个点(使用8 点算法)。点越多越好,可以使用RANSAC 算法得到更加稳定的结果。
下面是相关的测试代码:
# -*- coding:utf-8 -*-
__author__ = 'Microcosm'
import cv2
import numpy as np
from matplotlib import pyplot as plt
img_L = cv2.imread("E:\python\Python Project\opencv_showimage\images\stereoBM\\tsukuba_l.png",0)
img_R = cv2.imread("E:\python\Python Project\opencv_showimage\images\stereoBM\\tsukuba_r.png",0)
sift = cv2.SIFT()
kp1,des1 = sift.detectAndCompute(img_L,None)
kp2,des2 = sift.detectAndCompute(img_R,None)
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.2*n.distance:
good.append(m)
pts1.append(kp1[m.queryIdx].pt)
pts2.append(kp2[m.trainIdx].pt)
pts1 = np.float32(pts1)
pts2 = np.float32(pts2)
# 根据匹配点对计算基础矩阵
F, mask = cv2.findFundamentalMat(pts1,pts2,cv2.FM_LMEDS)
# 寻则内部点
pts1 = pts1[mask.ravel()==1]
pts2 = pts2[mask.ravel()==1]
# 绘制极线的函数
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[1]*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
# 计算并绘制两幅图像中的极线
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)
lines2 = cv2.computeCorrespondEpilines(pts1.reshape(-1,1,2),2,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()
结果图为: