先了解如何利用python语言实现以平面和标记物进行姿态估计
本实验只是先实现一个简单的小例子。简单来说就是先识别出图像中的参考面,再拍摄一张目标图像,将参考面顶部的3D模型投影到目标图像上。
大致步骤如下:
识别参考平面
在这一步中,我们所需要做的事就是提取参考图像和目标图像的sift特征,然后使用RANSAC算法稳健地估计单应性矩阵。
代码如下:
- #计算特征
- sift.process_image('D:输入图片/book_frontal.JPG','im0.sift')
- l0,d0 = sift.read_features_from_file('im0.sift')
- sift.process_image('D:输入图片/book_per.JPG','im1.sift')
- l1,d1 = sift.read_features_from_file('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)
由上面代码可得到单应性矩阵,它能够将一幅图像中标记物的点映射到另一幅图像中的对应点。还需要建立X-Y(Z=0)三维坐标系,标记物在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[2]-wid])
- p.append([c[0]-wid,c[2]+wid])
- p.append([c[0]-wid,c[2]+wid])
- p.append([c[0]+wid,c[2]+wid])
- p.append([c[0]-wid,c[2]-wid]
- return array(p).T
先计算出照相机的标定矩阵,就可以得出两个视图间的相对变换
代码如下:
- #计算照相机标定矩阵,使用图像的分辨率为747*1000
- K = my_calibration((747,1000))
- #位于边长为0.2,Z=0平面上的三维点
- Box = cube_points([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[:,1])]).T
- cam2.P[:,:3] = dot(K,A)
- #使用第二个照相机矩阵投影
- Box_cam2 = cam2.project(homography.make_homog(Box))
在图像(像素空间)中投影我们的3D模型并绘制它。
- #底部正方形的二维投影
- figure()
- imshow(im0)
- plot(Box_cam1[0,:],Box_cam1[1,linewidth=3)
- title('2D projection of bottom square')
- axis('off')
- #使用H对二维投影进行变换
- 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')
实验结果如下
什么是AR
AR全称Augmented Reality,意为增强现实技术。
它是一种将真实世界信息和虚拟世界信息“无缝”集成的新技术,是把原本在现实世界的一定时间空间范围内很难体验到的实体信息(视觉信息,声音,味道,触觉等),通过电脑等科学技术,模拟仿真后再叠加,将虚拟的信息应用到真实世界,被人类感官所感知,从而达到超越现实的感官体验。真实的环境和虚拟的物体实时地叠加到了同一个画面或空间同时存在。
增强现实技术,不仅展现了真实世界的信息,而且将虚拟的信息同时显示出来,两种信息相互补充、叠加。在视觉化的增强现实中,用户利用头盔显示器,把真实世界与电脑图形多重合成在一起,便可以看到真实的世界围绕着它。
增强现实技术包含了多媒体、三维建模、实时视频显示及控制、多传感器融合、实时跟踪及注册、场景融合等新技术与新手段。增强现实提供了在一般情况下,不同于人类可以感知的信息。
如何利用python实现AR
步骤和实现姿态估计无太大差别。由上述内容计算出照相机的位置和姿态,使用这些信息来放置计算机图像学模型。这里我们放置了一个红色的小茶壶。
在运行代码之前,我们需要先安装PyGame和PyOpenGL,下载链接(https://www.lfd.uci.edu/~gohlke/pythonlibs/)。
核心代码如下:
- 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,width,height)
- def set_modelview_from_camera(Rt):
- #从照相机姿态中获取模拟视图矩阵
- glMatrixMode(GL_MODELVIEW)
- glLoadIdentity()
- Rx = np.array([[1,[0,-1],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)
- 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,GL_RGBA,height,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); glVertex3f( 1.0,1.0,-1.0)
- glTexCoord2f(0.0,1.0); glVertex3f(-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])
- glMaterialfv(GL_FRONT,GL_DIFFUSE,[0.5,0.0,0.0])
- glMaterialfv(GL_FRONT,GL_SPECULAR,[0.7,0.6,0.0])
- glMaterialf(GL_FRONT,GL_SHININESS,0.25*128.0)
- glutSolidTeapot(size)
- width,height = 1000,747
- def setup():
- pygame.init()
- pygame.display.set_mode((width,height),OPENGL | DOUBLEBUF)
- pygame.display.set_caption("OpenGL AR demo")
实验结果如图:
错误如下:An error ocurred while starting the kernelfreeglut ERROR: Function called without first calling ‘glutInit'.
原因:经大神指点得知这个错误是freeglut和glut共存的缘故,它们俩定义了相同的方法,这个是动态链接库的重叠问题,我的在ana\Lib\site-packages\OpenGL\DLLS文件夹里面。