4.1 针孔照相机模型
针孔照相机模型(有时称为射影照相机模型)是计算机视觉中广泛使用的照相机模型。对于大多数应用来说,针孔照相机模型简单,并且具有足够的精准度。这个名字来源于一种类似暗箱机的照相机。该照相机从一个小孔采集射到暗箱内部的光线。在光线投影到图像平面之前,从唯一一个点经过,也就是照相机中心C。
由图像坐标轴和三维坐标系中的x轴和y轴对齐平行的假设,我们可以得出针孔照相机的投影性质。照相机的光学坐标轴和z轴一致,该投影几何一颗简化成相似三角形。在投影之前通过旋转和平移变换,对该坐标系加入三维点,会出现完整的投影变换。
在针孔照相机中,三维点X投影为图像点x(两个点都是用齐次坐标表示的),如下所示:
这里,3×4的矩阵P为照相机矩阵(或投影矩阵)。注意,在齐次坐标系中,三维点X的坐标由4个元素组成,X=[X,Y,Z,W]。这里的标量λ是三维点的逆深度。如果我们打算在齐次坐标中将最后一个数值归一化为1,那么就会使用到它。
4.1.1 照相机矩阵
照相机矩阵可以分解为:
其中,R是描述照相机方向的旋转矩阵,t是描述照相机中心位置的三维平移向量,内标定矩阵K描述照相机的投影性质。
标定矩阵仅和照相机自身的情况相关,通常情况下可以写成:
图像平面和照相机中心间的距离为焦距f。当像素数组在传感器上偏斜的时候,需要用到倾斜参数s。在大多数情况下,s可以设置成0.也就是说:
这里,我们使用了另外的记号fx和fy,两者关系为fx=αfy。
纵横比例参数α是在像素元素非正方形的情况下使用的。通常情况下,我们还可以默认设置α=1.经过这些假设,标定矩阵变为:
除焦距之外,标定矩阵中剩余的唯一参数为光心(有时称为主点)的坐标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 def rotation_matrix(a): """创建一个用于围绕向量a轴旋转的三维旋转矩阵""" R = eye(4) R[:3,:3] = linalg.expm([0,-a[2],a[1]],[a[2],0,-a[0]],[-a[1],a[0],0]) return R
进入网站下载牛津多视图数据集中“Model Housing”(http://www.robots.ox.ac.uk/~vgg/data-mview.html)时,发现网站好像已经关闭。网上搜索相关资源也没有找到,因此暂时先搁置该算法。
4.1.3 照相机矩阵的分解
如果给定方程(1.1节中)所示的照相机矩阵P,我们需要恢复内参数K以及照相机的位置t和姿势R。矩阵分块操作称为因子分解。这里,我们将使用一种矩阵因子分解的方法,称为RQ因子分解。
将下面的方法添加到Carmera类中:
def factor(self): """将照相机矩阵分解为 K,R,t,其中, R=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来改变符号。
在示例照相机上运行下面的代码,观察照相机矩阵分解的效果:
from PIL import Image from numpy import * from pylab import * import camera K = array([[1000,0,500],[0,1000,300],[0,0,1]]) tmp = camera.rotation_matrix([0,0,1])[:3,:3] Rt = hstack((tmp,array([[50],[40],[30]]))) cam = camera.Camera(dot(K,Rt)) print(K,Rt) print(cam.factor())
不知道为什么出现了AttributeError,试了很多方法没有解决,原因待查证。
第一个矩阵是K,第二个矩阵是Rt(加了一行列向量[50,40,30] (t)),cam.factor第三个矩阵是t,R与K点积再分解。注意到两个t矩阵的第二个元素符号不相同。
4.1.4 计算照相机中心
给定照相机投影矩阵P,我们可以计算出空间上照相机的所在位置。照相机的中心C,是一个三维点,满足约束PC=0.对于投影矩阵为P=K[R|t]的照相机,有:
照相机的中心可以由下述式子来计算:
需要注意,照相的中心和内标定矩阵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 照相机标定
标定照相机是指计算出该照相机的内参数。 标定照相机的标准方法是,拍摄多幅平面棋盘模式的图像,然后进行处理计算。
4.2.1 一个简单地标定方法
步骤如下:
- 测量你选定矩形标定物体的边长 dX 和 dY;
- 将照相机和标定物体放置在平面上,使得照相机的背面和标定物体平行,同时物
体位于照相机图像视图的中心,你可能需要调整照相机或者物体来获得良好的对
齐效果;
- 测量标定物体到照相机的距离 dZ;
- 拍摄一副图像来检验该设置是否正确,即标定物体的边要和图像的行和列对齐;
- 使用像素数来测量标定物体图像的宽度和高度 dx 和 dy。
使用下面的相似三角形关系可以获得焦距:
4.3 以平面和标记物进行姿态估计
在第三章中,我们学习了如何从平面间估计单应性矩阵。如果图像中包含平面状的标记物体,并且已经对照相机进行了标定,那么我们可以计算出照相机的姿态(旋转和平移)。这里的标记物体可以为对任何平坦的物体。
在本次实验中,标记物采用的是书本。我们使用下面的代码来提取两幅图像的SIFT特征,然后使用RANSAC算法稳健地估计单应性矩阵:
from PCV.geometry import homography, camera from PCV.localdescriptors import sift # compute features sift.process_image(r'G:\picture\book\1.jpg', 'im0.sift') l0, d0 = sift.read_features_from_file('im0.sift') sift.process_image(r'G:\picture\book\2.jpg', 'im1.sift') l1, d1 = sift.read_features_from_file('im1.sift') # match features and estimate homography 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)
单应性矩阵将书本上的点映射到另一图像中的对应点。下面我们定义相应的三维坐标系,使标记物在X-Y面上(Z=0),原点在标记物的某位置上
生成立方体:
def cube_points(c,wid): 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
有了单应性矩阵和照相机的标定矩阵,我们现在可以得出两个试图间的相对变换:
# 计算照相机标定矩阵 K = my_calibration((900,675)) # 位于边长为 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)) # 测试:将点投影在 z=0 上,应该能够得到相同的点 point = array([1,1,0,1]).T print (homography.normalize(dot(dot(H,cam1.P),point))) print (cam2.project(point)) im0 = array(Image.open(r'G:\picture\book\1.jpg')) im1 = array(Image.open(r'G:\picture\book\2.jpg')) # 底部正方形的二维投影 figure() imshow(im0) axis('off') plot(box_cam1[0,:],box_cam1[1,:],linewidth=3) # 使用 H 对二维投影进行变换 figure() imshow(im1) axis('off') plot(box_trans[0,:],box_trans[1,:],linewidth=3) # 三维立方体 figure() imshow(im1) plot(box_cam2[0,:],box_cam2[1,:],linewidth=3) axis('off') show()
这里我们使用图像的分辨率为 900x675,第一个产生的标定矩阵就是在该图像分辨率大小下的标定矩阵。下面,我们在原点附近创建立方体上的点。cube_points()函数产生的前五个点对应于立方体底部的点,在这个例子中对应于位于标记物上
Z=0 平面内的点。第一幅图像是书本的主视图,我们将其作为这个例子中的模板图像。因为场景坐标的尺度是任意的,所以我们使用下面的矩阵来创建第一个照相机:
其中,图像的坐标轴和照相机是对齐的,并且放置在标记物的正上方。将前 5 个三维点投影到图像上。有了估计出的单应性矩阵,我们可以将其变换到第二幅图像上。绘制出变换后的图像,并在同样的标记物位置绘制出这些点(如图 4-4 右上所示)。
现在,结合 P1 和 H 构建第二幅图像的照相机矩阵:
该矩阵将标记平面 Z=0 上的点变换到正确的位置。也就是说,P2 矩阵的前两列和第四列是正确的。由于我们知道前 3×3 矩阵块应该为 KR,并且 R 是旋转矩阵,所以我们可以将 P2 乘以标定矩阵的逆,然后将第三列替换为前两列的交叉乘积,以此来恢复第三列。
下面是完整代码展示:
from PCV.geometry import homography, camera from PCV.localdescriptors import sift from numpy import * from PIL import Image from pylab import * import cv2 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): 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 # compute features sift.process_image(r'G:\picture\book\1.jpg', r'G:\picture\book\im0.sift') l0, d0 = sift.read_features_from_file(r'G:\picture\book\im0.sift') sift.process_image(r'G:\picture\book\2.jpg', r'G:\picture\book\im1.sift') l1, d1 = sift.read_features_from_file(r'G:\picture\book\im1.sift') # match features and estimate homography 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((900,675)) # 位于边长为 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)) # 测试:将点投影在 z=0 上,应该能够得到相同的点 point = array([1,1,0,1]).T print (homography.normalize(dot(dot(H,cam1.P),point))) print (cam2.project(point)) im0 = array(Image.open(r'G:\picture\book\1.jpg')) im1 = array(Image.open(r'G:\picture\book\2.jpg')) # 底部正方形的二维投影 figure() imshow(im0) axis('off') plot(box_cam1[0,:],box_cam1[1,:],linewidth=3) # 使用 H 对二维投影进行变换 figure() imshow(im1) axis('off') plot(box_trans[0,:],box_trans[1,:],linewidth=3) # 三维立方体 figure() imshow(im1) plot(box_cam2[0,:],box_cam2[1,:],linewidth=3) axis('off') show()
使用Pickle保存照相机矩阵:
import pickle with open('ar_camera.pkl','wb') as f: pickle.dump(K,f) pickle.dump(dot(linalg.inv(K),cam2.P),f)
4.4 增强现实
增强现实(Augmented Reality,AR)是将物体和相应信息放置在图像数据上的一系列操作的总称。最经典的例子是放置一个三维计算机图形学模型,使其看起来属于该场景;如果在视频中,该模型会随着照相机的运动很自然地移动。如上一节所示,给定一幅带有标记平面的图像,我们能够计算出照相机的位置和姿态,使用这些信息来放置计算机图形学模型,能够正确表示它们。
4.4.1 PyGame和PyOpenGL
安装PyGame(http://www.pygame.org/download.shtml)和PyOpenGL(http://pypi.python.org/pypi/PyOpenGL)。如果安装后仍无法使用,可以参考下面的文章:
(23条消息) OpenGL.error.NullFunctionError: Attempt to call an undefined function glutInit 错误解决_odd~的博客-CSDN博客
我们使用OpenGL将一个三维模型放置在一个场景中。为了使用PyGame和PyOpenGL工具包来完成该应用,需要在脚本的开始部分载入下面的命令:
from OpenGL.GL import * from OpenGL.GLU import * import pygame,pygame.image from pygame.locals import *
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 * arctan(0.5*height / fy) * 180 / pi aspect = (width*fy) / (height * fx) # 定义近的和圆的裁剪平面 near = 0.1 far = 100.0 gluPerspective(fovy, aspect, near, far) glViewport(0, 0, width, height)
模拟视图矩阵能够表示相对的旋转和平移,该变换将该物体放置在照相机前。模拟视图矩阵是个典型的4 x 4矩阵:
实现如何获得一处标定矩阵后的3X4针孔照相机矩阵,并创建一个模拟视图:
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] #获得4X4de模拟视图矩阵 M = np.eye(4) M[:3,:3] = np.dot(R,Rx) M[:3,3] = t #转置并压平以获取列序数值 M = M.T m = M.flatten() #将模拟视图矩阵替换为新的矩阵 glLoadMatrixf(m)
4.4.3 在图像中放置虚拟物体
首先使用 PyGame 中的一些函数来载入一幅图像,将其序列化为能够在PyOpenGL 中使用的原始字符串表示。然后,重置模拟视图,清除颜色和深度缓存。接下来,绑定这个纹理,使其能够在四边形和指定插值中使用它。四边形是在每一维的坐标范围 -1 到 1之间。
# -*- coding: utf-8 -*- 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 from PCV.localdescriptors 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(r'G:\picture\book\1.jpg', r'G:\picture\book\im0.sift') l0, d0 = sift.read_features_from_file(r'G:\picture\book\im0.sift') sift.process_image(r'G:\picture\book\2.jpg', r'G:\picture\book\im1.sift') l1, d1 = sift.read_features_from_file(r'G:\picture\book\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(r'G:\picture\book\3.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()
还没有评论,来说两句吧...