这个分析是基于ARToolKit库的simpleTest程序进行分析的,可能有些错误,望高手看后能指点下。
1:首先是头文件的问题:在windows下要添加windows.h,在linux下不添加这个;若是要用OpenGL做三维虚拟物体,要添加glut.h;要用ARToolKit库,就要添加 AR/gsub.h,AR/video.h ,AR/param.h,AR/ar.h四个头文件,分别是:
gsub.h是 基于OpenGl的图形处理函数库,完成图像的实时显示,三维虚拟场景的实时渲染等功能;
video.h是视频处理函数库;param.h是参数库,ar.h是 包括摄像机校正与参数收集、目标识别与跟踪模块。主要完成摄像机定标,标识识别与三维注册等功能。
2:接下来是定义摄像头配置文件参数,windows下面是读入自带的WDM_camera_flipV.xml;
linux 下面进行手动配置,v4l2src device=/dev/video0 use-fixed-fps=false
是指使用Video4Linux2,设置摄像头设备为/dev/video0,不使用固定帧频,还有选项没做处理;
ffmpegcolorspace转换视频的颜色空间,如YUV to RGB; capsfiltercaps=video/xrawrgb, bpp=24,width=640,height=480设置摄像头参数,24位真彩,图像宽高和一些其他的stuff; identity name=artoolkit可能是声明调用的库;fakesink是对数据流做一些事情。
3:主函数分析:
1):首先初始化OpenGL,glutInit是在glut库中的函数,任何需要OpenGL编程的程序都要在主程序中先对glut进行初始化,
2):对摄像头参数初始化,导入标识图:
ar2VideoOpen:从多个摄像头源中根据输入的视频配置字符串打开一个摄像头,可以分时调用多个摄像头,返回:如果打开摄像头,返回一个指针指向AR2VideoParamT结构,这个结构体中包含视频流的配置信息,这个参数将被传输给其他的ar2Video函数指向被打开的摄像头。如果没被打开,返回NULL;
arVideoOpen:打开设定的摄像头,只是为捕获图像做准备,并没有捕获图像,当调用arVideoCapStart时才开始捕获图像;输入:选择的摄像头信息配置字符串。返回:成功0,不成功-1;
arVideoInqSize:得到视频图像的大小,该函数返回捕获视频帧的图像大小;输入:x,y指向捕获图像长宽的指针;返回:尺寸成功发现为0,否则-1;可debug打印出帧图像的大小。
arParamLoad(const char * filename,int num,ARParam *param,…):从一个文件中导入摄像头固有参数,这个文件是摄像机标定的输出。输入:摄像机固有参数放置的文件名,文件中参数个数,参数导入的结果。返回:0成功,-1错误(文件没找到,文件结构有问题);
ARParam类:包含摄像机固有参数的主要参数,图像长宽,镜头矩阵,径向失真。上面导入参数只有图像长宽?
arParamChangeSize:把导入的摄像头标定参数的图像长宽修改后存到另一个地址中;
arInitCparam:初始化摄像头参数,把相机参数指向一个静态存储单元的结构体。输入ARParam类结构体,返回常0.
arParamDisp:显示相机参数,就是把参数打印出来。
导入标识图:arLoadPatt:从文件中导入标识图描述,是一个bitmap图案;输入文件名,返回:成功导入返回标识号,不成功-1;
初始化gsub库:argInit:必须在使用任何argl函数之前初始化gsub库;输入:摄像机的固定参数,定义一个最后结果的变焦参数,全屏模式(1使能,0不使能);0;0;使能立体显示模式(只有在交互配置)。
整个基本参数初始化的框架:打开相机设备,获取图像大小,导入相机固定参数,修改图像大小后放进新的结构体指针中,打印相机参数;导入标识图矩阵,得到标识号;初始化gsub库,打开图像窗口。
3):arVideoCapStart():打开相机线程,与arVideoCapStop对应,合理运用可减少CPU负载。
4):主循环:
argMainLoop(鼠标,键盘,主循环):分别代表鼠标功能,键盘,主循环,逐个进行检测。
键盘响应:点击ESC退出循环,清除使用过的资源。
循环体:
arVideoGetImage():得到相机视频图像,捕获图像,输入为空。返回一个图像缓存,从左上开始,逐行保存在缓存器中,像素组合通过之前传入的相机参数决定。没有传入参数就使用默认配置,在config.h中。保存像素数据的存储器是不能free的,保持有效直到调用arVideoCapNext,或者再次调用arVideoGetImage返回一个非空指针,或者调用arVideoStop或者arVideoClose。返回相机捕获数据的存储地址指针。如果在这次调用时没有新的有效数据,返回NULL。
若这个函数返回NULL,则这个线程sleep几个ms后退出。进行下一次循环体。
在刚开始的时候即帧数为0的时候,复位定时器arUtilTimerReset,以便使用arUtilTimer得到从复位开始消耗的时间。然后每次调用循环体成功获取图像,帧数加一。
argDrawMode2D:渲染前更新当前相机内外参数,以便在可视平面渲染2D或者3D目标。这个函数定义了在图像平面上正投影,没有定义在渲染空间的OpenGL状态。主要是说明渲染环境为2D渲染模式。
argDispImage:在后台缓存中显示相机图像作为背景,这个必须在渲染3D目标前调用。
注意:根据你的argDrawMode,argTexmapMode和内部图像的格式,调用OpenGL函数是不同的。
使用AR_DRAW_BY_GL_DRAW_PIXELS(画像素),相机参数矩阵没有影响,但glRasterPos3f图像平移是有影响的。
使用AR_DRAW_BY_TEXTURE_MAPPING(纹理映射),会受当前相机参数矩阵影响。需要在调用这个函数之前调用arDrawMode2D。
输入参数:要显示的图像,0,0;
arDetectMarker:在视频帧中检测方形标记,运行二值化阈值分割,加标签,边缘提取,线角点估计,存储为历史标记,是一个常规检测程序,使用arGetTransMat。
输入:将要搜索标识的带色彩的图像指针即视频帧图像,因为要进行二值化阈值,所以ABGR的顺序不重要,但是透明度A的顺序很重要;阈值用于二值化分割;包含所有检测到的方形区域的信息结构体的数组,就是一个数组,元素是结构体,结构体包含一个检测到的方形的信息,ARMarkerInfo结构体;检测到方形的数目。
返回:函数正常完成返回0,否则-1;-1就清除所有使用的内存,退出循环。
ARMarkerInfo:标识检测的主要结构体,在轮廓检测后储存信息。
参数:area:标记区域的像素数;id:识别的标记编号;dir:表示标识的旋转方向(可能的值0,1,2,3),这个参数可以表明检测到的标识的线序(哪一条线是第一个),以便找到第一个角顶点,这对计算转换矩阵很重要,arGetTransMat();cf:可能是标识的概率值;pos:标识的中心;line:标识的四条边的线的方程式,线用三个值标识,a,b,c,ax+by+c=0;vertex:标识的角顶点集合。
接着将获取下一帧图像,arVideoCapNext:这个函数应该每一帧调用一次,它可以允许视频驱动完成常规任务,给视频捕获器信号表示最近的视频帧已经处理完毕,视频驱动可以重新使用被原来帧占用的内存。最好的调用地方是在arglDispImage和argDispImage之后。在一些操作系统中相当于一个no-op,空指令。
返回:成功为0,-1是视频驱动遇到错误。
对图像中检测到的方形区域组成的数组进行比对处理:
若在当前帧中没有找到方形,则进行屏幕缓冲,等待下一帧检测;
若检测到几个方形,则对每一个方形区域先进行ID号比对,若好几个方形的ID号一致,则选择概率比较大的那个方形作为标识。
计算标识图案与摄像头之间的坐标关系,关键点:arGetTransMat:计算相机位置和检测到的标识之间的转换矩阵,例如计算相机相对于已跟踪标识的位置和方位。输入:marker_info:由arDetectMarker产生的结构体,包含有检测到的标识相对于相机的位置和方位的参数;center:标识的物理中心,这个函数假定标识在2维平面,z轴向下,所以角顶点位置能被呈现在2D平面上,标识的角顶点位置顺时针指明;
width:标识的大小,因为是正方形标识,宽度就代表了大小,mm表示;conv:从标识到摄像机的转换矩阵,代表两者真实的相对位置。
然后就根据转换矩阵画出三维目标进行渲染。
进行屏幕缓存显示。
这一个主循环主要是得到视频图像,声明2D渲染环境,在后台缓存中显示图像,检测后台显示图像的可能方形标识,比对得到真正的标识,计算标识图案与相机的转换矩阵,画出三维目标渲染,然后就用双缓冲技术在屏幕显示。
5):绘制渲染部分:
绘制3D模型:并且根据转换矩阵放置在标识上。
argDrawMode3D:转换渲染环境为3D渲染环境。更新当前的相机参数到3D空间。通常调用重新初始化模型视觉矩阵,模型矩阵是将目标转换到世界坐标系,视觉矩阵是将世界坐标系转换到显示器坐标器。
argDraw3dCamera:转换视觉渲染到3D渲染模式。更新相机参数到3D渲染空间,完成argDrawMode3D。输入:视觉渲染的长宽,小于窗口长宽。
glClearDepth:为指定深度缓冲区的清除值。1.0是最大深度。
glClear(GL_DEPTH_BUFFER_BIT):将缓存清除为预先的设置值,黑色。
glEnable(GL_DEPTH_TEST):深度测试,开启更新深度缓冲区的功能。最好在display中使用。
glDepthFunc(GL_LEQUAL):指定深度缓冲比较函数,如果输入的深度值小于或等于参考值,进行渲染。
argConvGlpara:转换ARToolKit矩阵格式到OpenGL矩阵格式。输入是ARToolKit的转换矩阵,输出是OpenGL的转换矩阵。把3x4的矩阵转换成一个16元素的数组。这16个值表示相机真实的位置和方位,使用它们可以设置虚拟的相机位置,把图形目标准确的画在相应的物理标识上。
glMatrixMode(GL_MODELVIEW):对模型视景矩阵堆栈应用随后的矩阵操作。
glLoadMatrixd(gl_para):取代当前矩阵,加载这个矩阵到投影矩阵,模型视景矩阵或者纹理矩阵栈中,双精度的,可改成单精度的,减少开销。设置虚拟的相机位置。
下面是渲染三维目标物体。
灯光部分就是设置光源位置,环境光和漫射光。白光
计算三维虚拟目标的前后面材质的镜面反射指数,镜面反射颜色,材质的环境颜色。
再次调用glMatrixMode(GL_MODELVIEW):对模型视景矩阵堆栈应用随后的矩阵操作。
Gltranslatef(0.0,0.0,25.0):当前模型的移动距离,此时是往z轴移动25
glutSolidCube(50.0):绘制一个50的立方体蓝色
关闭光照,关闭深度测试。复位默认的一些OpenGL变量。
绘制三维虚拟模型主要工作是:声明是3D渲染环境,把前面求出的转换矩阵转换成OpenGL格式的转换矩阵,并把转换矩阵放在渲染堆栈中,对生成的三维模型进行渲染,OpenGL其实是生成一个三维物体,然后这个物体会经过预先设定好的渲染处理,比如这里就有位移变换,转换矩阵变换,把三维物体显示在后台帧中,等待显示。这里的光照和材质可以设定。
这里不理解的一点是三维物体是如何叠加在源图像上的,再深究下。
这里有问题是标识文件来源和相机校正参数来源,我该如何制作这两者。
ARToolKit的代码可以在http://artoolkit.sourceforge.net/apidoc/files.html中进行理解