【Python计算机视觉编程】第五章 多视几何

这里写目录标题

    • 一. 对极几何
      • 1.1 概念
      • 1.2 五点共面约束
      • 1.3 对极平面束、对极平面、对极点、对极线
    • 二. 基础矩阵
      • 2.1 基础矩阵估算方法
        • 2.1.1 八点估算法
        • 2.1.1 八点估算法步骤
    • 三. 实验内容
      • 3.1 实验准备
      • 3.2 代码
      • 3.3 实验结果及分析
        • 3.3.1 运行结果
        • 3.3.2 结果分析
    • 四.小结

一. 对极几何

1.1 概念

  1. 对极几何,一定是对二幅图像而言,它是图像平面与以基线为轴的平面束的交的几何(基线是指连接摄像机中心的直线)
    以下图为例:对极几何描述的是左右两幅图像(点x和x’对应的图像)与以CC’为轴的平面束的交的几何
    【Python计算机视觉编程】第五章 多视几何_第1张图片
  2. 直线CC’为基线,以该基线为轴存在一个平面束,该平面束与两幅图像平面相交,下图可以看到,该平面束中不同平面与两幅图像相交于不同直线
    【Python计算机视觉编程】第五章 多视几何_第2张图片

1.2 五点共面约束

【Python计算机视觉编程】第五章 多视几何_第3张图片

点x、x’与摄像机中心C和C’是共面的,并且与空间点X也是空面的,这5个点共面于平面π。这是一个最本质的约束,即5个点决定了一个平面π;
由该约束,可以推导出一个重要性质:由图像点x和x’反投影的射线共面,并且,在平面π上。

1.3 对极平面束、对极平面、对极点、对极线

  1. 对极平面束(epipolar pencil):以基线为轴的平面束;【Python计算机视觉编程】第五章 多视几何_第4张图片
  2. 对极平面(epipolar plane):任何包含基线的平面都称为对极平面,或者说是对极平面束中的平面
    【Python计算机视觉编程】第五章 多视几何_第5张图片
  3. 对极点(epipole):摄像机的基线与每幅图像的交点;即上图中的点e和e’
  4. 对极线(epipolar line):对极平面与图像的交线;例如,上图中的直线l和l’

二. 基础矩阵

基础矩阵是对极几何的代数表达方式,基础矩阵描述了图像中任意对应点 x↔x’ 之间的
约束关系
【Python计算机视觉编程】第五章 多视几何_第6张图片

2.1 基础矩阵估算方法

2.1.1 八点估算法

【Python计算机视觉编程】第五章 多视几何_第7张图片
所以可得到方程:
【Python计算机视觉编程】第五章 多视几何_第8张图片
即相应方程式为:
在这里插入图片描述
给定n组点的集合,我们有如下方程:
在这里插入图片描述
如果存在确定(非零)解,则系数矩阵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计算机视觉编程】第五章 多视几何_第9张图片
因为要秩为2,所以取最后一个元素设置为0,则
【Python计算机视觉编程】第五章 多视几何_第10张图片
最终的解
在这里插入图片描述

2.1.1 八点估算法步骤

第一步,导入两幅图像,并使用sift算法提取特征;
第二步,使用函数match_twosided连接两幅图的特征;
第三步,RANSAC去除错误点匹配;
第四步,归一化8点算法估计基础矩阵。这是通过对应点来计算基础矩阵的算法。

三. 实验内容

  1. 匹配点计算基础矩阵(七点、八点、十点)
  2. 对上述情况画出极点和极线(坐标均匀分布于各行)

3.1 实验准备

  1. 极点位于像平面
    【Python计算机视觉编程】第五章 多视几何_第11张图片
  2. 基线平行像平面
    【Python计算机视觉编程】第五章 多视几何_第12张图片
  3. 相机前后方位关系
    【Python计算机视觉编程】第五章 多视几何_第13张图片

3.2 代码

计算基础矩阵:

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

im1 = array(Image.open('33.jpg'))
sift.process_image('33.jpg', 'im1.sift')

im2 = array(Image.open('34.jpg'))
sift.process_image('34.jpg', 'im2.sift')

l1, d1 = sift.read_features_from_file('im1.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-6):
    """ 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-5)
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]

画极点和极线

# -*- coding: utf-8 -*-
from pylab import *
from mpl_toolkits.mplot3d import axes3d
import sfm

#在前两个视图中点的索引
ndx = (corr[:,0]>=0) & (corr[:,1]>=0)

#获得坐标,并将其用齐次坐标表示
x1 = points2D[0][:,corr[ndx,0]]
x1 = vstack((x1,ones(x1.shape[1])))
x2 = points2D[1][:,corr[ndx,1]]
x2 = vstack((x2,ones(x2.shape[1])))

#计算F
F = sfm.compute_fundamental(x1,x2)

#计算极点
e = sfm.compute_epipole(F)

#绘制图像
figure()
imshow(im1)

#分别绘制每条线,这样会绘制出很漂亮的颜色
for i in range(5):
    sfm.plot_epipolar_line(im1,F,x2[:,i],e,False)
axis('off')

figure()
imshow(im2)

#分别绘制每个点,这样绘制出和线同样的颜色
for i in range(5):
    plot(x2[0,i],x2[1,i],'o')
axis('off')
show()

sfm.py:

# -*- coding: utf-8 -*-

def compute_fundamental(x1,x2):
    """使用归一化的八点算法,从对应点(x1,x2 3*n 的数组)中计算基础矩阵
    每行由如下构成:
    [x'*x,x'*y,x',y'*x,y'*y,y',x,y,1]"""
    
    n = x1.shape[1]
    if x2.shape[1]!=n:
        raise ValueError("Number of points don't match.")
        
    #创建方程对应的矩阵
    A = zeros((n,9))
    for i in range(n):
        A[i] = [x1[0,i]*x2[0,i],x1[0,i]*x2[1,i],x1[0,i]*x2[2,i],
          x1[1,i]*x2[0,i],x1[1,i]*x2[1,i],x1[1,i]*x2[2,i],
          x1[2,i]*x2[0,i],x1[2,i]*x2[1,i],x1[2,i]*x2[2,i] ]
        
    #计算线性最小二乘解
    U,S,V = linalg.svd(A)
    F = V[-1].reshape(3,3)
    
    #受限F
    #通过将最后一个奇异值置0,使秩为2
    U,S,V = linalg.svd(F)
    S[2] = 0
    F = dot(U,dot(diag(S),V))
    
    return F

def compute_epipole(F):
    """从基础矩阵F中计算右极点(可以使用F,T获得左极点)"""
    
    #返回F的零空间(FX=0)
    U,S,V = linalg.svd(F)
    e = V[-1]
    
    return e/e[2]
    
def plot_epipolar_line(im,F,x,epipole=None,show_epipole=True):
    """在图像中,绘制外极点和外极线 Fx=0 。F是基础矩阵,x是另一幅图像中的点"""
    
    m,n = im.shape[:2]
    line =dot(F,x)
    
    #外机线参数和值
    t = linspace(0,n,100)
    lt = array([(line[2]+line[0]*tt)/(-line[1]) for tt in t])
    
    #仅仅处理位于图像内部的点和线
    ndx = (lt>=0) & (lt

3.3 实验结果及分析

3.3.1 运行结果

  1. 极点位于像平面
    sift匹配结果:
    【Python计算机视觉编程】第五章 多视几何_第14张图片
    优化后:
    【Python计算机视觉编程】第五章 多视几何_第15张图片【Python计算机视觉编程】第五章 多视几何_第16张图片
    基础矩阵F:
    在这里插入图片描述
    p1 p2 p3相机矩阵:
    【Python计算机视觉编程】第五章 多视几何_第17张图片
  2. 基线平行像平面
    sift匹配结果:
    【Python计算机视觉编程】第五章 多视几何_第18张图片
    优化后的结果:
    【Python计算机视觉编程】第五章 多视几何_第19张图片
    【Python计算机视觉编程】第五章 多视几何_第20张图片
    基础矩阵F:
    在这里插入图片描述
    p1 p2 p3相机矩阵:
    【Python计算机视觉编程】第五章 多视几何_第21张图片
  3. 相机前后方位关系
    【Python计算机视觉编程】第五章 多视几何_第22张图片
    【Python计算机视觉编程】第五章 多视几何_第23张图片
    【Python计算机视觉编程】第五章 多视几何_第24张图片
    基础矩阵F:
    在这里插入图片描述
    p1 p2 p3相机矩阵:
    【Python计算机视觉编程】第五章 多视几何_第25张图片

3.3.2 结果分析

  1. 根据匹配结果可推测出,平行拍摄和左右角度变换拍摄的匹配效果差一点,优化后剩余匹配较少
  2. 第一组和第三组明显特征被保留效果更好,第三组前后位距离变化的图像处理结果错误匹配明显剔除更多。
  3. 距离较远的目标剔除错误匹配难度较大
  4. 输出的矩阵分别为基础矩阵和三张图片的投影矩阵。先计算第一对图像中的三维点,匹配特征到剩下的图像对应,然后根据对应的三维点计算其他图像的相机矩阵。
    在这里插入图片描述
    画极线出现错误,但是修改多次不成功,换取方法后由于numpy问题依旧无法解决。

四.小结

  1. sift算法本身有时候会因为匹配点附近太过相似而出现错误匹配,这个算法便能在这个基础上相对优化,避开错误的匹配
  2. 远近相同的测试图片效果会比远近相差较大的图片要好,更容易匹配;
  3. 在进行远近,角度等变换的时候,幅度不容过大,否则容易出现
    ValueError: did not meet fit acceptance criteria 这一错误;
  4. 室外取景普遍比室内取景效果更好,大概是光线原因造成的影响。

你可能感兴趣的:(【Python计算机视觉编程】第五章 多视几何)