基于标记的AR的OpenCV实现:动态视频输入
2016-03-21 14:23
330 查看
我的上篇博客《基于标记的AR的OpenCV实现》实现的是单幅图片的标记检测和增强现实,稍微改动了程序实现了摄像头视频流图像的动态检测和实时增强,经测试,
实时性不错,在标记不被遮挡的情况下,绘制的虚拟模型和实时与检测标记结合,但标记只要有被遮挡一少部分,标记就检测失败。
以下讲下程序修改部分。
1:show函数并没有修改,但还是讲一下程序流程,首先在main()函数中调用show()函数后,在show()函数中,首先完成一些显示参数的初始化,这部分只需执行一次即可,然后将图像数据读入pixeldata中,之后通过glutDisplayFunc(&display)调用display()函数进行显示,但请注意,display()函数只会执行一次。glutMainLoop进入GLUT事件处理循环。在一个GLUT程序中,这个例程被调用一次
。一旦被调用,这个程序将永远不会返回 。它将调用必要的任何已注册的回调。
2:为了实现display()函数的循环调用,执行glutPostRedisplay();语句会触发一次display()的执行,有两种思路:1:设置一个定时器,定时执行glutPostRedisplay();从而周期性
调用display()函数;2:在display()函数体最后执行glutPostRedisplay();从而完成自我不断触发调用。我采用了思路2的做法成功实现,思路1原理上也可实现。
主要修改display()函数。#if 1---#endif为添加部分,主要是更新图像数据,并执行markerDetector.processFrame(src,camMatrix, distCoeff,markers);从而得到更新后标记。
本博客所改代码为mastering opencv with practical computer vision projects 第二章书中代码。
我实现的代码已上传到,需要的自行下载。
实时性不错,在标记不被遮挡的情况下,绘制的虚拟模型和实时与检测标记结合,但标记只要有被遮挡一少部分,标记就检测失败。
以下讲下程序修改部分。
1:show函数并没有修改,但还是讲一下程序流程,首先在main()函数中调用show()函数后,在show()函数中,首先完成一些显示参数的初始化,这部分只需执行一次即可,然后将图像数据读入pixeldata中,之后通过glutDisplayFunc(&display)调用display()函数进行显示,但请注意,display()函数只会执行一次。glutMainLoop进入GLUT事件处理循环。在一个GLUT程序中,这个例程被调用一次
。一旦被调用,这个程序将永远不会返回 。它将调用必要的任何已注册的回调。
int show(const char* filename,int argc, char** argv,Mat_<float>& cameraMatrix, vector<Marker>& detectedMarkers) { //打开文件 FILE* pfile=fopen(filename,"rb"); if(pfile == 0) exit(0); //读取图像大小 fseek(pfile,0x0012,SEEK_SET); fread(&imagewidth,sizeof(imagewidth),1,pfile); fread(&imageheight,sizeof(imageheight),1,pfile); //计算像素数据长度 pixellength=imagewidth*3; while(pixellength%4 != 0)pixellength++; pixellength *= imageheight; //读取像素数据 pixeldata = (GLubyte*)malloc(pixellength); if(pixeldata == 0) exit(0); fseek(pfile,54,SEEK_SET); fread(pixeldata,pixellength,1,pfile); //以上是读取一个bmp图像宽高和图像数据的操作 //关闭文件 fclose(pfile); build_projection(cameraMatrix); //这是建立摄像机内参数矩阵,就是相机矩阵,display函数开始导入的模型就是相机矩阵 setMarker(detectedMarkers); //导入找到的标识 //初始化glut运行 glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA); glutInitWindowPosition(100,100); glutInitWindowSize(imagewidth,imageheight); glutCreateWindow(filename); glutDisplayFunc(&display); glutMainLoop(); //------------------------------------- free(pixeldata); return 0; }
2:为了实现display()函数的循环调用,执行glutPostRedisplay();语句会触发一次display()的执行,有两种思路:1:设置一个定时器,定时执行glutPostRedisplay();从而周期性
调用display()函数;2:在display()函数体最后执行glutPostRedisplay();从而完成自我不断触发调用。我采用了思路2的做法成功实现,思路1原理上也可实现。
主要修改display()函数。#if 1---#endif为添加部分,主要是更新图像数据,并执行markerDetector.processFrame(src,camMatrix, distCoeff,markers);从而得到更新后标记。
pixeldata的更新还是采用show()函数中读图像文件的方法,因此先将从摄像头读入图像保存为一个临时图片文件"aa.bmp",在用fread()读入的方式,这种方法确实比较low,
但比较容易实现了想要达到的效果。
void display(void) { #if 1 Mat src; camera >> src; imwrite("aa.bmp",src); //打开文件 FILE* pfile=fopen("aa.bmp","rb"); if(pfile == 0) exit(0); fseek(pfile,54,SEEK_SET); fread(pixeldata,pixellength,1,pfile); //以上是读取一个bmp图像宽高和图像数据的操作 //关闭文件 fclose(pfile); double t = (double)getTickCount(); markerDetector.processFrame(src,camMatrix, distCoeff,markers); t = ((double)getTickCount() - t)/getTickFrequency(); cout<<"t"<<t<<endl; setMarker(markers); //导入找到的标识 #endif cout<<"display"<<endl; glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //绘制图片,第一、二、三、四个参数表示图象宽度、图象高度、像素数据内容、像素数据在内存中的格式,最后一个参数表示用于绘制的像素数据在内存中的位置 glDrawPixels(imagewidth,imageheight,GL_BGR_EXT,GL_UNSIGNED_BYTE,pixeldata); /* glMatrixMode - 指定哪一个矩阵是当前矩阵 glMatrixMode设置当前矩阵模式: GL_MODELVIEW,对模型视景矩阵堆栈应用随后的矩阵操作. GL_PROJECTION,对投影矩阵应用随后的矩阵操作. GL_TEXTURE,对纹理矩阵堆栈应用随后的矩阵操作. 与glLoadIdentity()一同使用 glLoadIdentity():该函数的功能是重置当前指定的矩阵为单位矩阵。 在glLoadIdentity()之后我们为场景设置了透视图。glMatrixMode(GL_MODELVIEW)设置当前矩阵为模型视图矩阵,模型视图矩阵储存了有关物体的信息。 */ //绘制坐标 ,导入相机内参数矩阵模型 glMatrixMode(GL_PROJECTION); glLoadMatrixf(projectionMatrix.data); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnableClientState(GL_VERTEX_ARRAY); //启用客户端的某项功能 glEnableClientState(GL_NORMAL_ARRAY); glPushMatrix(); glLineWidth(3.0f); float lineX[] = {0,0,0,1,0,0}; float lineY[] = {0,0,0,0,1,0}; float lineZ[] = {0,0,0,0,0,1}; const GLfloat squareVertices[] = { -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, }; const GLubyte squareColors[] = { 255, 255, 0, 255, 0, 255, 255, 255, 0, 0, 0, 0, 255, 0, 255, 255, }; for (size_t transformationIndex=0; transformationIndex<m_detectedMarkers.size(); transformationIndex++) { const Transformation& transformation = m_detectedMarkers[transformationIndex].transformation; Matrix44 glMatrix = transformation.getMat44(); //导入相机外参数矩阵模型 glLoadMatrixf(reinterpret_cast<const GLfloat*>(&glMatrix.data[0])); //reinterpret_cast:任何类型的指针之间都可以互相转换,修改了操作数类型,仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换 glVertexPointer(2, GL_FLOAT, 0, squareVertices); //指定顶点数组的位置,2表示每个顶点由三个量构成(x, y),GL_FLOAT表示每个量都是一个GLfloat类型的值。第三个参数0。最后的squareVertices指明了数组实际的位置。这个squareVertices是由第一个参数和要画的图形有几个顶点决定大小,理解。 glEnableClientState(GL_VERTEX_ARRAY); //表示启用顶点数组 glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors); //RGBA颜色,四个顶点 glEnableClientState(GL_COLOR_ARRAY); //启用颜色数组 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableClientState(GL_COLOR_ARRAY); float scale = 0.5; glScalef(scale, scale, scale); glColor4f(1.0f, 0.0f, 0.0f, 1.0f); glVertexPointer(3, GL_FLOAT, 0, lineX); glDrawArrays(GL_LINES, 0, 2); glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glVertexPointer(3, GL_FLOAT, 0, lineY); glDrawArrays(GL_LINES, 0, 2); glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glVertexPointer(3, GL_FLOAT, 0, lineZ); glDrawArrays(GL_LINES, 0, 2); } glFlush(); glPopMatrix(); glDisableClientState(GL_VERTEX_ARRAY); glutSwapBuffers(); glutPostRedisplay(); }
本博客所改代码为mastering opencv with practical computer vision projects 第二章书中代码。
我实现的代码已上传到,需要的自行下载。
相关文章推荐
- 使用VTS分析bacnet协议的ReadProperty(一)
- linux之cat命令
- 物理standby database的日常维护
- Apache Stratos探究:CLI Tool简单使用手册
- 磁盘IO性能监控指标
- 解决SUSE Linux无法使用SSH登录的问题
- linux:如何修改用户的密码
- linux下分卷压缩,合并解压的3种方法
- linux中关于tmpfs文件系统资料的整理与分析
- Hadoop入门
- 怎么解决tomcat占用8080端口问题图文教程
- apache + tomcat + mod_jk解决session会话共享的问题
- Nginx-location常用配置
- .NET分布式事务--TransactionScop
- CentOS 安装ElasticSearch2.x
- nginx-lua get post请求小例子
- linux添加swap
- CUDA范例精解通用GPU架构-(1)Tesla服务器Kepler架构和万年的HelloWorld
- 采用dlopen、dlsym、dlclose加载动态链接库【总结】
- 使用System.arraycopy()实现数组之间的复制