在上一篇博客中,我们提到了sift特征匹配中需要计算出两个图片平面间的单应性矩阵,该单应性矩阵将一副图像中标记物上的点映射到另外一副图像中的对应点。如果图像中包含平面状的标记物体,并且对照相机进行了标定,那么我们可以计算出照相机的姿态,包括旋转和平移。我们以书本为例:
我们使用以下代码来提出两幅图像的SIFT特征,然后用RANSAC算法稳健地估计单应性矩阵:
from PCV.geometry import homography, camera
from PCV.localdescriptors import sift
# compute features
sift.process_image('book_frontal.JPG', 'im0.sift')
l0, d0 = sift.read_features_from_file('im0.sift')
sift.process_image('book_perspective.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)
单应性矩阵就是将一幅图像中的标记物上的点映射到另外一幅图像的对应点,在在本次实验中,我们的标记物采取的是书本。
新视图
将图像的特征和对齐后的标记匹配,计算出单应性矩阵,准备用来计算照相机的姿态,根据单应性变化矩阵,求解出两幅图像的照相机矩阵。
K = my_calibration((747, 1000))
cam1 = camera.Camera(hstack((K, dot(K, array([[0], [0], [-1]])))))
box = cube_points([0, 0, 0.1], 0.1)
box_cam1 = cam1.project(homography.make_homog(box[:, :5]))
box_trans = homography.normalize(dot(H,box_cam1))
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)
例如我们在标记物上加一个蓝色的正方形,第一个产生的标定矩阵就是该图像分辨率大小下的标定矩阵P1:
经过前面计算的单应性矩阵变换后,第二个照相机矩阵为P2=P1*K。可得到下图,:
作为合理性验证,我们可以使用新矩阵标记投影平面的一个点,会发现投用后的点和使用第一个照相机模型P1和单应性矩阵变换后的点相同。
增强现实是将物体和相应信息放置在图像数据上的一系列操作的总称,接下来我们将放置一个三维计算机图形学模型,以水壶为例子。其中我们会用到两个工具包,分别是PyGame和PyOpenGL。
PyGame是非常流行的游戏开发工具包,可以非常简单地处理显示窗口、输入设备、事件,已经其他内容。PyOpenGL是OpenGL图形编程的Python绑定接口,具有良好的图形性能。安装包的过程可以采用pip命令进行安装,但是为了提升速度和避免未查找成功的情况,一般直接去第三方库找,下载后安装即可,速度比较快一点。
以下安装步骤同样适用于其他包的安装:
①下载安装文件
进入Python第三方库,直接ctrl+F搜索想要的包名
下载相应的版本,Python3.6就下载cp36版的,Python2.7就下载cp27版的。
②放进文件夹
下载后将文件放在文件夹D:\Python\Python27\Lib\site-packages下,然后复制文件名,准备用pip进行安装。
③安装
在文件下按住shift+右键进入PowerShell窗口,用pip命令进行安装,速度杠杠的。
到这里就可以安装成功了,PyOpenGL安装步骤同理。
接下来,可以通过以下的代码来实现增强现实。
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
Rt=dot(linalg.inv(K),cam2.P)
setup()
draw_background("book_perspective.bmp")
set_projection_from_camera(K)
set_modelview_from_camera(Rt)
draw_teapot(0.05)
pygame.display.flip()
while True:
for event in pygame.event.get():
if event.type==pygame.QUIT:
sys.exit()
setup()设置了窗口和PyGame环境
pygame.init()
pygame.display.set_mode((width,height),OPENGL | DOUBLEBUF)
pygame.display.set_caption("OpenGL AR demo")
draw_background(“book_perspective.bmp”) 是将图像作为背景添加进来,然后将其转换成一个OpenGL纹理,并将该纹理放置在一个四边形上:
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)
OpenGL使用的4X4矩阵来表示变换。这和我们使用的3X4照相机矩阵略有差别,假设我们已经获得了标定好的照相机,即已知标定矩阵K,set_projection_from_camera(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)
而set_modelview_from_camera(Rt)函数实现移除标定矩阵后的3X4针孔照相机矩阵,并创建一个模拟视图:
def set_modelview_from_camera(Rt):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
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,:]
t = Rt[:,3]
M = np.eye(4)
M[:3,:3] = np.dot(R,Rx)
M[:3,3] = t
M = M.T
m = M.flatten()
glLoadMatrixf(m)
最后通过draw_teapot(0.05)函数可以绘制出尺寸为0.05的茶壶模型,效果如下图: