计算机视觉三维重建代码

   三维重建.py

import homography
import sfm
from pylab import *
import sfm
import camera
import numpy as np
from PIL import Image
import camera
import matplotlib.pyplot as plt
import sift

# 标定矩阵
K = array([[2394,0,932],[0,2398,628],[0,0,1]])
# 载入图像,并计算特征
im1 = array(Image.open('img1.png'))
sift.process_image('img1.jpg','im1.sift')
l1,d1 = sift.read_features_from_file('im1.sift')
im2 = array(Image.open('img2.png'))
sift.process_image('img2.jpg','im2.sift')
l2,d2 = sift.read_features_from_file('im2.sift')
# 匹配特征
matches = sift.match_twosided(d1,d2)
ndx = matches.nonzero()[0]
# 使用齐次坐标表示,并使用 inv(K) 归一化
x1 = homography.make_homog(l1[ndx,:2].T)
ndx2 = [int(matches[i]) for i in ndx]
x2 = homography.make_homog(l2[ndx2,:2].T)
x1n = dot(inv(K),x1)
x2n = dot(inv(K),x2)
# 使用 RANSAC 方法估计 E
model = sfm.RansacModel()
E,inliers = sfm.F_from_ransac(x1n,x2n,model)
# 计算照相机矩阵(P2 是 4 个解的列表)
P1 = array([[1,0,0,0],[0,1,0,0],[0,0,1,0]])
P2 = sfm.compute_P_from_essential(E)

# 选取点在照相机前的解
ind = 0
maxres = 0
for i in range(4):
    # 三角剖分正确点,并计算每个照相机的深度
    X = sfm.triangulate(x1n[:, inliers], x2n[:, inliers], P1, P2[i])
    d1 = dot(P1, X)[2]
    d2 = dot(P2[i], X)[2]
    if sum(d1 > 0) + sum(d2 > 0) > maxres:
        maxres = sum(d1 > 0) + sum(d2 > 0)
        ind = i
        infront = (d1 > 0) & (d2 > 0)
    # 三角剖分正确点,并移除不在所有照相机前面的点
    X = sfm.triangulate(x1n[:, inliers], x2n[:, inliers], P1, P2[ind])
    X = X[:, infront]

    # 绘制三维图像
from mpl_toolkits.mplot3d import axes3d

fig = figure()
ax = fig.add_subplot(projection='3d')
ax.plot(-X[0], X[1], X[2], 'k.')
axis('off')


# 绘制 X 的投影
import camera
# 绘制三维点
cam1 = camera.Camera(P1)
cam2 = camera.Camera(P2[ind])
x1p = cam1.project(X)
x2p = cam2.project(X)


x1p = dot(K,x1p)
x2p = dot(K,x2p)
figure()
imshow(im1)
gray()
plot(x1p[0],x1p[1],'o')
plot(x1[0],x1[1],'r.')
axis('off')
figure()
imshow(im2)
gray()
plot(x2p[0],x2p[1],'o')
plot(x2[0],x2[1],'r.')
axis('off')
show()

sfm.py

from pylab import *
import camera
import numpy as np
from PIL import Image
import sfm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import pyransac



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):
    """ 在图像中,绘制外极点和外极线 F×x=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 < m)
    plot(t[ndx], lt[ndx], linewidth=2)

    if show_epipole:
        if epipole is None:
            epipole = compute_epipole(F)
        plot(epipole[0] / epipole[2], epipole[1] / epipole[2], 'r*')


def triangulate_point(x1, x2, P1, P2):
    """ 使用最小二乘解,绘制点对的三角剖分 """
    M = zeros((6, 6))
    M[:3, :4] = P1
    M[3:, :4] = P2
    M[:3, 4] = -x1
    M[3:, 5] = -x2
    U, S, V = linalg.svd(M)
    X = V[-1, :4]
    return X / X[3]


def triangulate(x1, x2, P1, P2):
    """ x1 和 x2(3×n 的齐次坐标表示)中点的二视图三角剖分 """
    n = x1.shape[1]
    if x2.shape[1] != n:
        raise ValueError("Number of points don't match.")
    X = [triangulate_point(x1[:, i], x2[:, i], P1, P2) for i in range(n)]
    return array(X).T


def compute_P(x, X):
    """ 由二维 - 三维对应对(齐次坐标表示)计算照相机矩阵 """
    n = x.shape[1]
    if X.shape[1] != n:
        raise ValueError("Number of points don't match.")
    # 创建用于计算 DLT 解的矩阵
    M = zeros((3 * n, 12 + n))
    for i in range(n):
        M[3 * i, 0:4] = X[:, i]
        M[3 * i + 1, 4:8] = X[:, i]
        M[3 * i + 2, 8:12] = X[:, i]
        M[3 * i:3 * i + 3, i + 12] = -x[:, i]
    U, S, V = linalg.svd(M)
    return V[-1, :12].reshape((3, 4))


def compute_P_from_fundamental(F):
    """ 从基础矩阵中计算第二个照相机矩阵(假设 P1 = [I 0])"""
    e = compute_epipole(F.T) # 左极点
    Te = skew(e)
    return vstack((dot(Te,F.T).T,e)).T


def skew(a):
    """ 反对称矩阵 A,使得对于每个 v 有 a×v=Av """
    return array([[0,-a[2],a[1]],[a[2],0,-a[0]],[-a[1],a[0],0]])


def compute_P_from_essential(E):
    """ 从本质矩阵中计算第二个照相机矩阵(假设 P1 = [I 0])
    输出为 4 个可能的照相机矩阵列表 """
    # 保证 E 的秩为 2
    U,S,V = svd(E)
    if det(dot(U,V))<0:
        V = -V
    E = dot(U,dot(diag([1,1,0]),V))
    # 创建矩阵(Hartley)
    Z = skew([0,0,-1])
    W = array([[0,-1,0],[1,0,0],[0,0,1]])
    # 返回所有(4 个)解
    P2 = [vstack((dot(U,dot(W,V)).T,U[:,2])).T,
        vstack((dot(U,dot(W,V)).T,-U[:,2])).T,
        vstack((dot(U,dot(W.T,V)).T,U[:,2])).T,
        vstack((dot(U,dot(W.T,V)).T,-U[:,2])).T]
    return P2


class RansacModel(object):
    """ 用从 http://www.scipy.org/Cookbook/RANSAC 下载的 ransac.py 计算基础矩阵的类 """

    def __init__(self, debug=False):
        self.debug = debug

    def fit(self, data):
        """ 使用选择的 8 个对应计算基础矩阵 """

        # 转置,并将数据分成两个点集
        data = data.T
        x1 = data[:3, :8]
        x2 = data[3:, :8]

        # 估计基础矩阵,并返回
        F = compute_fundamental_normalized(x1, x2)
        return F

    def get_error(self, data, F):
        """ 计算所有对应的 x^T F x,并返回每个变换后点的误差 """

        # 转置,并将数据分成两个点集

        data = data.T
        x1 = data[:3]
        x2 = data[3:]
        # 将 Sampson 距离用作误差度量
        Fx1 = dot(F, x1)
        Fx2 = dot(F, x2)
        denom = Fx1[0] ** 2 + Fx1[1] ** 2 + Fx2[0] ** 2 + Fx2[1] ** 2
        err = (diag(dot(x1.T, dot(F, x2)))) ** 2 / denom
        # 返回每个点的误差
        return err


def compute_fundamental_normalized(x1,x2):
    """ 使用归一化的八点算法,由对应点(x1,x2 3×n 的数组)计算基础矩阵 """
    n = x1.shape[1]
    if x2.shape[1] != n:
        raise ValueError("Number of points don't match.")
    # 归一化图像坐标
    x1 = x1 / x1[2]
    mean_1 = mean(x1[:2],axis=1)
    S1 = sqrt(2) / std(x1[:2])
    T1 = array([[S1,0,-S1*mean_1[0]],[0,S1,-S1*mean_1[1]],[0,0,1]])
    x1 = dot(T1,x1)
    x2 = x2 / x2[2]
    mean_2 = mean(x2[:2],axis=1)
    S2 = sqrt(2) / std(x2[:2])
    T2 = array([[S2,0,-S2*mean_2[0]],[0,S2,-S2*mean_2[1]],[0,0,1]])
    x2 = dot(T2,x2)
    # 使用归一化的坐标计算F
    F = compute_fundamental(x1,x2)
    # 反归一化
    F = dot(T1.T,dot(F,T2))
    return F/F[2,2]


def F_from_ransac(x1, x2, model, maxiter=5000, match_theshold=1e-6):
    """ 使用 RANSAN 方法(ransac.py,来自 http://www.scipy.org/Cookbook/RANSAC),
    从点对应中稳健地估计基础矩阵F
    输入:使用齐次坐标表示的点 x1,x2(3×n 的数组)"""
    from PCV.tools import ransac
    data = vstack((x1,x2))
    # 计算F,并返回正确点索引
    F,ransac_data = ransac.ransac(data.T,model,8,maxiter,match_theshold,20,return_all=True)
    return F, ransac_data['inliers']

sift.py

import os
from PIL import Image
from pylab import *
import sfm
import camera
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt


def process_image(imagename,resultname,params="--edge-thresh 10 --peak-thresh 5"):
    """ 处理一幅图像,然后将结果保存在文件中 """
    if imagename[-3:] != 'pgm':
        # 创建一个 pgm 文件
        im = Image.open(imagename).convert('L')
        im.save('tmp.pgm')
        imagename = 'tmp.pgm'
    cmmd = str(r"E:/pythonProject/sift.exe "+imagename+" --output="+resultname+
    " "+params)
    os.system(cmmd)
    print ('processed', imagename, 'to', resultname)


def read_features_from_file(filename):
    """ 读取特征属性值,然后将其以矩阵的形式返回 """
    f = loadtxt(filename)
    return f[:,:4],f[:,4:] # 特征位置,描述子


def write_features_to_file(filename,locs,desc):
    """ 将特征位置和描述子保存到文件中 """
    savetxt(filename,hstack((locs,desc)))


def plot_features(im,locs,circle=False):
    """ 显示带有特征的图像
    输入:im(数组图像),locs(每个特征的行、列、尺度和朝向)"""
    def draw_circle(c,r):
        t = arange(0,1.01,.01)*2*pi
        x = r*cos(t) + c[0]
        y = r*sin(t) + c[1]
        plot(x,y,'b',linewidth=2)
        imshow(im)
    if circle:
        for p in locs:
            draw_circle(p[:2],p[2])
    else:
        plot(locs[:,0],locs[:,1],'ob')
    axis('off')


def match(desc1,desc2):
    """ 对于第一幅图像中的每个描述子,选取其在第二幅图像中的匹配
    输入:desc1(第一幅图像中的描述子),desc2(第二幅图像中的描述子)"""
    desc1 = array([d/linalg.norm(d) for d in desc1])
    desc2 = array([d/linalg.norm(d) for d in desc2])
    dist_ratio = 0.6
    desc1_size = desc1.shape
    matchscores = zeros((desc1_size[0],1),'int')
    desc2t = desc2.T # 预先计算矩阵转置
    for i in range(desc1_size[0]):
        dotprods = dot(desc1[i,:],desc2t) # 向量点乘
        dotprods = 0.9999*dotprods
        # 反余弦和反排序,返回第二幅图像中特征的索引
        indx = argsort(arccos(dotprods))
        # 检查最近邻的角度是否小于 dist_ratio 乘以第二近邻的角度
        if arccos(dotprods)[indx[0]] < dist_ratio * arccos(dotprods)[indx[1]]:
            matchscores[i] = int(indx[0])
    return matchscores


def match_twosided(desc1,desc2):
    """ 双向对称版本的 match()"""
    matches_12 = match(desc1,desc2)
    matches_21 = match(desc2,desc1)
    ndx_12 = matches_12.nonzero()[0]
    # 去除不对称的匹配
    for n in ndx_12:
        if matches_21[int(matches_12[n])] != n:
            matches_12[n] = 0
    return matches_12








其中需要用到PCV库,pycharm无法下载,建议去查阅这篇文章下载https://blog.csdn.net/qq_31253399/article/details/88774467?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168110170616800211516208%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168110170616800211516208&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-88774467-null-null.142^v82^control,201^v4^add_ask,239^v2^insert_chatgpt&utm_term=PCV.tools&spm=1018.2226.3001.4187

如果你的python版本是3.x则需要更改PCV\tools\imtools.py中27行代码

print后面括起来

这是由于python3.x与python2.x的语法差异所致,3系列print使用print()函数所致

另外还有ransac.py内的print函数也需要更改

猜你喜欢

转载自blog.csdn.net/weixin_49502029/article/details/130059315