ARToolKit教程:开发第一个程序
来源:第三维度
要使用 ARToolKit 开发应用有两个部分:编写应用程序,以及训练对增强现实应用中所用到的真实世界标志的图像处理例程。
使用 ARToolKit 编写应用是很简单的:新建一个 AR 应用需要一个简单的框架。我们在这个框架的基础上编写新的应用。同样地,因为应用这个简单的框架,训练模板的过程也被简化。
一个应用程序的主代码必须包含以下步骤:
第二步到第五步一直重复,直到应用程序退出。但是步骤一和步骤六只分别在应用程序的初始化时和关闭时才执行。除了这些步骤之外,一个应用程序还应该对鼠标、键盘或者其他的特殊事件响应。
主要过程:Introduction main init mainLoop draw cleanup
为了详细地示范怎么样开发一个 ARToolKit 的应用,我们将一步步地介绍一个现有的例程的源代码:simpleTest(或者在有的版本里是 simple)。可以在目录 examples/simple/里找到这个程序。
程序 simpleTest
我们要找的文件名字是 simpleTest.c (或者 simple.c )。这个程序仅仅包含了一个主函数和几个绘制图像的函数。
相应于上节介绍的六个应用步骤的函数列出在表 1 中。相应于步骤二到步骤五的函数在 mainLoop 函数(主循环)中被调用。
表格 1:相应于 ARToolKit 应用程序步骤的函数调用和代码
在这个程序中,最重要的函数是 main ,init , mainloop ,draw 和 cleanup。在本节的其他部分我们将详细地解释这些函数调用。
main
Simple 例程中 main 函数的流程如下所示:
其中的初始化例程 init 包含的代码可以初始化视频捕捉,读取标识卡信息和摄像机参数信息,以及设置图像窗口。这相对于《开发原则》中的第一步。接下来,我们通过调用视频开始函数 arVideoCaPSTart 输入实时状态。再接着,函数 argMainLoop 被调用,这个函数启动了主要的程序循环,通过键盘事件与函数 keyEvent 结合使用,通过主要的图像显示循环与 mainLoop 结合使用。函数 argMainLoop 的定义在文件 ggsub.c 中。
init
init 例程在 main 例程中被调用,它的作用是初始化视频捕捉以及读入 ARToolKit 应用的初始参数信息。
首先,视频通道被打开,确定视频图像大小:
变量 vconf 包含了初始视频的配置,在 simple.c 的顶部被定义。但它的内容在你的平台的函数里可能很不一样:参照视频配置链接。对于每一个平台,都定义了一个默认的字符串,这个字符串一般都打开你的应用程序结构中第一个可用的视频流。
然后,我们需要初始化 ARToolKit 应用程序的参数。对于 ARToolKit 应用程序来说,关键的参数是:
● 可能被用来进行模板模式匹配的模板信息,以及这些模板锁对应的虚拟物体。
● 所用的视频摄像机的相机特性参数。
这些都是从文件里读取,这些文件的名字可以在命令行里被指定,或使用硬件编码的文件的默认名称。
因此,摄像机的参数信息通过默认的摄像机参数文件名 Data/camera_para.dat 被读入:
接下来,这些参数根据现有的图像大小被转换,因为摄像机的参数根据图像的大小而改变,甚至是使用相同的摄像机。
摄像机的参数被读入它的程序设置,摄像机的参数被输出显示到屏幕上:
这样之后我们通过默认的模板文件 Data/patt.hiro 读入模板的定义信息:
其中 patt_id 是一个已经被识别的模板的鉴定信息(告诉我们是哪一个模板,相当于人类的身份证)。
最终打开了图像窗口:
函数 arginit 的第二个参数定义了一个缩放函数,适应视频图像格式时的值设为 1.0,值设为 2.0 时是双倍大小(比如说,输入 320*240 图像,输出为 VGA AR 格式)。
mainloop
ARToolKit 应用程序的大部分调用都在这个例程里完成,这个例程包含了相对于《开发原则》中所要求的步骤二到步骤五。首先通过函数 arVideoGetImage 来捕捉一个输入视频帧:
该视频图像立即被输出显示到屏幕上。这个图像可以是一幅没有被扭曲的图像,也可以是一幅根据摄像头的失真信息被扭曲修正。扭曲以修正图像可以生成更加正常的图像,但是可能会导致视频帧的速率明显降低。在下例中图像是已经被扭曲的:
接着函数 arDetectMarker 被使用以搜索整个图像来寻找含有正确的标识模板的方块:
找到的标识卡的数量被存放在变量 marker_num 里,同时 marker_info 是一个指向一列z识结构体的指针,这个结构体包含了坐标信息,识别可信度,以及每个标识对应的鉴定信息和物体。marker_info 的详细信息在 API documentation 中。
此时,视频图像已经被显示和分析了。所以我们不需要再使用它:我们可以在使用新的函数的同时使用帧捕捉器来启动一个新的帧捕捉操作。完成这些工作,你只需要调用函数 arVideoCapNext:
备注:当你调用这个函数时,使用上一个视频图像缓冲会导致坏的结果(根据你的应用程序平台而定)。确保你已经处理好了视频图像缓冲。
接下来,所有的已经探测到的标识的可信度信息被加以比较,最终确定正确的标识鉴定信息为可信度最高的标识的鉴定信息:
标识卡和摄像机之间的转移信息可以通过使用函数 arGetTransMat 来获取:
相对于标识物体 i 的真实的摄像机的位置和姿态包含在一个 3*4 的矩阵 patt_trans 中。
最后,使用绘图函数,虚拟物体可以被叠加在标识卡上:
备注:如果没有标识被找到(k==-1),应用程序会做一个简单的优化步骤,我们可以交换缓冲器而不需要调用函数 draw,然后返回:
draw
函数 draw 分为显示环境初试化,设置矩阵,显示物体几个部分。你可以使用 ARToolKit显示一个三维物体并设置最小的 OpenGL 状态来初始化一个 3d 显示:
在这之后你需要这个把转移矩阵(3*4 的矩阵)转化成 OpenGL 适用的格式(16 个值的向量),可用函数 argConvGlpara 来完成此功能。这十六个值是真实世界的摄像机的位置和姿态信息,因此利用这些信息可以设置虚拟世界摄像机的位置,因此任何的图形物体都可以被准确地放置在相应的真实标识卡上。
虚拟世界的摄像机的位置是用函数 glLoadMatrixd(gl_para)来设置的。代码的最后是三维物体的显示。在这个例子中,显示的是白色光束下是一个蓝色立方体:
在最后,你要重置某些 OpenGL 的参数为默认值:
上述所讲到的步骤出现并贯穿了主要显示函数的始终,当这个程序在运行时,鼠标事件被鼠标事件函数控制,键盘事件被键盘函数控制。
cleanup
函数 cleanup 被调用的作用的停止视频处理以及关闭视频路径并释放它使其他的应用可以使用:
这些工作可以使用函数 arVideoCaPSTop, arVideoClose 和 argCleanup 来完成。
你可以编译这个程序并运行它!
这个程序的一个限制的,它只使用模板 Hiro:使用其他多个模板是很有趣的!我们将在下一节介绍怎么样使用其他模板。
使用其他的模板
程序 simpletest 使用模板匹配法来识别标识方框中的 Hiro 字样。输入视频流中的方块被系统与之前训练过的模板相比较。这些模板在运行时被加载,包含在 bin 目录下的名为data 的目录下。这这个目录下,我们找到了上次应用程序所用到的文件,比如说,名字为patt.hiro。这个文件包含了模板的格式,仅仅是一个样本图案。
为了改变 simpletest 中识别的模板,你需要改动你的代码,创造一个新的模板文件。
你可以通过改变夹在文件夹名字来修改 simpletest.c 文件,将:
改为:
这段程序生成的新的模板文件名为 mk_patt,包含在 bin 目录下。mk_patt 的源代码在util 目录下的文件 mk_patt.c 里。
要创建一个新的模板,首先应打印模板目录下的 blznkpatt.c 文件。这只是一个黑方块,中间是空的白色方块。接着为需要的模板创建一个黑白或者彩色的、适合这个中心的方块的图像,并把它打印出来。好的模板应该是不对称,而且没有很细微的细节的模板。图 1展示了一下样本模板。将做好的新模板粘在黑方块里。
图 1 样本模板
一旦新的模板制作完毕,改变 bin 目录,运行 mk_patt 程序(仅在控制台模式下)。系统会提示你输入一个摄像机的参数文件夹名字。输入文件夹名:camera_para.dat。这是默认的摄像机的参数文件。
这段程序接着会打开一个视频窗口,如图 2 所示:
图 2 mk_patt 视频窗口
把要训练的模板放在一个平的表面上,光照条件应和运行识别应用程序时的光照条件相同。然后把视频摄像头拿起在标识的上面,向下直对着标识,转动它直到标识的周围出现一个红色和绿色的方框。这指示软件 mk_patt 已经找到了围绕在待测试的模板周围的方框。应该转动摄像头直到视频图像中的方块的左上方边角是高亮的方块的红色的边角,如图 2 中所示。一旦方块被找到且方位正确,单击鼠标左键。接着系统会提示你输入一个模板的文件名字。比如说,输入 patt.yourpatt。
一旦文件名字被输入,系统就生成了一个该模板的位图图像,位图图像被复制到以这个文件名命名的文件中。接下来这个将被用在 ARToolKit 的模板匹配中。为了使用这个新模板,这些数据要被拷贝到文件目录 bin/Data 下。重新编译 simpletest 后,现在,你就可以使用你自己的模板了!
训练了一个模板后,其他的模板也可以被训练,只需要用摄像头对着新模板并重复以上步骤,或者,单击鼠标右键可以退出应用程序。
使用多个模板
现在我们想要使用不止一个的模板,而不同的模板有各自不同的三维物体相对应。为达到此目的,我们将逐步分析目录 examples/simplem/下的 simplem 文件的源代码。你会发现两个源文件,simplemTest.c 和 object.c。这个程序可以探测多个标识卡,并且在每个标识上面显示不同形状的物体(锥体,立方体,球体)。
它和 simple 程序的主要区别是:
● 加载的文件中有多个模板的声明。
● 与模板相关联的结构不同,这意味着程序中检查代码以及转换调用不同。
● 语法重新定义,定义画图函数。
其他的代码则都是一样的!
系统建议使用一个特定的函数——object.c中的read_ObjData 来加载 ARToolKit中的多个模板。利用此函数,可以用如下方法来加载标识:
参量 object 是一个指向一个 ObjectData_T 的结构体的指针。参量 model_name 定义的不是一个模板定义文件名(在这里文件名是 model_name),而是一个特定的多个模板定义的文件名(警告:这个格式和多个模板跟踪文件名不同!!!)。文本文件 object_data 指定了哪些标识物体应被识别以及模板怎么样与各个物体相关联。文件 object_data 的开始处记录了要被指定的物体的数量,接着是每个物体的文本类型的数据结构。object_data 文件中每个标识都被以下结构体详细说明:
· 名字
· 模板识别文件名
· 跟踪模板的宽度
· 跟踪模板的中心
比如说,对应着与虚拟的立方体相关的标识的结构体如下:
#pattern 1
cone
Data/patt.hiro
80.0
0.0 0.0
请注意,以#character 开始是代码是命令行,被文件读取器忽略。
ARToolKit 可以试着在 arDetectMarker 流程中识别多个模板了。因为我们现在是探测多个模板,我们需要保持每一个虚拟物体的可见性,同时修改对于以及探测到的模板的检查步骤。更进一步,我们还需要维持每个已探测模板的特定的转移。
因此,如果标识被探测到,每一个标识都有一个视觉标志和一个新的转移矩阵。现在通过结构体 ObjectData_T 调用绘图函数来绘制虚拟物体。结构体 ObjectData_T 需要被赋予虚拟物体的参数以及虚拟物体的个数。
绘图函数同样很容易理解:遍历物体的列表,如果物体可见,利用它的姿态按照相应的形状绘制物体。
现在可以编译 simplem,确保所有必须的文件已经被放在 data 文件目录下。结果如图3 所示。
图 3 simplem 视频窗口
你可以修改文件 object_data,使用你自己的模板实验了!