【ARToolkit】第二个实例simple2

时间:2024-04-02 08:26:36

我们这次分析ARtooklit里面的simple2这个程序,我们先上源码,随后分析一些不同于simpletest的地方

#ifdef _WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifndef __APPLE__
#include <GL/gl.h>
#include <GL/glut.h>
#else
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#endif
#include <AR/gsub.h>
#include <AR/video.h>
#include <AR/param.h>
#include <AR/ar.h>

/* set up the video format globals */

#ifdef _WIN32
char            *vconf = "Data\\WDM_camera_flipV.xml";
#else
char            *vconf = "";
#endif

int             xsize, ysize;
int             thresh = 100;
int             count = 0; 

int             mode = 1;//标识位

char           *cparam_name    = "Data/camera_para.dat";
ARParam         cparam;

char           *patt_name      = "Data/patt.hiro";
int             patt_id;
int             patt_width     = 80.0;
double          patt_center[2] = {0.0, 0.0};
double          patt_trans[3][4];

static void   init(void);
static void   cleanup(void);
static void   keyEvent( unsigned char key, int x, int y);
static void   mainLoop(void);
static void   draw( double trans[3][4] );

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    init();

    arVideoCapStart();
    argMainLoop( NULL, keyEvent, mainLoop );
    return (0);
}

static void   keyEvent( unsigned char key, int x, int y)
{
    /* quit if the ESC key is pressed */
    if( key == 0x1b ) {
        printf("*** %f (frame/sec)\n", (double)count/arUtilTimer());
        cleanup();
        exit(0);
    }
    //如果键盘输入c
    if( key == 'c' ) {
        printf("*** %f (frame/sec)\n", (double)count/arUtilTimer());
        count = 0;

        mode = 1 - mode;//让每次键盘输入c都让mode变化
        
//此处mode转换为布尔型,如果mode=0,则为假arGetTransMatCont
        if( mode ) printf("Continuous mode: Using arGetTransMatCont.\n");
        
//如果mode不为假,输出arGetTransMat
         else      printf("One shot mode: Using arGetTransMat.\n");
    }
}

/* main loop */
static void mainLoop(void)
{
    static int      contF = 0;//静态变量
    ARUint8         *dataPtr;
    ARMarkerInfo    *marker_info;
    int             marker_num;
    int             j, k;

    /* grab a vide frame */
    if( (dataPtr = (ARUint8 *)arVideoGetImage()) == NULL ) {
        arUtilSleep(2);
        return;
    }
    if( count == 0 ) arUtilTimerReset();
    count++;

    argDrawMode2D();
    argDispImage( dataPtr, 0,0 );

    /* detect the markers in the video frame */
    if( arDetectMarker(dataPtr, thresh, &marker_info, &marker_num) < 0 ) {
        cleanup();
        exit(0);
    }

    arVideoCapNext();

    /* check for object visibility */
    k = -1;
    for( j = 0; j < marker_num; j++ ) {
        if( patt_id == marker_info[j].id ) {
            if( k == -1 ) k = j;
            else if( marker_info[k].cf < marker_info[j].cf ) k = j;
        }
    }
    if( k == -1 ) {
        contF = 0;
        argSwapBuffers();
        return;
    }


    /*计算摄像头的转移矩阵,标识卡和摄像机之间的转移信息通过使用函数*/
    //mode为0
    if( mode == 0 || contF == 0 ) {
        arGetTransMat(&marker_info[k], patt_center, patt_width, patt_trans);
    }
    else {
        
//mode为1
        arGetTransMatCont(&marker_info[k], patt_trans, patt_center, patt_width, patt_trans);
    }

    contF = 1;

    draw( patt_trans );

    argSwapBuffers();
}

static void init( void )
{
    ARParam  wparam;

    /* open the video path */
    if( arVideoOpen( vconf ) < 0 ) exit(0);
    /* find the size of the window */
    if( arVideoInqSize(&xsize, &ysize) < 0 ) exit(0);
    printf("Image size (x,y) = (%d,%d)\n", xsize, ysize);

    /* set the initial camera parameters */
    if( arParamLoad(cparam_name, 1, &wparam) < 0 ) {
        printf("Camera parameter load error !!\n");
        exit(0);
    }
    arParamChangeSize( &wparam, xsize, ysize, &cparam );
    arInitCparam( &cparam );
    printf("*** Camera Parameter ***\n");
    arParamDisp( &cparam );

    if( (patt_id=arLoadPatt(patt_name)) < 0 ) {
        printf("pattern load error !!\n");
        exit(0);
    }

    /* open the graphics window */
    argInit( &cparam, 1.0, 0, 0, 0, 0 );
}

/* cleanup function called when program exits */
static void cleanup(void)
{
    arVideoCapStop();
    arVideoClose();
    argCleanup();
}

static void draw( double trans[3][4] )
{
    double    gl_para[16];
    GLfloat   mat_ambient[]     = {0.0, 0.0, 1.0, 1.0};
    GLfloat   mat_flash[]       = {0.0, 0.0, 1.0, 1.0};
    GLfloat   mat_flash_shiny[] = {50.0};
    GLfloat   light_position[]  = {100.0,-200.0,200.0,0.0};
    GLfloat   ambi[]            = {0.1, 0.1, 0.1, 0.1};
    GLfloat   lightZeroColor[]  = {0.9, 0.9, 0.9, 0.1};
    
    argDrawMode3D();
    argDraw3dCamera( 0, 0 );
    glClearDepth( 1.0 );
    glClear(GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    
    /* load the camera transformation matrix */
    argConvGlpara(trans, gl_para);
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixd( gl_para );

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambi);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_flash);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_flash_shiny);    
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMatrixMode(GL_MODELVIEW);
    glTranslatef( 0.0, 0.0, 25.0 );
    glutSolidCube(50.0);
    glDisable( GL_LIGHTING );

    glDisable( GL_DEPTH_TEST );
}

【ARToolkit】第二个实例simple2【ARToolkit】第二个实例simple2

       上面的是每次输入字母C后的变化和用Hiro标识出现的结果
       这篇simple2不同于simpletest的地方我用了特殊的颜色进行标记,在开头,添加了一个标识位【ARToolkit】第二个实例simple2,在后面用到了标识位。这篇代码在simpletest的基础上在键盘响应事件里面添加了如果用键盘输入字母C的响应事件,这里的意思是如果输入C,选择连续模式,就是使用arGetTransMatCout函数,或者选择单帧模式arGetTransMat求转换矩阵此时mode为0,这里的mode由整形强制转换为布尔类型,此处为布尔类型即为假则在控

【ARToolkit】第二个实例simple2

制台输出"Continuous mode: Using arGetTransMatCont.\n",如果mode为1,则输出"One shot mode: Using arGetTransMat.\n"。后面我给大家讲解arGetTransMatCont函数和arGetTransMat函数的不同。

第二个不同在mainloop里面加入了一个 static contF = 0,在下面 计算摄像头的转移矩阵这

【ARToolkit】第二个实例simple2

里,这里是如果mode==0或者contF==0,就执行arGetTransMat()函数,如果mode不等于0,就执行arGetTransMatCont()函数,这两个函数里面实现的内容都一样,都是&marker_info[k], patt_center, patt_width, patt_trans。

marker_info[k]:由arDetectMarker产生的结构体,下面为结构体的内容:

【ARToolkit】第二个实例simple2

area:标识区域内的像素数量

id :为标识号

dir:方向,它告诉标记的旋转(可能的值是0、1、2或3).此参数告诉我们所检测标记的行序(即哪一行是第一行),从而找到第一个顶点。这对于计算arGetTransMat()中的转换矩阵非常重要

cf:置信度值(概率作为一个标记)

pons:中心(理想屏幕坐标)

line:理想屏幕坐标

vertex:标记点的顶点边缘点(在理想屏幕坐标中)

以上的内容能在ar.h中找到。

 

patt_center:标识的物理中心,【ARToolkit】第二个实例simple2这个函数假定标识在2维平面,z轴向下,所以角顶点位置能被呈现在2D平面上,标识的角顶点位置顺时针指明;

patt_width:标识的大小,因为是正方形标识,宽度就代表了大小,单位为mm;

patt_trans:相对于标识物体 i 的真实的摄像机的位置和姿态包含在一个 3*4 的矩阵 patt_trans 中。【ARToolkit】第二个实例simple2其中patt_trans[0][3],patt_trans[1][3],patt_trans[2][3],分别表示了摄像头的坐标系的X轴,Y轴,Z轴。标识往左移动,第一个值会增加(往X轴正方向移动),往上移动(对应摄像机的Y轴负方向),数值减小,往前移动(对应Z轴正方向),数值增加,这里的坐标系是摄像头的坐标系

大家可以把printf("%f %f %f\n",patt_trans[0][3],patt_trans[1][3],patt_trans[2][3])这一行代码添加在arGetTransMat()函数的后面试一下。

       说了了那么多,说完了函数的内容,接下里就要说一下这两个函数arGetTransMat()和arGetTransMatCont()都是计算相机位置和检测到的标识之间的转换矩阵(重要的是函数里面的内容&marker_info[k], patt_center, patt_width, patt_trans这些)。

       这两个函数实现的东西是一样的,不一样的地方就是arGetTransMatCont()使方块看起来很稳定,这是因为,在arGetTransMatCont()下,我们使用了历史函数,使用历史信息会降低精确度,但是提供了更为稳定的图像,但是速度稍快些,在arGetTransMat()函数下,方块像是有点微微的抖动。前一种情况就是我们使用了历史函数,在后一种 情况下没有使用。 所以这两个函数的区别就在于一个使图像更稳定,但会降低精确度,另一个则不会降低精确度,图像不是那么稳定。