본문 바로가기

IT/ARtoolkit

ARToolKit Documentation - Developing your first application, part1 : mainLoop 함수


2. mainLoop!

 

static void mainLoop(void)
{
    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 ) {
        argSwapBuffers();
        return;
    }

    /* get the transformation between the marker and the real camera */
    arGetTransMat(&marker_info[k], patt_center, patt_width, patt_trans);

    draw();

    argSwapBuffers();
}

 

 

1) 변수 선언 부분

 

    ARUint8         *dataPtr;
    ARMarkerInfo    *marker_info;
    int             marker_num;
    int             j, k;

 

① ARUint8

 

ar.h에 정보가 나와있다.

 

/* overhead ARToolkit type*/
typedef char              ARInt8;
typedef short             ARInt16;
typedef int               ARInt32;
typedef unsigned char     ARUint8;
typedef unsigned short    ARUint16;
typedef unsigned int      ARUint32;

 

정보가 좀 부실한데?;

 

 ARint를 보니 ARUint란 AR unsigned int 를 의미하는 것 같고 뒤에 있는 숫자는 그 변수의 비트수를 의미하는 것 같다. 그런데 포인터형 변수로 선언되어 있네!?; unsigned char pointer랑 같은 의미로 받아들여도 되는건가

 

 

② ARMarkerInfo

 

/** \struct ARMarkerInfo
* \brief main structure for detected marker.
*
* Store information after contour detection (in idea screen coordinate, after distorsion compensated).
* \remark lines are represented by 3 values a,b,c for ax+by+c=0
* \param area number of pixels in the labeled region
* \param id marker identitied number
* \param dir Direction that tells about the rotation about the marker (possible values are 0, 1, 2 or 3). This parameter makes it possible to tell about the line order of the detected marker (so which line is the first one) and so find the first vertex. This is important to compute the transformation matrix in arGetTransMat().
* \param cf confidence value (probability to be a marker)
* \param pos center of marker (in ideal screen coordinates)
* \param line line equations for four side of the marker (in ideal screen coordinates)
* \param vertex edge points of the marker (in ideal screen coordinates)
*/
typedef struct {
    int     area; 

    int     id;    
    int     dir;
    double  cf;
    double  pos[2];
    double  line[4][3];
    double  vertex[4][2];
} ARMarkerInfo;

 

 음...-_-;; 이건 주석도 이해하기 힘드네... 아마 영상 처리를 공부하고, 마커를 인식하는 원리를 배워야 이해할 수 있을듯!? 마커의 중심이 왜 2개나 되는지 이해가 되지 않는다...

 

 

2) 영상 출력

 

    /* grab a vide frame */
    if( (dataPtr = (ARUint8 *)arVideoGetImage()) == NULL ) {// 아래 참고
        arUtilSleep(2);                                                      //2msec동안 정지                                              
        return;                                                                 //mainLoop을 빠져나간다.
    }
    if( count == 0 ) arUtilTimerReset();     //count가 0이면 프로그램 시간을 윈도우 시간으로 설정한다.
    count++;   

    argDrawMode2D();                         //2D나 3D랜더링을 위해 카메라 매개변수를 설정해준다.
    argDispImage( dataPtr, 0,0 );           //dataPtr가 가르키는 이미지를 출력

 

 

arVideoGetImage함수는 카메라에서 받은 이미지를 저장하는 함수이다. 카메라에서 받은 이미지를 픽셀별 색상 정보를 저장하는 방식으로 저장한 후, 첫번째 주소값을 반환한다. 따라서 dataPtr로 그 정보를 받는다. 이때 이전 이미지와 현재 이미지가 차이가 없으면 NULL값을 반환하므로, NULL이 나오면 arUtilSleep함수를 통해 2msec동안 화면이 정지하고 위 mainLoop는 끝나게 된다. 만약 이미지가 변환되었다면, dataPtr에 그 정보의 주소가 입력된다. 그리고 count(전역변수이다.)가 0이라면 프로그램의 시간을 윈도우 시간과 동기화 시킨다. 그리고 카운트를 올려준 다음, argDrawMode2D함수를 통해 2D나 3D랜더링을 위해 카메라 매개변수를 설정한다. 그리고 argDispImage함수를 이용, dataPtr이 가르키는 이미지를 출력한다.


 

 

① arVideoGetImage()

 

video.h에 자세한 설명이 나와있다.

 

/**
 * \brief get the video image.
 *
 * This function returns a buffer with a captured video image.
 * The returned data consists of a tightly-packed array of
 * pixels, beginning with the first component of the leftmost
 * pixel of the topmost row, and continuing with the remaining
 * components of that pixel, followed by the remaining pixels
 * in the topmost row, followed by the leftmost pixel of the
 * second row, and so on.
 * The arrangement of components of the pixels in the buffer is
 * determined by the configuration string passed in to the driver
 * at the time the video stream was opened. If no pixel format
 * was specified in the configuration string, then an operating-
 * system dependent default, defined in <AR/config.h> is used.
 * The memory occupied by the pixel data is owned by the video
 * driver and should not be freed by your program.
 * The pixels in the buffer remain valid until the next call to
 * arVideoCapNext, or the next call to arVideoGetImage which
 * returns a non-NULL pointer, or any call to arVideoCapStop or
 * arVideoClose.
 * \return A pointer to the pixel data of the captured video frame,
 * or NULL if no new pixel data was available at the time of calling.
 */

AR_DLL_API  ARUint8*  arVideoGetImage(void);

 

하악; 뭔말이냐...

 

 이미지를 새로 잡아오고, 잡아오는데 성공한다면 각 픽셀의 값이 저장되어있는 ARUint8포인터를 반환한다. 만약 이미지가 이전 이미지와 변함이 없다면 NULL값을 반환한다.

 

 

②void   arUtilSleep( int msec );

 

/**
* \brief sleep the actual thread.
*
* Sleep the actual thread.
* \param msec time to sleep (in millisecond)
*/
void   arUtilSleep( int msec );

 

말 그대로 잠시 멈추는 함수. 초는 msec로 받는다.

 

③arUtilTimerReset

 

ar.h에 설명이 나와있다.

 

/**
* \brief reset the internal timer of ARToolkit.
*
* Reset the internal timer used by ARToolKit.
* timer measurement (with arUtilTimer()).
*/

void   arUtilTimerReset(void);

 

윈도우 시계에서 시간을 받아와서 프로그램의 시간과 동기화 시켜주는 함수인 듯 하다.

 

 

④argDrawMode2D

 

/** \fn void argDrawMode2D( void )
* \brief switch the rendering context for 2D rendering mode.
*
* Update curent camera parameters (internal and external)
* for rendering 2D or 3D objects in the view plane (like text or 2D shape).
* This function define an orthographic projection in the image
* plane. It not define opengl state for rendering in image space (like
* for a bitmap copy).
*/

void argDrawMode2D( void );

 

2D나 3D랜더링을 하기 위해 현재 카메라 파라매터로 업데이트 해주는 함수라고 한다.

 

소스는.. 흐흐.. OpenGL공부할때 봐야지

 

 

⑤ argDispImage( dataPtr, 0,0 )

 

gsub.h에 자세한 설명이 나와있다.

 

/** \fn void argDispImage( ARUint8 *image, int xwin, int ywin )
* \brief display the video image.
*
* Display in the back-buffer the video image in argument.
* For doing AR video background, this function must be called before
* any rendering of 3D object.
* \remark According to your argDrawMode, argTexmapMode and the internal
* image format the openGL function called is different and less
* or more efficient.
* \remark with AR_DRAW_BY_GL_DRAW_PIXELS, unaffected by current camera
* parameters matrix but affected by glRasterPos3f.
* \remark with AR_DRAW_BY_TEXTURE_MAPPING, affected by current current camera
* parameters matrix. You need generally call argDrawMode2D before this function.
* \param image image to display
* \param xwin XXXBK
* \param ywin XXXBK

*/
void argDispImage( ARUint8 *image, int xwin, int ywin );

ARUint8에 저장된 데이터를 받아서 Display를 출력한다...

 

 이건 어디서 튀어나온 함수인지 기원을 모르겠군 -_-; 소스를 보니 역시 OpenGL을 알아야 분석이 가능한 함수이다. ㅠㅠ

 

 

 

3) 마커 찾기

 

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

    arVideoCapNext();

 

 arDetectMarker 함수를 이용하여 dataPtr이 가르키는 이미지에서 마커를 찾는다. 모든 찾은 마커의 정보는 marker_info에 저장하고, 찾은 마커의 총 개수는 marker_num에 저장한다. 마커를 찾는 중에 Error가 나게 되면 -1이 출력되고 그렇게 되면 cleanup함수를 통해 프로그램을 정리한 후 종료하게 된다. 만약 arDetectMarker가 성공적으로 동작한다면 다음 영상을 받아오기 위해 arVideoCapNext함수를 실행한다.

 

 

① arDetectMarker(dataPtr, thresh, &marker_info, &marker_num)

 

ar.h에 자세한 정보가 나와있다.

 

 

/**
* \brief main function to detect the square markers in the video input frame.
*
* This function proceeds to thresholding, labeling, contour extraction and line corner estimation
* (and maintains an history).
* It's one of the main function of the detection routine with arGetTransMat.
* \param dataPtr a pointer to the color image which is to be searched for square markers.
*                The pixel format depend of your architecture. Generally ABGR, but the images
*                are treated as a gray scale, so the order of BGR components does not matter.
*                However the ordering of the alpha comp, A, is important.
* \param thresh  specifies the threshold value (between 0-255) to be used to convert
*                the input image into a binary image.
* \param marker_info a pointer to an array of ARMarkerInfo structures returned
*                    which contain all the information about the detected squares in the image

* \param marker_num the number of detected markers in the image.
* \return 0 when the function completes normally, -1 otherwise
*/

int arDetectMarker( ARUint8 *dataPtr, int thresh,
                    ARMarkerInfo **marker_info, int *marker_num );

 

 dataPtr이 가르키는 이미지 정보에서 마커를 찾는 함수이다. thresh값이 발견되면 마커를 탐색하기 시작한다. 찾은 모든 마커의 정보는 marker_info를 통해 반환하고, 찾은 마커의 개수는 marker_num을 통해 반환한다. 함수가 정상적으로 종료되면 0을 반환하고, 비정상적으로 종료되는 경우 -1을 반환한다.

 

 

②arVideoCapNext()

 

 

/**
 * \brief call for the next grabbed video frame.
 *
 * This function should be called at least once per frame.
 * It has several purposes, depending on the operating system.
 * It allows the video driver to perform housekeeping tasks
 * and also signals to the video grabber that your code
 * has finished using the most recent video frame returned by
 * arVideoGetImage(), and that the video driver may re-use the
 * memory occupied by the frame.
 * The effect of this call is operating-system dependent.
 * The best place to call this function is immediately after
 * you have finished displaying the current video frame
, i.e.
 * after calling arglDispImage() or argDispImage().
 *
 * \remark On some operating systems, this function is a no-op.
 * \return 0 if successful, -1 if the video driver encountered an error.
 */
AR_DLL_API  int    arVideoCapNext(void);

 

 

 다음 영상을 받는다. 성공하면 0, 실패하면 -1을 반환한다. 이 함수는 비디오가 디스플래이 되고 난 바로 다음에 실행해 주는 것이 가장 좋다.

 

 

 

4) 인식한 마커 중 사용할 마커가 있는지 찾는다.

 

    /* check for object visibility */
    k = -1;
    for( j = 0; j < marker_num; j++ ) {        //marker_num은 화면에서 찾은 마커의 개수를 의미
        if( patt_id == marker_info[j].id ) {    //patt_id : 설정한 마커의 id  
            if( k == -1 ) k = j;                      //marker_info[j].id : 화면에서 찾은 j번째 마커의 id
            else if( marker_info[k].cf < marker_info[j].cf ) k = j;
        }
    }
    if( k == -1 ) {                      //화면에서 설정한 마커를 찾지 못한 경우
        argSwapBuffers();          //후면 버퍼에 저장된 내용을 화면에 출력한다.
        return;                           //mainLoop를 종료한다.
    }

 

 

①argSwapBuffers()

 

/** \fn void argSwapBuffers( void )
* \brief swap the rendering buffer.
*
* Swap the back-buffer to the front-buffer. the
* pre-condition is that all the rendering functions have been
* called.
*/

void argSwapBuffers( void );

 

 OpenGL에서는 화면을 더욱 매끄럽게 진행하기 위해 화면에 영상을 출력하고 있을때 그 다음에 출력할 영상을 미리 메모리에 저장해둔다. 그리곤 그 미리 만든 화면(후면 버퍼에 저장된 화면)을 화면 버퍼와 변경함으로써 화면을 지우고 다시 화면에 띄울 그림을 재구성하는것 보다 매끄러운 화면 전환이 이루어지는 것이다.

 

 위 함수는 후면 버퍼에 저장되어있는 이미지를 화면 버퍼에 띄우는 함수이다.

 

 

5) 만약 인식된 마커 중 사용할 마커가 있다면, 그 마커에 대응하는 이미지를 삽입한다.   

 

    /* get the transformation between the marker and the real camera */
   arGetTransMat(&marker_info[k], patt_center, patt_width, patt_trans); // TransMatrix 생성

    draw();                                                                                      // draw함수를 이용하여 그림을 생성

    argSwapBuffers();                                                                       // 후면 버퍼의 이미지를 출력

 

arGetTransMat(&marker_info[k], patt_center, patt_width, patt_trans)

 

/**
* \brief compute camera position in function of detected markers.
*
* calculate the transformation between a detected marker and the real camera,
* i.e. the position and orientation of the camera relative to the tracking mark.
* \param marker_info the structure containing the parameters for the marker for
*                    which the camera position and orientation is to be found relative to.
*                    This structure is found using arDetectMarker.
* \param center the physical center of the marker. arGetTransMat
assumes that the marker
*              is in x-y plane, and z axis is pointing downwards from marker plane.
*              So vertex positions can be represented in 2D coordinates by ignoring the
*              z axis information. The marker vertices are specified in order of clockwise.
* \param width the size of the marker (in mm).
* \param conv the transformation matrix from the marker coordinates to camera coordinate frame,
*             that is the relative position of real camera to the real marker
* \return always 0.
*/

double arGetTransMat( ARMarkerInfo *marker_info,
                      double center[2], double width, double conv[3][4] );

 

 이미 찾아둔 마커의 정보와 설정해둔 마커의 중심과 크기, transmation matrix를 이용하여 현재 카메라에서 입력받은 영상에서 마커의 위치를 검색하여 TransMatrix를 생성한다.