Aim:
学习多视角几何基础;
学习极点、极线、对极约束。
基本概念
- 使用针孔相机时,由于是从3D到2D的转换,丢失大量信息,比如:图像深度、图像上点和摄像机的距离。
- 如何计算深度信息:使用多个相机,称为立体视觉。
- 使用两台摄像机同时对一个场景拍摄的示意图。
- 由于OX连线上的每个点投影到图像平面上的点相同,直线OX上的点又投影到右侧图像上的不同位置。所以根据两幅图像,可以使用三角测量计算出3D空间中的点到摄像机的距离。
- 与X点对应的极线:直线OX上的不同点投射到右侧图像上形成的线L‘。
- 对极约束:X点在极线上的某个位置,从而将二维搜索转变为对极线的一维搜索。
- 对极平面:所有的点在其他图像中都有与之相对的极线,即为此平面。
- 极点:摄像机中心连线与图像平面的交点。极点若在图像外则图像中找不到极点,说明两个摄像机不能拍摄到彼此。
- 极线的交点是极点、
- 本征矩阵 E :包含物理空间中两个摄像机相关的旋转和平移信息。
- 基础矩阵 F:除了包含E的信息,还包含两个摄像机的内参数。故可以在像素坐标系将两台摄像机关联起来。使用校正后的图像并通过除以焦距进行归一化后,F=E。
- 基础矩阵F将一副图像上的点映射到另一幅图像的线上,通过匹配两幅图像上的点实现。至少8个点(8点算法)才能计算基础矩阵。点越多越好,可以使用RANSAC算法得到更稳定的结果。
代码演示
为了在两幅图像中找到尽量多的匹配点得到基础矩阵,使用SIFT描述符、FLANN匹配器和比值检测。
#coding=utf-8
import cv2
import numpy as np
from matplotlib import pyplot as plt
img1=cv2.imread('./image2/mario.jpg',0)
img2=cv2.imread('./image2/coin.jpg',0)
sift=cv2.SIFT()
kp1,des1=sift.detectAndCompute(img1,None)
kp2,des2=sift.detectAndCompute(img2,None)
FLANN_INDEX_KDTREE=0
index_params=dict(algorithm=FLANN_INDEX_KDTREE,trees=5)
search_params=dict(checks=50)
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)
pts2.append(kp2[m.trainIdx].pt)
pts1.append(kp1[m.queryIdx].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]
#找出极线,得到包含很多线的数组。
#定义函数将线绘制到图像中
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]])
img1=cv2.line(img1,(x0,y0),(x1,y1),color,1)
img1=cv2.circle(img1,tuple(pt1),5,color,-1)
img2=cv2.circle(img2,tuple(pt2),5,color,-1)
return img1,img2
#在两幅图像中计算并绘制极线
lines1=cv2.computeCorrespondEpilines()