python计算机视觉编程——基础矩阵和对极几何

基础矩阵

    • 一、基础矩阵原理
    • 1.1原理定义
    • 1.2几何推导
    • 1.3基础矩阵性质
    • 1.4基础矩阵估算方法
  • 二、实验测试
    • 2.1实验要求
    • 2.2.1实验一
      • 2.2.1.1代码
      • 2.2.1.2结果分析
    • 2.2.2实验二
      • 2.2.2.1代码
      • 2.2.2.2分析
    • 2.3 实验总结

此次实验的内容主要有关于基础矩阵的内容,与教材《python计算机视觉编程》衔接。

一、基础矩阵原理

用两个相机在不同位置拍摄同一物体,两张照片中的景物有重叠部分,那么理论上这两张照片会存在一定的对应关系即外极几何。

1.1原理定义

两幅视图存在两个关系:第一种,通过对极几何一幅图像上的点可以确定另外一幅图像上的一条直线;另外一种,通过上一种映射,一幅图像上的点可以确定另外一幅图像上的一个点,这个点是第一幅图像通过光心和图像点的射线与一个平面的交点在第二幅图像上的影像。第一种情况可以用基础矩阵来表示,第二种情况则用单应矩阵来表示。而本质矩阵则是基础矩阵的一种特殊情况,是在归一化图像坐标下的基础矩阵。

基础矩阵(Fundamental Matrix)

基础矩阵体现了两视图几何(对极几何,epipolar geometry)的内在射影几何(projective geometry)关系,基础矩阵只依赖于摄像机的内参KK和外参R,tR,t。
python计算机视觉编程——基础矩阵和对极几何_第1张图片

1.2几何推导

首先在几何层面对上述一个图像上的点到另外一个图像上的对极线的映射关系进行分析。可以把这个映射分成两个步骤:

1.点到平面的转移

存在一个不经过两个相机光心的的平面ππ,光心OO与xx的射线与平面ππ相交与一点XX。该点XX又投影到第二幅图像平面上的点x′x′。这个称为点xx通过平面ππ的转移。点xx,x′x′是平面ππ上的3D点XX在两个相机平面上的像。对应每一个3D点XX都存在一个2D的单应HπHπ把每一个xx映射到x′x′。

2.对极线的构造

已知第二幅图像上的对应点x′x′,再加上对极点e′e′我们可以构造对极线l′=e′×x′=[e′]×x′l′=e′×x′=[e′]×x′(这里的[e′]×[e′]×是叉乘矩阵)。由于x′x′可以表示为x′=Hπxx′=Hπx,则有:

				l′=[e′]×Hπx=Fx
				l′=[e′]×Hπx=Fx

基本矩阵可以记作为F=[e′]×HπF=[e′]×Hπ,其中HπHπ是一幅图像到另外一幅图像通过任意平面ππ的转移映射。因为[e′]×[e′]×的秩是2,HπHπ的秩是3,因此FF是秩为2的矩阵。

从几何角度来讲,F矩阵表示了第一幅图像的2维射影平面P2P2到通过对极点e′e′的对极线束的映射,是从2维到1维的射影空间的映射,因此秩为2。
python计算机视觉编程——基础矩阵和对极几何_第2张图片

1.3基础矩阵性质

(1) 基础矩阵是秩为2、自由度为7的齐次矩阵。(3×3其次矩阵有8个独立比率,还有一个满足约束detF=0的约束,所以再减去一个自由度)
(2)若x与x’是两幅图上的对应点,那么x′TFx=0。
(3)l’是对应于x的对极线,l′=Fx。
(4)若e是第二个摄像机光心在第一幅图像上的极点,那么Fe=0 。

1.4基础矩阵估算方法

八点估算法
基本矩阵是由该方程定义的:
x′TFx=0
其中x↔x′是两幅图像的任意一对匹配点。由于每一组点的匹配提供了计算F系数的一个线性方程,当给定至少7个点(3×3的齐次矩阵减去一个尺度,以及一个秩为2的约束),方程就可以计算出未知的F。我们记点的坐标为x=(x,y,1)T,x′=(x′,y′,1)T
又因为F为:
python计算机视觉编程——基础矩阵和对极几何_第3张图片
所以可得到方程:
在这里插入图片描述
即相应方程式为
在这里插入图片描述
给定n组点的集合,我们有如下方程:
python计算机视觉编程——基础矩阵和对极几何_第4张图片
如果存在确定(非零)解,则系数矩阵A的秩最多是8。由于F是齐次矩阵,所以如果矩阵A的秩为8,则在差一个尺度因子的情况下解是唯一的。可以直接用线性算法解得。

如果由于点坐标存在噪声则矩阵AA的秩可能大于8(也就是等于9,由于A是n×9的矩阵)。这时候就需要求最小二乘解,这里就可以用SVD来求解,f的解就是系数矩阵A最小奇异值对应的奇异向量,也就是A奇异值分解后A=UDVT中矩阵V的最后一列矢量,这是在解矢量f在约束∥f∥下取∥Af∥最小的解。以上算法是解基本矩阵的基本方法,称为8点算法。

上述求解后的F不一定能满足秩为2的约束,因此还要在F的基础上加以约束。通过SVD分解可以解决,令F=UΣVT,则
python计算机视觉编程——基础矩阵和对极几何_第5张图片
因为要秩为2,所以取最后一个元素强制设置为0,则令:
python计算机视觉编程——基础矩阵和对极几何_第6张图片

最终的解为:在这里插入图片描述

二、实验测试

2.1实验要求

实验一.分别用七点、八点、十点(匹配点),计算基础矩阵
实验图片包含三种情况,即
(1)左右拍摄,极点位于图像平面上
(2)像平面接近平行,极点位于无穷远
(3)图像拍摄位置位于前后
实验二. 针对上述情况,画出极点和极线,其中点坐标要均匀分布于各行

2.2.1实验一

2.2.1.1代码

from PIL import Image
from numpy import *
from pylab import *
import numpy as np

import PCV.geometry.camera as camera
import PCV.geometry.homography as homography
import PCV.geometry.sfm as sfm
import PCV.localdescriptors.sift as sift

image1='E:/thirddown/computervision/data/fun/image6.jpg'
im1 = array(Image.open(image1))
sift.process_image(image1, 'im1.sift')
l1, d1 = sift.read_features_from_file('im1.sift')

image2='E:/thirddown/computervision/data/fun/image7.jpg'
im2 = array(Image.open(image2))
sift.process_image(image2, 'im2.sift')
l2, d2 = sift.read_features_from_file('im2.sift')


matches = sift.match_twosided(d1, d2)

ndx = matches.nonzero()[0]
x1 = homography.make_homog(l1[ndx, :2].T)
ndx2 = [int(matches[i]) for i in ndx]
x2 = homography.make_homog(l2[ndx2, :2].T)

d1n = d1[ndx]
d2n = d2[ndx2]
x1n = x1.copy()
x2n = x2.copy()

figure(figsize=(16,16))
sift.plot_matches(im1, im2, l1, l2, matches, True)
show()

def F_from_ransac(x1, x2, model, maxiter=5000, match_threshold=1e-3):
    """ Robust estimation of a fundamental matrix F from point
    correspondences using RANSAC (ransac.py from
    http://www.scipy.org/Cookbook/RANSAC).

    input: x1, x2 (3*n arrays) points in hom. coordinates. """

    import PCV.tools.ransac as ransac
    data = np.vstack((x1, x2))
    d = 10 # 20 is the original
    F, ransac_data = ransac.ransac(data.T, model,
                                   8, maxiter, match_threshold, d, return_all=True)
    return F, ransac_data['inliers']

model = sfm.RansacModel()
F, inliers = F_from_ransac(x1n, x2n, model, maxiter=5000, match_threshold=1e-3)
print (F)

P1 = array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])
P2 = sfm.compute_P_from_fundamental(F)

X = sfm.triangulate(x1n[:, inliers], x2n[:, inliers], P1, P2)


cam1 = camera.Camera(P1)
cam2 = camera.Camera(P2)
x1p = cam1.project(X)
x2p = cam2.project(X)

figure(figsize=(16, 16))
imj = sift.appendimages(im1, im2)
imj = vstack((imj, imj))

imshow(imj)

cols1 = im1.shape[1]
rows1 = im1.shape[0]
for i in range(len(x1p[0])):
    if (0<= x1p[0][i]<cols1) and (0<= x2p[0][i]<cols1) and (0<=x1p[1][i]<rows1) and (0<=x2p[1][i]<rows1):
        plot([x1p[0][i], x2p[0][i]+cols1],[x1p[1][i], x2p[1][i]],'c')
axis('off')
show()

d1p = d1n[inliers]
d2p = d2n[inliers]


print (P1)
print (P2)

通过修改:
在这里插入图片描述
这段代码中的8,来设置7/10点算法。

2.2.1.2结果分析

场景一:
python计算机视觉编程——基础矩阵和对极几何_第7张图片

得出最终结果如下用基础矩阵八点算法转化以后:
python计算机视觉编程——基础矩阵和对极几何_第8张图片
python计算机视觉编程——基础矩阵和对极几何_第9张图片
场景二:

python计算机视觉编程——基础矩阵和对极几何_第10张图片
用基础矩阵转化以后:
python计算机视觉编程——基础矩阵和对极几何_第11张图片
python计算机视觉编程——基础矩阵和对极几何_第12张图片
场景三:
python计算机视觉编程——基础矩阵和对极几何_第13张图片
用基础矩阵转化以后:

python计算机视觉编程——基础矩阵和对极几何_第14张图片python计算机视觉编程——基础矩阵和对极几何_第15张图片

以上展示的都为八点算法的结果。
以下对应展示7/8/10点算法的结果:

场景一的七/八/十点:
python计算机视觉编程——基础矩阵和对极几何_第16张图片
python计算机视觉编程——基础矩阵和对极几何_第17张图片python计算机视觉编程——基础矩阵和对极几何_第18张图片
场景二的对应七点 十点:

python计算机视觉编程——基础矩阵和对极几何_第19张图片
python计算机视觉编程——基础矩阵和对极几何_第20张图片python计算机视觉编程——基础矩阵和对极几何_第21张图片
场景三:
python计算机视觉编程——基础矩阵和对极几何_第22张图片

python计算机视觉编程——基础矩阵和对极几何_第23张图片
python计算机视觉编程——基础矩阵和对极几何_第24张图片
由上可以看出七点算法虽然求的点少,速度快,但是对于过少的选择,无法对错配的点找出可替换解决方案,受错配影响大。十点法求的点多,但是因为所求点多,在最终选取上找到点可能不是最优,所以选取八点法是较为适中的决定。

2.2.2实验二

2.2.2.1代码

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt


def drawlines(img1, img2, lines, pts1, pts2):
    ''' img1 - image on which we draw the epilines for the points in img2
        lines - corresponding epilines '''
    r, c = img1.shape
    img1 = cv.cvtColor(img1, cv.COLOR_GRAY2BGR)
    img2 = cv.cvtColor(img2, cv.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 = cv.line(img1, (x0, y0), (x1, y1), color, 1)
        img1 = cv.circle(img1, tuple(pt1), 5, color, -1)
        img2 = cv.circle(img2, tuple(pt2), 5, color, -1)
    return img1, img2


img1 = cv.imread('E:/thirddown/computervision/data/fun/image1.jpg', 0)  # queryimage # left image
img2 = cv.imread('E:/thirddown/computervision/data/fun/image2.jpg', 0)  # trainimage # right image
sift = cv.xfeatures2d.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
good = []
pts1 = []
pts2 = []
# ratio test as per Lowe's paper
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 = cv.findFundamentalMat(pts1, pts2, cv.FM_LMEDS)
# We select only inlier points
pts1 = pts1[mask.ravel() == 1]
pts2 = pts2[mask.ravel() == 1]

# Find epilines corresponding to points in right image (second image) and
# drawing its lines on left image
lines1 = cv.computeCorrespondEpilines(pts2.reshape(-1, 1, 2), 2, F)
lines1 = lines1.reshape(-1, 3)
img5, img6 = drawlines(img1, img2, lines1, pts1, pts2)
# Find epilines corresponding to points in left image (first image) and
# drawing its lines on right image
lines2 = cv.computeCorrespondEpilines(pts1.reshape(-1, 1, 2), 1, F)
lines2 = lines2.reshape(-1, 3)
img3, img4 = drawlines(img2, img1, lines2, pts2, pts1)
plt.subplot(121), plt.imshow(img5)
plt.subplot(122), plt.imshow(img3)
plt.show()

2.2.2.2分析

对三组图进行画极线:
(1)左右拍摄,极点位于图像平面上
python计算机视觉编程——基础矩阵和对极几何_第25张图片
(2)像平面接近平行,极点位于无穷远
python计算机视觉编程——基础矩阵和对极几何_第26张图片
(3)图像拍摄位置位于前后
python计算机视觉编程——基础矩阵和对极几何_第27张图片

2.3 实验总结

对于实验一,用到的点数越小找到的重建点也越少,容易受错配影响较大。用十点虽然算法运行更慢,消耗更多,但是又更多的点可供确认选择,精度更高。图像压缩之后会丢失很多匹配特征点,因此在计算基础矩阵是一定要控制好图像压缩大小。
对于实验二,当已知图像上的点x1和基础矩阵F时是无法获得对应的像素点下x2的,只能获得x1对应的极线。
在实验中会遇到问题:
在这里插入图片描述
这是因为图像sift特征匹配的匹配点数过少,这时候需要修改代码中匹配的阈值。但是,sift特征匹配的匹配对过少的话,会对后续进行ransac算法实现产生影响,所以拍摄图片的时候要特别注意。

你可能感兴趣的:(python计算机视觉编程——基础矩阵和对极几何)