python计算机视觉——第四章 照相机模型与增强现实

目录

4.1 针孔照相机模型

4.1.1 照相机矩阵

4.1.2 三维点的投影

4.1.3 照相机矩阵的分解 

4.1.4 计算照相机中心 

4.2 照相机标定

4.2.1 一个简单的标定方法 

4.3 以平面和标记物进行姿态估计 

4.4 增强现实

4.4.1 PyGame和PyOpenGL 

4.4.2 从照相机矩阵到OpenGL格式 

 4.4.3 在图像中放置虚拟物体

4.4.4 综合集成


4.1 针孔照相机模型

        针孔照相机模型(有时称为射影照相机模型)是计算机视觉中广泛使用的照相机模型。对于大多数应用来说,针孔照相机模型简单,并且具有足够的精确度。这个名字来源于一种类似暗箱机的照相机。该照相机从一个小孔采集射到暗箱内部的光线。在针孔照相机模型中,在光线投影到图像平面之前,从唯一一个点经过,也就是照相机中心C。

         由图像坐标轴和三维坐标系中的x 轴和y 轴对齐平行的假设,我们可以得出针孔照相机的投影性质。照相机的光学坐标轴和z 轴一致,该投影几何可以简化成相似三角形。在投影之前通过旋转和平移变换,对该坐标系加入三维点,会出现完整的投影变换。

        在针孔照相机中,三维点X 投影为图像点x(两个点都是用齐次坐标表示的),如下所示:

\(\lambda\mathbf{x}=P\mathbf{X}\)

        这里,3×4 的矩阵P 为照相机矩阵(或投影矩阵)。注意,在齐次坐标系中,三维点X 的坐标由4 个元素组成,X=[X, Y, Z, W]。这里的标量λ 是三维点的逆深度。如果我们打算在齐次坐标中将最后一个数值归一化为1,那么就会使用到它。 

4.1.1 照相机矩阵

照相机矩阵可以分解为:

\(P=K[R|t]\)

        其中,R 是描述照相机方向的旋转矩阵,t 是描述照相机中心位置的三维平移向量,内标定矩阵K 描述照相机的投影性质。 

        标定矩阵仅和照相机自身的情况相关,通常情况下可以写成:

\(K=\begin{bmatrix}\alpha f&s&c_x\\0&f&c_y\\0&0&1\end{bmatrix}\)

        图像平面和照相机中心间的距离为焦距f。当像素数组在传感器上偏斜的时候,需要用到倾斜参数s。在大多数情况下,s 可以设置成0。也就是说: 

\(K=\begin{bmatrix}f_x&0&c_x\\0&f_y&c_y\\0&0&1\end{bmatrix}\)

        这里,我们使用了另外的记号\(f_x\)和\(f_y\),两者关系为\(f_{x}=af_{y}\)。

        纵横比例参数α 是在像素元素非正方形的情况下使用的。通常情况下,我们可以默认设置α=1。经过这些假设,标定矩阵变为: 

\(K=\begin{bmatrix}f&0&c_x\\0&f&c_y\\0&0&1\end{bmatrix}\)

        除焦距之外,标定矩阵中剩余的唯一参数为光心(有时称主点)的坐标c=[cx,cy],也就是光线坐标轴和图像平面的交点。因为光心通常在图像的中心,并且图像的坐标是从左上角开始计算的,所以光心的坐标常接近于图像宽度和高度的一半。特别强调一点,在这个例子中,唯一未知的变量是焦距f。 

4.1.2 三维点的投影

 下面来创建照相机类,用来处理我们对照相机和投影建模所需要的全部操作:

from scipy import linalg
from pylab import  *

class Camera(object):
    """ 表示针孔照相机的类"""
    def __init__(self,P):
        """ 初始化 P = K[R|t] 照相机模型"""
        self.P = P
        self.K = None # 标定矩阵
        self.R = None # 旋转
        self.t = None # 平移
        self.c = None # 照相机中心

    def project(self,X):
        """ X(4×n 的数组)的投影点,并且进行坐标归一化 """
        x = dot(self.P,X)
        for i in range(3):
            x[i] /= x[2]
        return x

        在这个例子中,我们将使用牛津多视图数据集中的“Model Housing”数据集,可以从http://www.robots.ox.ac.uk/~vgg/data/data-mview.html 下载。 

4.1.3 照相机矩阵的分解 

        如果给定如方程(4.1.1)所示的照相机矩阵P,我们需要恢复内参数K 以及照相机的位置t 和姿势R。矩阵分块操作称为因子分解。这里,我们将使用一种矩阵因子分解的方法,称为RQ 因子分解。 

        将下面的方法添加到Camera 类中:

def factor(self):
        """ 将照相机矩阵分解为K、R、t,其中,P = K[R|t] """
        # 分解前3×3 的部分
        K,R = linalg.rq(self.P[:,:3])
        # 将K 的对角线元素设为正值
        T = diag(sign(diag(K)))
        if linalg.det(T) < 0:
            T[1,1] *= -1
        self.K = dot(K,T)
        self.R = dot(T,R) # T 的逆矩阵为其自身
        self.t = dot(linalg.inv(self.K),self.P[:,3])
        return self.K, self.R, self.t

        RQ 因子分解的结果并不是唯一的。在该因子分解中,分解的结果存在符号二义性。由于我们需要限制旋转矩阵R 为正定的(否则,旋转坐标轴即可),所以可以在求解到的结果中加入变换T 来改变符号。

        观察照相机矩阵分解的效果:

4.1.4 计算照相机中心 

        给定照相机投影矩阵P,我们可以计算出空间上照相机的所在位置。照相机的中心
C,是一个三维点,满足约束PC=0。对于投影矩阵为P=K[R|t] 的照相机,有: 

\(K[R|t]\text{C}=K R\text{C}+Kt=0\)

照相机的中心可以由下述式子来计算: 

\(\mathbf{C}=-R^Tt\)

注意,如预期一样,照相机的中心和内标定矩阵K 无关。 

        下面的代码可以按照上面公式计算照相机的中心。将其添加到Camera 类中,该方法
会返回照相机的中心:

    def center(self):
        """计算并返回照相机的中心"""
        
        if self.c is not None:
            return self.c
        else:
            #通过因子分解计算c
            self.factor()
            self.c = -dot(self.R.T,self.t)
            return self.c

4.2 照相机标定

        简单来说就是从世界坐标系换到图像坐标系的过程,也就是求最终的投影矩阵P的过程。 

4.2.1 一个简单的标定方法 

        需要准备一个平面矩形的标定物体(一本书即可)、用于测量的卷尺或直尺,以及一个平面。具体操作步骤如下: 

  1. 测量你选定矩形标定物体的边长dX和dY;
  2. 将照相机和标定物体放置在平面上,使得照相机的背面和标定物体平行,同时物体位于照相机图像视图的中心,你可能需要调整照相机或者物体来获得良好的对齐效果;
  3. 测量标定物体到照相机的距离dZ;
  4. 拍摄一幅图像来检验该设置是否正确,即标定物体的边要和图像的行和列对齐;
  5. 使用像素数来测量标定物体图像的宽度和高度dx和dy。

        使用下面的相似三角形关系可以获得焦距: 

\(f_{x}=\frac{\mathrm{d}x}{\mathrm{d}X}\mathrm{d}Z,\quad f_{y}=\frac{\mathrm{d}y}{\mathrm{d}Y}\mathrm{d}Z\)

4.3 以平面和标记物进行姿态估计 

        在第三章中,我们学习了如何从平面间估计单应性矩阵。如果图像中包含平面状的标记物体,并且已经对照相机进行了标定,那么我们可以计算出照相机的姿态(旋转和平移)。这里的标记物体可以为对任何平坦的物体。

        在本次实验中,标记物采用的是书本。我们使用下面的代码来提取两幅图像的SIFT特征,然后使用RANSAC算法稳健地估计单应性矩阵:

from pylab import *
from PIL import Image
from numpy import *
# If you have PCV installed, these imports should work
from PCV.geometry import homography, camera
import sift

def my_calibration(sz):
    row, col = sz
    fx = 2555 * col / 2592
    fy = 2586 * row / 1936
    K = diag([fx, fy, 1])
    K[0, 2] = 0.5 * col
    K[1, 2] = 0.5 * row
    return K

def cube_points(c, wid):
    """ Creates a list of points for plotting
        a cube with plot. (the first 5 points are
        the bottom square, some sides repeated). """
    p = []
    # bottom
    p.append([c[0] - wid, c[1] - wid, c[2] - wid])
    p.append([c[0] - wid, c[1] + wid, c[2] - wid])
    p.append([c[0] + wid, c[1] + wid, c[2] - wid])
    p.append([c[0] + wid, c[1] - wid, c[2] - wid])
    p.append([c[0] - wid, c[1] - wid, c[2] - wid])  # same as first to close plot

    # top
    p.append([c[0] - wid, c[1] - wid, c[2] + wid])
    p.append([c[0] - wid, c[1] + wid, c[2] + wid])
    p.append([c[0] + wid, c[1] + wid, c[2] + wid])
    p.append([c[0] + wid, c[1] - wid, c[2] + wid])
    p.append([c[0] - wid, c[1] - wid, c[2] + wid])  # same as first to close plot

    # vertical sides
    p.append([c[0] - wid, c[1] - wid, c[2] + wid])
    p.append([c[0] - wid, c[1] + wid, c[2] + wid])
    p.append([c[0] - wid, c[1] + wid, c[2] - wid])
    p.append([c[0] + wid, c[1] + wid, c[2] - wid])
    p.append([c[0] + wid, c[1] + wid, c[2] + wid])
    p.append([c[0] + wid, c[1] - wid, c[2] + wid])
    p.append([c[0] + wid, c[1] - wid, c[2] - wid])

    return array(p).T



#  计算特征
sift.process_image(r'pic\book_frontal.jpg', 'ch04\\4.3\im0.sift')
l0, d0 = sift.read_features_from_file('ch04\\4.3\im0.sift')

sift.process_image(r'pic\book_perspective.jpg', 'ch04\\4.3\im1.sift')
l1, d1 = sift.read_features_from_file('ch04\\4.3\im1.sift')


#  匹配特征并计算单应性矩阵
matches = sift.match_twosided(d0, d1)
ndx = matches.nonzero()[0]
fp = homography.make_homog(l0[ndx, :2].T)
ndx2 = [int(matches[i]) for i in ndx]
tp = homography.make_homog(l1[ndx2, :2].T)

model = homography.RansacModel()
H, inliers = homography.H_from_ransac(fp, tp, model)

# 计算照相机标定矩阵
K = my_calibration((400, 300))

# 位于边长为0.2 z=0平面的三维点
box = cube_points([0, 0, 0.1], 0.1)

# 投影第一幅图像上底部的正方形
cam1 = camera.Camera(hstack((K, dot(K, array([[0], [0], [-1]])))))
# 底部正方形上的点
box_cam1 = cam1.project(homography.make_homog(box[:, :5]))


# 使用H将点变换到第二幅图像中
box_trans = homography.normalize(dot(H,box_cam1))

# 从cam1和H中计算第二个照相机矩阵
cam2 = camera.Camera(dot(H, cam1.P))
A = dot(linalg.inv(K), cam2.P[:, :3])
A = array([A[:, 0], A[:, 1], cross(A[:, 0], A[:, 1])]).T
cam2.P[:, :3] = dot(K, A)

# 使用第二个照相机矩阵投影
box_cam2 = cam2.project(homography.make_homog(box))



# plotting
im0 = array(Image.open(r'pic\book_frontal.jpg'))
im1 = array(Image.open(r'pic\book_perspective.jpg'))

figure()
imshow(im0)
plot(box_cam1[0, :], box_cam1[1, :], linewidth=3)
title('2D projection of bottom square')
axis('off')

figure()
imshow(im1)
plot(box_trans[0, :], box_trans[1, :], linewidth=3)
title('2D projection transfered with H')
axis('off')

figure()
imshow(im1)
plot(box_cam2[0, :], box_cam2[1, :], linewidth=3)
title('3D points projected in second image')
axis('off')

show()

4.4 增强现实

        增强现实(Augmented Reality,AR)是将物体和相应信息放置在图像数据上的一 系列操作的总称。最经典的例子是放置一个三维计算机图形学模型,使其看起来属于该场景;如果在视频中,该模型会随着照相机的运动很自然地移动。如上一节所示,给定一幅带有标记平面的图像,我们能够计算出照相机的位置和姿态,使用这些信息来放置计算机图形学模型,能够正确表示它们。其中,我们会用到两个工具包:PyGame 和 PyOpenGL。 

4.4.1 PyGame和PyOpenGL 

安装PyGame

pip install PyGame

安装PyOpenGL

pip install PyOpenGL-3.1.6-cp39-cp39-win_amd64.whl

pip install PyOpenGL_accelerate-3.1.6-cp39-cp39-win_amd64.whl

         为了使用PyGame 和PyOpenGL 工具包来完成该应用,需要在脚本的开始部分载入下面的命令:

from OpenGL.GL import *
from OpenGL.GLU import *
import pygame, pygame.image
from pygame.locals import *

 测试:

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *


def drawFunc():
    # 清除之前画面
    glClear(GL_COLOR_BUFFER_BIT)
    glRotatef(0.1, 5, 5, 0)  # (角度,x,y,z)
    glutSolidTeapot(0.5)  # 实心茶壶
    # 刷新显示
    glFlush()


# 使用glut初始化OpenGL
glutInit()
# 显示模式:GLUT_SINGLE无缓冲直接显示|GLUT_RGBA采用RGB(A非alpha)
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
# 窗口位置及大小-生成
glutInitWindowPosition(0, 0)
glutInitWindowSize(400, 400)
glutCreateWindow(b"first")
# 调用函数绘制图像
glutDisplayFunc(drawFunc)
glutIdleFunc(drawFunc)
# 主循环
glutMainLoop()

4.4.2 从照相机矩阵到OpenGL格式 

         OpenGL 使用4×4 的矩阵来表示变换(包括三维变换和投影)。这和我们使用 的 3×4 照相机矩阵略有差别。但是,照相机与场景的变换分成了两个矩阵,GL_PROJECTION 矩阵和GL_MODELVIEW 矩阵GL_PROJECTION 矩阵处理图像成像的性质,等价于我们的内标定矩阵 K。GL_MODELVIEW 矩阵处理物体和照 相机之间的三维变换关系,对应于我们照相机矩阵中的R 和 t 部分。一个不同之处是,假设照相机为坐标系的中心,GL_MODELVIEW 矩阵实际上包含了将物体放置 在照相机前面的变换。

        假设我们已经获得了标定好的照相机,即已知标定矩阵 K,下面的函数可以将照相机参数转换为 OpenGL 中的投影矩阵:

def set_projection_from_camera(K):
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    fx = K[0, 0]
    fy = K[1, 1]
    fovy = 2 * math.atan(0.5 * height / fy) * 180 / math.pi
    aspect = (width * fy) / (height * fx)
    near = 0.1
    far = 100.0
    gluPerspective(fovy, aspect, near, far)
    glViewport(0, 0, width, height)

        第 一个 函 数 glMatrixMode() 将工作矩阵设置为 GL_PROJECTION,接下来的命令会修改这个矩 阵 1。 然后,glLoadIdentity() 函数将该矩阵设置为单位矩阵,这是重置矩阵的一般 操作。然后,我们根据图像的高度、照相机的焦距以及纵横比,计算出视图中的垂 直场。OpenGL 的投影同样具有近距离和远距离的裁剪平面来限制场景拍摄的深度 范围。我们设置近深度为一个小的数值,使得照相机能够包含最近的物体,而远深 度设置为一个大的数值。我们使用 GLU 的实用函数 gluPerspective() 来设置投影矩 阵,将整个图像定义为视图部分(也就是显示的部分)。和下面的模拟视图函数相 似,你可以使用 glLoadMatrixf() 函数的一个选项来定义一个完全的投影矩阵。当简单版本的标定矩阵不够好时,可以使用完全投影矩阵。

        模拟视图矩阵能够表示相对的旋转和平移,该变换将该物体放置在照相机前(效果是照相机在原点上)。模拟视图矩阵是个典型的 4×4 矩阵,如下所示:

\(\begin{bmatrix}R&t\\0&1\end{bmatrix}\)

使用函数实现如何获得移除标定矩阵后的3×4针孔照相机矩阵,并创建一个模拟视图: 

def set_modelview_from_camera(Rt):
    """从照相机姿态中获得模拟视图矩阵"""
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    Rx = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]])  #围绕x轴将茶壶旋转90度,使z轴向上
    R = Rt[:, :3]  #获得旋转的最佳逼近
    U, S, V = np.linalg.svd(R)
    R = np.dot(U, V)
    R[0, :] = -R[0, :]  #改变x轴的符号
    t = Rt[:, 3]  #获得平移量
    M = np.eye(4) #获得4*4的模拟视图矩阵
    M[:3, :3] = np.dot(R, Rx)
    M[:3, 3] = t
    M = M.T  #转置并压平以获得列序数值
    m = M.flatten()
    glLoadMatrixf(m)  #将模拟视图矩阵替换为新的矩阵

 4.4.3 在图像中放置虚拟物体

        在OpenGL中,该操作可以通过创建一个四边形的方式来完成,该四边形为整个视图。完成该操作最简单的方式是绘制出四边形,同时将投影和模拟视图矩阵重置,使得每一维的坐标范围在-1到1之间。 

使用函数载入一幅图像,然后将其转换成一个OpenGL纹理,并将该纹理放置在四边形上:

def draw_background(imname):
    """使用四边形绘制背景图像"""
    
    #载入背景图像,转为OpenGL纹理
    bg_image = pygame.image.load(imname).convert()
    bg_data = pygame.image.tostring(bg_image, "RGBX", 1)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    #绑定纹理
    glEnable(GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D, glGenTextures(1))
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bg_data)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    #创建四方形填充整个窗口
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 0.0);
    glVertex3f(-1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 0.0);
    glVertex3f(1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 1.0);
    glVertex3f(1.0, 1.0, -1.0)
    glTexCoord2f(0.0, 1.0);
    glVertex3f(-1.0, 1.0, -1.0)
    glEnd()
    #清除纹理
    glDeleteTextures(1)

4.4.4 综合集成

import math
import pickle
import sys
from pylab import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import pygame, pygame.image
from pygame.locals import *
from PCV.geometry import homography, camera
import sift


def cube_points(c, wid):  # 绘制立方体的一各点列表
    """ Creates a list of points for plotting
        a cube with plot. (the first 5 points are
        the bottom square, some sides repeated). """
    p = []
    # 底部
    p.append([c[0] - wid, c[1] - wid, c[2] - wid])
    p.append([c[0] - wid, c[1] + wid, c[2] - wid])
    p.append([c[0] + wid, c[1] + wid, c[2] - wid])
    p.append([c[0] + wid, c[1] - wid, c[2] - wid])
    p.append([c[0] - wid, c[1] - wid, c[2] - wid])  # 和第一个相同

    # 顶部
    p.append([c[0] - wid, c[1] - wid, c[2] + wid])
    p.append([c[0] - wid, c[1] + wid, c[2] + wid])
    p.append([c[0] + wid, c[1] + wid, c[2] + wid])
    p.append([c[0] + wid, c[1] - wid, c[2] + wid])
    p.append([c[0] - wid, c[1] - wid, c[2] + wid])  # 和第一个相同

    # 竖直边
    p.append([c[0] - wid, c[1] - wid, c[2] + wid])
    p.append([c[0] - wid, c[1] + wid, c[2] + wid])
    p.append([c[0] - wid, c[1] + wid, c[2] - wid])
    p.append([c[0] + wid, c[1] + wid, c[2] - wid])
    p.append([c[0] + wid, c[1] + wid, c[2] + wid])
    p.append([c[0] + wid, c[1] - wid, c[2] + wid])
    p.append([c[0] + wid, c[1] - wid, c[2] - wid])

    return array(p).T


def my_calibration(sz):
    row, col = sz
    fx = 2555 * col / 2592
    fy = 2586 * row / 1936
    K = diag([fx, fy, 1])
    K[0, 2] = 0.5 * col
    K[1, 2] = 0.5 * row
    return K


def set_projection_from_camera(K):  # 获取视图
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    fx = K[0, 0]
    fy = K[1, 1]
    fovy = 2 * math.atan(0.5 * height / fy) * 180 / math.pi
    aspect = (width * fy) / (height * fx)
    # 定义近和远的剪裁平面
    near = 0.1
    far = 100.0
    # 设定透视
    gluPerspective(fovy, aspect, near, far)
    glViewport(0, 0, width, height)


def set_modelview_from_camera(Rt):  # 获取矩阵
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    # 围绕x轴将茶壶旋转90度,使z轴向上
    Rx = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]])
    # 获得旋转的最佳逼近
    R = Rt[:, :3]
    U, S, V = np.linalg.svd(R)
    R = np.dot(U, V)
    R[0, :] = -R[0, :]  # 改变x轴的符号
    # 获得平移量
    t = Rt[:, 3]
    # 获得4*4的的模拟视图矩阵
    M = np.eye(4)
    M[:3, :3] = np.dot(R, Rx)
    M[:3, 3] = t
    # 转置并压平以获取列序数值
    M = M.T
    m = M.flatten()
    # 将模拟视图矩阵替换成新的矩阵
    glLoadMatrixf(m)


def draw_background(imname):
    # 载入背景图像
    bg_image = pygame.image.load(imname).convert()
    bg_data = pygame.image.tostring(bg_image, "RGBX", 1)  # 将图像转为字符串描述
    glMatrixMode(GL_MODELVIEW)  # 将当前矩阵指定为投影矩阵
    glLoadIdentity()  # 把矩阵设为单位矩阵

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # 清楚颜色、深度缓冲
    glEnable(GL_TEXTURE_2D)  # 纹理映射
    glBindTexture(GL_TEXTURE_2D, glGenTextures(1))
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bg_data)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    # 绑定纹理
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 0.0);
    glVertex3f(-1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 0.0);
    glVertex3f(1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 1.0);
    glVertex3f(1.0, 1.0, -1.0)
    glTexCoord2f(0.0, 1.0);
    glVertex3f(-1.0, 1.0, -1.0)
    glEnd()
    glDeleteTextures(1)  # 清除纹理


def draw_teapot(size):  # 红色茶壶
    glEnable(GL_LIGHTING)
    glEnable(GL_LIGHT0)
    glEnable(GL_DEPTH_TEST)
    glClear(GL_DEPTH_BUFFER_BIT)
    # 绘制红色茶壶
    glMaterialfv(GL_FRONT, GL_AMBIENT, [0, 0, 0, 0])
    glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.5, 0.0, 0.0, 0.0])
    glMaterialfv(GL_FRONT, GL_SPECULAR, [0.7, 0.6, 0.6, 0.0])
    glMaterialf(GL_FRONT, GL_SHININESS, 0.25 * 128.0)
    glutSolidTeapot(size)


def drawFunc(size):  # 白色茶壶
    glRotatef(0.5, 5, 5, 0)  # (角度,x,y,z)
    glutWireTeapot(size)
    # 刷新显示
    glFlush()


width, height = 1000, 747


def setup():  # 设置窗口和pygame环境
    pygame.init()
    pygame.display.set_mode((width, height), OPENGL | DOUBLEBUF)
    pygame.display.set_caption("OpenGL AR demo")


# 计算特征
sift.process_image('pic\\book_frontal.jpg', 'ch04\\4.4\im0.sift')
l0, d0 = sift.read_features_from_file('ch04\\4.4\im0.sift')

sift.process_image('pic\\book_perspective.jpg','ch04\\4.4\im1.sift')
l1, d1 = sift.read_features_from_file('ch04\\4.4\im1.sift')

# 匹配特征,计算单应性矩阵
matches = sift.match_twosided(d0, d1)
ndx = matches.nonzero()[0]
fp = homography.make_homog(l0[ndx, :2].T)
ndx2 = [int(matches[i]) for i in ndx]
tp = homography.make_homog(l1[ndx2, :2].T)

model = homography.RansacModel()
H, inliers = homography.H_from_ransac(fp, tp, model)

# 计算照相机标定矩阵
K = my_calibration((747, 1000))
# 位于边长为0.2,z=0平面上的三维点
box = cube_points([0, 0, 0.1], 0.1)

# 投影第一幅图下个上底部的正方形
cam1 = camera.Camera(hstack((K, dot(K, array([[0], [0], [-1]])))))
# 底部正方形上的点
box_cam1 = cam1.project(homography.make_homog(box[:, :5]))

# 使用H将点变换到第二幅图像中
box_trans = homography.normalize(dot(H, box_cam1))

# 从cam1和H中计算第二个照相机矩阵
cam2 = camera.Camera(dot(H, cam1.P))
A = dot(linalg.inv(K), cam2.P[:, :3])
A = array([A[:, 0], A[:, 1], cross(A[:, 0], A[:, 1])]).T
cam2.P[:, :3] = dot(K, A)
# 使用第二个照相机矩阵投影
box_cam2 = cam2.project(homography.make_homog(box))

Rt = dot(linalg.inv(K), cam2.P)
setup()
draw_background('pic\\book_perspective.bmp')
set_projection_from_camera(K)
set_modelview_from_camera(Rt)

#draw_teapot(0.05)  # 显示红色茶壶
drawFunc(0.05)  # 显示白色空心茶壶
pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()


  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值