#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>
#include <AR/config.h>
#include <AR/video.h>
#include <AR/param.h> //显示参数arParamDisp()
#include <AR/ar.h>
#include <AR/gsub_lite.h>
// ============================================================================
// constants
// ============================================================================
#define VIEW_SCALEFACTOR 0.025// 1.0 ARToolKit unit becomes 0.025 of my OpenGL units.范围因子,一个ARToolKit单位变成0.025个OpenGL单位。
#define VIEW_DISTANCE_MIN 0.1// Objects closer to the camera than this will not be displayed.目标与摄像机距离小于这个不显示
#define VIEW_DISTANCE_MAX10 100.0// Objects further away from the camera than this will not be displayed.大于这个不显示
//---------------------------------------------
// globle
//---------------------------------------------
static ARParam gARTCparam;//全局变量,用于储存正确的相机的参数,包括大小修改过参数
static int gPatt_id; //全局变量,存储标识的ID号
// OpenGL 初始化参数设置Preferences.
static int prefWindowed = TRUE;
static int prefWidth = 640;// Fullscreen mode width.全屏模式长宽
static int prefHeight = 480;// Fullscreen mode height.
static int prefDepth = 32;// Fullscreen mode bit depth.全屏模式字节深度
static int prefRefresh = 0;// Fullscreen mode refresh rate. Set to 0 to use default rate.刷新频率,设为0则使用默认频率。
static ARGL_CONTEXT_SETTINGS_REF gArglSettings = NULL;//保存AR的状态变量,可以显示,在debugReportMode函数中进行显示。
// Image acquisition.
static ARUint8 *gARTImage = NULL; //获取的图像
// Marker detection.
static int gARTThreshhold = 100; //标识检测时的二值化阈值
static long gCallCountMarkerDetect = 0;//
//
// Transformation matrix retrieval.转换矩阵检索
static double gPatt_width = 80.0;
static double gPatt_centre[2] = {0.0, 0.0};
static double gPatt_trans[3][4];
static int gPatt_found = FALSE;//判断是否找到标识
//计算四方体是否旋转,以及旋转角度
static int gDrawRotate = FALSE;
static float gDrawRotateAngle = 0;// For use in drawing.
//----------------------------------------------------------------
// Function
//----------------------------------------------------------------
static int setupCamera(const char *cparam_name,char *vconf,ARParam *cparam);
static int setupMarker(const char *patt_name,int *patt_id);
static void init_opengl(char **argv);
static void debugReportMode(const ARGL_CONTEXT_SETTINGS_REF arglContextSettings);
static void Display(void);
static void Reshape(int w,int h);
static void Visibility(int visible);
static void Idle(void);
static void Keyboard(unsigned char key,int x,int y);
static void DrawCube(void);
static void DrawCubeUpdate(float timeDelta);
static void Quit(void);//退出程序,清除内存
int main(int argc,char** argv)
{
const char *cparam_name = "/home/zhu/downloads/ARToolKit/examples/simple/Data/camera_para.dat";
#ifdef _WIN32
char *vconf = "Data\\WDM_camera_flipV.xml";
#else
char *vconf = "v4l2src device=/dev/video0 use-fixed-fps=false ! ffmpegcolorspace ! video/x-raw-rgb,bpp=24 ! identity name=artoolkit sync=true ! fakesink";
#endif
const char *patt_name = "/home/zhu/downloads/ARToolKit/examples/simple/Data/patt.hiro";
//--------------------------
//初始化glut库,所有OpenGL函数首先要进行这一步
//--------------------------
glutInit(&argc,argv);
//--------------------------
//打开相机,导入相机参数,更改相机长宽大小,初始化相机参数,捕获图像。
//--------------------------
if(!setupCamera(cparam_name,vconf,&gARTCparam)){
fprintf(stderr,"main():Unable to set up AR camera.\n");
exit(-1);
}
//--------------------------
//打开相机线程,与arVideoCapStop对应,合理运用可减少CPU负载
//--------------------------
if(arVideoCapStart() != 0){
fprintf(stderr,"setupCamera(): Unable to begin data capture.\n");
exit(-1);
}
//--------------------------------------------------
//设置OpenGL环境,新建glut窗口,初始化AR环境。
//--------------------------------------------------
init_opengl(argv);
arUtilTimerReset();//在刚开始的时候即帧数为0的时候,复位定时器arUtilTimerReset,以便使用arUtilTimer得到从复位开始消耗的时间。
//--------------------------
//导入标识图,一个或多个
//--------------------------
if(!setupMarker(patt_name,&gPatt_id)){
fprintf(stderr,"main():Unable to set up AR marker.\n");
Quit();//到这里为止,初始化完成,若这里再出错,则耀清除内存了。
}
//注册GLUT事件处理回调
//注意:Idle() 在Visibility中注册。
//glutReshapeFunc与glutIdelFunc这些函数都不去直接处理显示,而是设置好相应的参数,让glutdisplayfunc来执行绘制。
glutDisplayFunc(Display);//用于注册一个绘图函数, 这样操作系统在必要时刻就会对窗体进行重新绘制操作。类似于windows程序设计中处理WM_PAINT消息。具体来说呢,就是设置一个函数当需要进行画图时就调用这个函数。
glutReshapeFunc(Reshape);//当你改变窗口大小时的回调(CallBack)函数 需要你自己编写的ReShape Function来注册
glutVisibilityFunc(Visibility);//设置当前窗口的可视回调函数,这个函数设置当前窗口的可视回调函数,当窗口的可视性改变时,该窗口的可视回调函数被调用.只要窗口中的任何一个像素是可见的,或者他的任意一个子窗口中任意一个像素是可见的,GLUT则认为窗口是可见的.
glutKeyboardFunc(Keyboard);//注册当前窗口的键盘回调函数
glutMainLoop();//进入GLUT事件处理循环,让所有的与“事件”有关的函数调用无限循环。在一个GLUT程序中,这个例程被调用一次 。一旦被调用,这个程序将永远不会返回 。它将调用必要的任何已注册的回调。
return (0);
}
//打开相机,导入相机参数,更改相机长宽大小,初始化相机参数,捕获图像
static int setupCamera(const char *cparam_name, char *vconf, ARParam *cparam)
{
int xsize,ysize;//储存图像大小的值
ARParam wparam;//储存相机参数
//--------------------------------------------
//导入相机参数,或者得到相机校正参数
//--------------------------------------------
//打开视频路径
if(arVideoOpen(vconf) < 0){
fprintf(stderr,"setupCamera(): Unable to open connection to camera.\n");
return (FALSE);
}
//得到当前视频图像的大小
if(arVideoInqSize(&xsize,&ysize) < 0) return (FALSE);
fprintf(stdout,"Camera image size (x,y) = (%d,%d)\n",xsize,ysize);//debug打印出图像大小值
//导入相机参数,重新定义窗口大小
if(arParamLoad(cparam_name,1,&wparam) < 0){
fprintf(stderr,"setupCamera(): Error loading parameter file %s for camera.\n",cparam_name);//两种情况:文件未找到,文件结构不对
return (FALSE);
}
arParamChangeSize(&wparam,xsize,ysize,cparam);
fprintf(stdout,"*** Camera Parameter ***\n");
arParamDisp(cparam);//显示参数值
arInitCparam(cparam);//初始化相机参数
return (TRUE);
}
//导入标识图像矩阵,一个或多个
static int setupMarker(const char *patt_name, int *patt_id)
{
//-----------------------------------
//导入标识图,一个或多个
//-----------------------------------
if((*patt_id = arLoadPatt(patt_name)) < 0){
fprintf(stderr,"setupMarker():pattern load error!!\n");
return (FALSE);
}
return (TRUE);
}
//初始化OpenGL显示环境
static void init_opengl(char** argv)
{
char glutGamemode[32];
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);//设置初始显示模式:指定 RGBA 颜色模式的窗口,指定双缓存窗口,窗口使用深度缓存
if(!prefWindowed){
if(prefRefresh)
sprintf(glutGamemode,"%ix%i:%i@%i",prefWidth,prefHeight,prefDepth,prefRefresh);
else
sprintf(glutGamemode,"%ix%i:%i",prefWidth,prefHeight,prefDepth);//GLUT的游戏模式是为开启高性能全屏渲染而设计的.告诉GLUT全屏模式的设置,设置包括屏幕分辨率,像素深度和刷新频率,640x512:32 @默认刷新频率.
if(glutGameModeGet(GLUT_GAME_MODE_POSSIBLE))//检查给定的游戏模式是否有效,by zhuzhu
glutEnterGameMode();//进入或至少可以尝试进入游戏模式.函数会准确的设置屏幕到指定配置,只要是可行的.
}
else{
glutInitWindowSize(prefWidth,prefHeight);//若不进入游戏模式,就直接指定窗口大小,新建一个窗口
glutCreateWindow(argv[0]);
}
//为当前OpenGL环境建立argl库
//arglSetupForCurrentContext()是针对当前的OpenGL环境初始化Gsub_Lite库;返回一个ARGL_CONTEXT_SETTINGS_REF参数,这个参数就代表OpenGL在这个机器上的状态,包括纹理和显示列表,可传递,Gsub_Lite要跟踪这个参数,特别是在显示多个窗口时;使用结束后,可用arglCleanup()清除这个参数。
if((gArglSettings = arglSetupForCurrentContext()) == NULL){
fprintf(stderr,"main(): arglSetupForCurrentContext() returned error.\n");
exit(-1);
}
debugReportMode(gArglSettings);//显示当前AR的系统变量状态。
glEnable(GL_DEPTH_TEST);//使能深度测试
}
// Report state of ARToolKit global variables arFittingMode,
// arImageProcMode, arglDrawMode, arTemplateMatchingMode, arMatchingPCAMode.
// 报告这些变量的状态。显示这个变量的值。
static void debugReportMode(const ARGL_CONTEXT_SETTINGS_REF arglContextSettings)
{
// 1:arFittingMode:ARToolKit的合适的显示模式:对相机失真的校正模式,可以用纹理映射使能校正,可能的值:AR_FITTING_TO_INPUT:输入图像;AR_FITTING_TO_IDEAL:默认的补偿图像。
if(arFittingMode == AR_FITTING_TO_INPUT){
fprintf(stderr,"FittingMode(Z):INPUT IMAGE\n");
} else{
fprintf(stderr,"FittingMode(Z):COMPENSATED IMAGE\n");
}
// 2:定义标识图像分辨率的大小:如果图像整体被分析,这个变量的值:AR_IMAGE_PROC_IN_FULL:使用全分辨率图像;AR_IMAGE_PROC_IN_HALF:使用半分辨率图像,默认模式。
if (arImageProcMode == AR_IMAGE_PROC_IN_FULL) {
fprintf(stderr, "ProcMode (X) : FULL IMAGE\n");
} else {
fprintf(stderr, "ProcMode (X) : HALF IMAGE\n");
}
// 3:定义绘制后台显示的配置模式:AR_DRAW_BY_GL_DRAW_PIXELS:使用GL_DRAW_PIXELS函数;AR_DRAW_BY_TEXTURE_MAPPING:使用四方格进行纹理映射模式;
if (arglDrawModeGet(arglContextSettings) == AR_DRAW_BY_GL_DRAW_PIXELS) {
fprintf(stderr, "DrawMode (C) : GL_DRAW_PIXELS\n");
} else if (arglTexmapModeGet(arglContextSettings) == AR_DRAW_TEXTURE_FULL_IMAGE) {
fprintf(stderr, "DrawMode (C) : TEXTURE MAPPING (FULL RESOLUTION)\n");
} else {
fprintf(stderr, "DrawMode (C) : TEXTURE MAPPING (HALF RESOLUTION)\n");
}
// 4:这个可能的值:AR_TEMPLATE_MATCHING_COLOR:颜色模板;AR_TEMPLATE_MATCHING_BW:BW模板?
if (arTemplateMatchingMode == AR_TEMPLATE_MATCHING_COLOR) {
fprintf(stderr, "TemplateMatchingMode (M) : Color Template\n");
} else {
fprintf(stderr, "TemplateMatchingMode (M) : BW Template\n");
}
// 5:这个可能的值:-AR_MATCHING_WITHOUT_PCA:没有PCA;-AR_MATCHING_WITH_PCA:有PCA,默认模式。
if (arMatchingPCAMode == AR_MATCHING_WITHOUT_PCA) {
fprintf(stderr, "MatchingPCAMode (P) : Without PCA\n");
} else {
fprintf(stderr, "MatchingPCAMode (P) : With PCA\n");
}
}
static void Display(void)
{
GLdouble p[16];
GLdouble m[16];
//第一步:清除屏幕,显示最新的后台缓冲帧
glDrawBuffer(GL_BACK);//指定哪个颜色缓存将要被绘制;当像素颜色被写到帧缓存时,他们被写进由glDrawBuffer指定的颜色缓存区:GL_NONE没有缓存区要被写入;GL_FRONT_LEFT只有左前缓存区被写入;类似的都是这样。这里是后缓存区被绘制。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//为绘制新帧清除之前帧的颜色缓存和深度缓存。
arglDispImage(gARTImage,&gARTCparam,1.0,gArglSettings);//zoom = 1.0.在后台缓存中显示相机图像作为背景,这个必须在渲染3D目标前调用。根据你的argDrawMode,argTexnapMode和内部图像的格式,调用的OpenGL函数是不同的。
arVideoCapNext();//开启下一帧,这个函数应该每一帧调用一次,它可以允许视频驱动完成常规任务,给视频捕获器信号表示最近的视频帧已经处理完毕,视频驱动可以重新使用被原来帧占用的内存。最好的调用地方是在arglDispImage和argDispImage之后。在一些操作系统中相当于一个no-op,空指令。返回:成功为0,-1是视频驱动遇到错误。
gARTImage = NULL;//当调用了arVideoCapNext(),此时图像数据不再有效。等待接收下一帧图像。这里以上是清除屏幕,绘制后台最新的图像帧!!
//第二步:根据相机参数设置投影矩阵
//投影变换:创建一个透视投影矩阵,传递给OpenGL设置模型视景矩阵。
arglCameraFrustumRH(&gARTCparam,VIEW_DISTANCE_MIN,VIEW_DISTANCE_MAX10,p);//把相机参数转换成OpenGL投影矩阵16的数组,然后直接导入,生成的三维物体就会匹配真实的相机视场。
glMatrixMode(GL_PROJECTION);
glLoadMatrixd(p);
glMatrixMode(GL_MODELVIEW);
//视景变换:
glLoadIdentity();//重置当前指定的矩阵为单位矩阵
//目标随摄像机移动的光照和几何一致性应该在这里,必须在视景变换之前指明。
//第三步:寻找标识,找到了之后就定位相机观察到的标识,在标识上放置系统原点
if(gPatt_found){//这里是找到标识物了!!!!!!!
//Calculate the camera position relative to the marker.
//计算相机相对于标识的位置。
//Replace VIEW_SCALEFACTOR with 1.0 to make one drawing unit equal to 1.0 ARToolKit units(mm为单位)
arglCameraViewRH(gPatt_trans,m,VIEW_SCALEFACTOR);//创建一个视景矩阵,传递给OpenGL设置虚拟相机的视景变换。转换标识转换矩阵成OpenGL视景矩阵,这16个值就是真实相机的位置和方向,使用他们设置虚拟相机的位置,使三维目标准确的放置在物理标识上。
glLoadMatrixd(m);//这里是设置虚拟相机的位置。
//第四步:在标识上画出虚拟物体。每次执行这个绘制函数时,OpenGL系统的原点就在物理标识的中间,注意不要绘制-z部分的东西。
//所有的三维目标相对于标识的光照和几何一致性在这里
DrawCube();//绘制一个带颜色的正方体
}
//All 2D overlays go here.
//none
glutSwapBuffers();//设置双缓存显示。
}
//手动改变显示窗口大小时调用的程序,当窗口大小变化时,为了防止物体变形,这时要重设投影转换矩阵,设置视口转换矩阵,以及视图转换矩阵。
static void Reshape(int w,int h)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0,0,(GLsizei) w,(GLsizei) h);//获得窗口的大小,负责把视景体截取的图像按照怎样的高和宽显示到屏幕上
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//当窗口的可视化改变时,调用该函数,包括第一次可视化也要调用。每次循环至少调用一次Idle函数。
static void Visibility(int visible)
{
if(visible == GLUT_VISIBLE){
glutIdleFunc(Idle);
} else{
glutIdleFunc(NULL);
}
}
static void Idle(void)
{
static int ms_prev;
int ms;
float s_elapsed;
ARUint8 *image;
ARMarkerInfo *marker_info;
int marker_num;
int j,k;
//计算上次运行Idle函数到现在用了多长时间
ms = glutGet(GLUT_ELAPSED_TIME);
s_elapsed = (float)(ms - ms_prev) * 0.001;
if(s_elapsed < 0.01f) return; //大于100HZ时不更新
ms_prev = ms;
//Update drawing.
DrawCubeUpdate(s_elapsed);//本帧比上一帧四方体旋转角度;
//捕获一帧图像
if((image = arVideoGetImage()) != NULL){
gARTImage = image; //缓存这一帧图像
gCallCountMarkerDetect++;//增加ARToolKit 帧频计数器
//在图像帧中检测标识
if(arDetectMarker(gARTImage,gARTThreshhold,&marker_info,&marker_num) < 0){
exit(-1);
}
//从检测出的形似标识物的数组中按确信度匹配标识
k = -1;
for(j = 0;j < marker_num;j++){
if(marker_info[j].id == gPatt_id){
if(k == -1) k = j;//检测到的第一个标识
else if(marker_info[j].cf > marker_info[k].cf) k = j;//找到最高可信度的那个
}
}
if(k != -1){
//得到标识物和真实相机的变换矩阵
arGetTransMat(&(marker_info[k]),gPatt_centre,gPatt_width,gPatt_trans);
gPatt_found = TRUE;
} else{
gPatt_found = FALSE;
}
glutPostRedisplay();//告诉glut,要显示的内容已经改变了。标记当前窗口需要重新绘制。在下一次循环中,display函数将被回调,重新绘制窗口。节省CPU资源,防止display函数被频繁调用,即使没有更新。
}
}
static void Keyboard(unsigned char key, int x, int y)
{
int mode;
switch (key) {
case 0x1B: // Quit.
case 'Q':
case 'q':
Quit();
break;
case ' ':
gDrawRotate = !gDrawRotate;//旋转立方体
break;
case 'C':
case 'c'://绘制模式从DRAW_PIXELS到AR_DRAW_BY_TEXTURE_MAPPING和FULL_IMAGE 到HALF_IMAGE 再到PIXELS;绘制模式的变化,纹理映射的分辨率变化
mode = arglDrawModeGet(gArglSettings);
if (mode == AR_DRAW_BY_GL_DRAW_PIXELS) {
arglDrawModeSet(gArglSettings, AR_DRAW_BY_TEXTURE_MAPPING);
arglTexmapModeSet(gArglSettings, AR_DRAW_TEXTURE_FULL_IMAGE);
} else {
mode = arglTexmapModeGet(gArglSettings);
if (mode == AR_DRAW_TEXTURE_FULL_IMAGE)
arglTexmapModeSet(gArglSettings, AR_DRAW_TEXTURE_HALF_IMAGE);
else
arglDrawModeSet(gArglSettings, AR_DRAW_BY_GL_DRAW_PIXELS);
}
fprintf(stderr, "*** Camera - %f (frame/sec)\n", (double)gCallCountMarkerDetect/arUtilTimer());
gCallCountMarkerDetect = 0;
arUtilTimerReset();
debugReportMode(gArglSettings);
break;
case 'D':
case 'd':
arDebug = !arDebug;//这是ARToolKit内部变量,激活debug模式,0不debug,1debug,默认0;
break;
case '?':
case '/':
printf("Keys:\n");
printf(" q or [esc] Quit demo.\n");
printf("space rorate the cube\n");
printf(" c Change arglDrawMode and arglTexmapMode.\n");
printf(" d Activate / deactivate debug mode.\n");
printf(" ? or / Show this help.\n");
printf("\nAdditionally, the ARVideo library supplied the following help text:\n");
arVideoDispOption();//显示视频选项
break;
default:
break;
}
}
// Something to look at, draw a rotating colour cube.
static void DrawCube(void)
{
// Colour cube data.
static GLuint polyList = 0;
float fSize = 0.5f;
long f, i;
const GLfloat cube_vertices [8][3] = {
{1.0, 1.0, 1.0}, {1.0, -1.0, 1.0}, {-1.0, -1.0, 1.0}, {-1.0, 1.0, 1.0},
{1.0, 1.0, -1.0}, {1.0, -1.0, -1.0}, {-1.0, -1.0, -1.0}, {-1.0, 1.0, -1.0} };
const GLfloat cube_vertex_colors [8][3] = {
{1.0, 1.0, 1.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 1.0},
{1.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 1.0} };
GLint cube_num_faces = 6;
const short cube_faces [6][4] = {
{3, 2, 1, 0}, {2, 3, 7, 6}, {0, 1, 5, 4}, {3, 0, 4, 7}, {1, 2, 6, 5}, {4, 5, 6, 7} };
if (!polyList) {
polyList = glGenLists (1);//glGenLists()唯一的标识一个显示列表
glNewList(polyList, GL_COMPILE);//用于对显示列表进行定界。第一个参数是一个整形索引值,由glGenLists()指定,第二个参数指只是编译。
glBegin (GL_QUADS);//画四方体
for (f = 0; f < cube_num_faces; f++)
for (i = 0; i < 4; i++) {
glColor3f (cube_vertex_colors[cube_faces[f][i]][0], cube_vertex_colors[cube_faces[f][i]][1], cube_vertex_colors[cube_faces[f][i]][2]);
glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize, cube_vertices[cube_faces[f][i]][1] * fSize, cube_vertices[cube_faces[f][i]][2] * fSize);
}
glEnd ();
glColor3f (0.0, 0.0, 0.0);
for (f = 0; f < cube_num_faces; f++) {
glBegin (GL_LINE_LOOP);
for (i = 0; i < 4; i++)
glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize, cube_vertices[cube_faces[f][i]][1] * fSize, cube_vertices[cube_faces[f][i]][2] * fSize);
glEnd ();
}
glEndList ();
}
glPushMatrix(); // Save world coordinate system.//保存当前矩阵
glTranslatef(0.0, 0.0, 0.5); // Place base of cube on marker surface.往z轴移动0.5
glRotatef(gDrawRotateAngle, 0.0, 0.0, 1.0); // Rotate about z axis.根据参数进行选准
glDisable(GL_LIGHTING); // Just use colours.//不使能光照。
glCallList(polyList); // Draw the cube.
glPopMatrix(); // Restore world coordinate system.//弹出栈顶的矩阵
}
static void DrawCubeUpdate(float timeDelta)//每秒转动45度,按帧频计算每帧转动角度。
{
if (gDrawRotate) {
gDrawRotateAngle += timeDelta * 45.0f; // Rotate cube at 45 degrees per second.
if (gDrawRotateAngle > 360.0f) gDrawRotateAngle -= 360.0f;
}
}
//清除使用过的内存,释放资源。
static void Quit(void)
{
arglCleanup(gArglSettings);
arVideoCapStop();
arVideoClose();
exit(0);
}
#include <stdlib.h>
#include <GL/glut.h>
#include <AR/config.h>
#include <AR/video.h>
#include <AR/param.h> //显示参数arParamDisp()
#include <AR/ar.h>
#include <AR/gsub_lite.h>
// ============================================================================
// constants
// ============================================================================
#define VIEW_SCALEFACTOR 0.025// 1.0 ARToolKit unit becomes 0.025 of my OpenGL units.范围因子,一个ARToolKit单位变成0.025个OpenGL单位。
#define VIEW_DISTANCE_MIN 0.1// Objects closer to the camera than this will not be displayed.目标与摄像机距离小于这个不显示
#define VIEW_DISTANCE_MAX10 100.0// Objects further away from the camera than this will not be displayed.大于这个不显示
//---------------------------------------------
// globle
//---------------------------------------------
static ARParam gARTCparam;//全局变量,用于储存正确的相机的参数,包括大小修改过参数
static int gPatt_id; //全局变量,存储标识的ID号
// OpenGL 初始化参数设置Preferences.
static int prefWindowed = TRUE;
static int prefWidth = 640;// Fullscreen mode width.全屏模式长宽
static int prefHeight = 480;// Fullscreen mode height.
static int prefDepth = 32;// Fullscreen mode bit depth.全屏模式字节深度
static int prefRefresh = 0;// Fullscreen mode refresh rate. Set to 0 to use default rate.刷新频率,设为0则使用默认频率。
static ARGL_CONTEXT_SETTINGS_REF gArglSettings = NULL;//保存AR的状态变量,可以显示,在debugReportMode函数中进行显示。
// Image acquisition.
static ARUint8 *gARTImage = NULL; //获取的图像
// Marker detection.
static int gARTThreshhold = 100; //标识检测时的二值化阈值
static long gCallCountMarkerDetect = 0;//
//
// Transformation matrix retrieval.转换矩阵检索
static double gPatt_width = 80.0;
static double gPatt_centre[2] = {0.0, 0.0};
static double gPatt_trans[3][4];
static int gPatt_found = FALSE;//判断是否找到标识
//计算四方体是否旋转,以及旋转角度
static int gDrawRotate = FALSE;
static float gDrawRotateAngle = 0;// For use in drawing.
//----------------------------------------------------------------
// Function
//----------------------------------------------------------------
static int setupCamera(const char *cparam_name,char *vconf,ARParam *cparam);
static int setupMarker(const char *patt_name,int *patt_id);
static void init_opengl(char **argv);
static void debugReportMode(const ARGL_CONTEXT_SETTINGS_REF arglContextSettings);
static void Display(void);
static void Reshape(int w,int h);
static void Visibility(int visible);
static void Idle(void);
static void Keyboard(unsigned char key,int x,int y);
static void DrawCube(void);
static void DrawCubeUpdate(float timeDelta);
static void Quit(void);//退出程序,清除内存
int main(int argc,char** argv)
{
const char *cparam_name = "/home/zhu/downloads/ARToolKit/examples/simple/Data/camera_para.dat";
#ifdef _WIN32
char *vconf = "Data\\WDM_camera_flipV.xml";
#else
char *vconf = "v4l2src device=/dev/video0 use-fixed-fps=false ! ffmpegcolorspace ! video/x-raw-rgb,bpp=24 ! identity name=artoolkit sync=true ! fakesink";
#endif
const char *patt_name = "/home/zhu/downloads/ARToolKit/examples/simple/Data/patt.hiro";
//--------------------------
//初始化glut库,所有OpenGL函数首先要进行这一步
//--------------------------
glutInit(&argc,argv);
//--------------------------
//打开相机,导入相机参数,更改相机长宽大小,初始化相机参数,捕获图像。
//--------------------------
if(!setupCamera(cparam_name,vconf,&gARTCparam)){
fprintf(stderr,"main():Unable to set up AR camera.\n");
exit(-1);
}
//--------------------------
//打开相机线程,与arVideoCapStop对应,合理运用可减少CPU负载
//--------------------------
if(arVideoCapStart() != 0){
fprintf(stderr,"setupCamera(): Unable to begin data capture.\n");
exit(-1);
}
//--------------------------------------------------
//设置OpenGL环境,新建glut窗口,初始化AR环境。
//--------------------------------------------------
init_opengl(argv);
arUtilTimerReset();//在刚开始的时候即帧数为0的时候,复位定时器arUtilTimerReset,以便使用arUtilTimer得到从复位开始消耗的时间。
//--------------------------
//导入标识图,一个或多个
//--------------------------
if(!setupMarker(patt_name,&gPatt_id)){
fprintf(stderr,"main():Unable to set up AR marker.\n");
Quit();//到这里为止,初始化完成,若这里再出错,则耀清除内存了。
}
//注册GLUT事件处理回调
//注意:Idle() 在Visibility中注册。
//glutReshapeFunc与glutIdelFunc这些函数都不去直接处理显示,而是设置好相应的参数,让glutdisplayfunc来执行绘制。
glutDisplayFunc(Display);//用于注册一个绘图函数, 这样操作系统在必要时刻就会对窗体进行重新绘制操作。类似于windows程序设计中处理WM_PAINT消息。具体来说呢,就是设置一个函数当需要进行画图时就调用这个函数。
glutReshapeFunc(Reshape);//当你改变窗口大小时的回调(CallBack)函数 需要你自己编写的ReShape Function来注册
glutVisibilityFunc(Visibility);//设置当前窗口的可视回调函数,这个函数设置当前窗口的可视回调函数,当窗口的可视性改变时,该窗口的可视回调函数被调用.只要窗口中的任何一个像素是可见的,或者他的任意一个子窗口中任意一个像素是可见的,GLUT则认为窗口是可见的.
glutKeyboardFunc(Keyboard);//注册当前窗口的键盘回调函数
glutMainLoop();//进入GLUT事件处理循环,让所有的与“事件”有关的函数调用无限循环。在一个GLUT程序中,这个例程被调用一次 。一旦被调用,这个程序将永远不会返回 。它将调用必要的任何已注册的回调。
return (0);
}
//打开相机,导入相机参数,更改相机长宽大小,初始化相机参数,捕获图像
static int setupCamera(const char *cparam_name, char *vconf, ARParam *cparam)
{
int xsize,ysize;//储存图像大小的值
ARParam wparam;//储存相机参数
//--------------------------------------------
//导入相机参数,或者得到相机校正参数
//--------------------------------------------
//打开视频路径
if(arVideoOpen(vconf) < 0){
fprintf(stderr,"setupCamera(): Unable to open connection to camera.\n");
return (FALSE);
}
//得到当前视频图像的大小
if(arVideoInqSize(&xsize,&ysize) < 0) return (FALSE);
fprintf(stdout,"Camera image size (x,y) = (%d,%d)\n",xsize,ysize);//debug打印出图像大小值
//导入相机参数,重新定义窗口大小
if(arParamLoad(cparam_name,1,&wparam) < 0){
fprintf(stderr,"setupCamera(): Error loading parameter file %s for camera.\n",cparam_name);//两种情况:文件未找到,文件结构不对
return (FALSE);
}
arParamChangeSize(&wparam,xsize,ysize,cparam);
fprintf(stdout,"*** Camera Parameter ***\n");
arParamDisp(cparam);//显示参数值
arInitCparam(cparam);//初始化相机参数
return (TRUE);
}
//导入标识图像矩阵,一个或多个
static int setupMarker(const char *patt_name, int *patt_id)
{
//-----------------------------------
//导入标识图,一个或多个
//-----------------------------------
if((*patt_id = arLoadPatt(patt_name)) < 0){
fprintf(stderr,"setupMarker():pattern load error!!\n");
return (FALSE);
}
return (TRUE);
}
//初始化OpenGL显示环境
static void init_opengl(char** argv)
{
char glutGamemode[32];
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);//设置初始显示模式:指定 RGBA 颜色模式的窗口,指定双缓存窗口,窗口使用深度缓存
if(!prefWindowed){
if(prefRefresh)
sprintf(glutGamemode,"%ix%i:%i@%i",prefWidth,prefHeight,prefDepth,prefRefresh);
else
sprintf(glutGamemode,"%ix%i:%i",prefWidth,prefHeight,prefDepth);//GLUT的游戏模式是为开启高性能全屏渲染而设计的.告诉GLUT全屏模式的设置,设置包括屏幕分辨率,像素深度和刷新频率,640x512:32 @默认刷新频率.
if(glutGameModeGet(GLUT_GAME_MODE_POSSIBLE))//检查给定的游戏模式是否有效,by zhuzhu
glutEnterGameMode();//进入或至少可以尝试进入游戏模式.函数会准确的设置屏幕到指定配置,只要是可行的.
}
else{
glutInitWindowSize(prefWidth,prefHeight);//若不进入游戏模式,就直接指定窗口大小,新建一个窗口
glutCreateWindow(argv[0]);
}
//为当前OpenGL环境建立argl库
//arglSetupForCurrentContext()是针对当前的OpenGL环境初始化Gsub_Lite库;返回一个ARGL_CONTEXT_SETTINGS_REF参数,这个参数就代表OpenGL在这个机器上的状态,包括纹理和显示列表,可传递,Gsub_Lite要跟踪这个参数,特别是在显示多个窗口时;使用结束后,可用arglCleanup()清除这个参数。
if((gArglSettings = arglSetupForCurrentContext()) == NULL){
fprintf(stderr,"main(): arglSetupForCurrentContext() returned error.\n");
exit(-1);
}
debugReportMode(gArglSettings);//显示当前AR的系统变量状态。
glEnable(GL_DEPTH_TEST);//使能深度测试
}
// Report state of ARToolKit global variables arFittingMode,
// arImageProcMode, arglDrawMode, arTemplateMatchingMode, arMatchingPCAMode.
// 报告这些变量的状态。显示这个变量的值。
static void debugReportMode(const ARGL_CONTEXT_SETTINGS_REF arglContextSettings)
{
// 1:arFittingMode:ARToolKit的合适的显示模式:对相机失真的校正模式,可以用纹理映射使能校正,可能的值:AR_FITTING_TO_INPUT:输入图像;AR_FITTING_TO_IDEAL:默认的补偿图像。
if(arFittingMode == AR_FITTING_TO_INPUT){
fprintf(stderr,"FittingMode(Z):INPUT IMAGE\n");
} else{
fprintf(stderr,"FittingMode(Z):COMPENSATED IMAGE\n");
}
// 2:定义标识图像分辨率的大小:如果图像整体被分析,这个变量的值:AR_IMAGE_PROC_IN_FULL:使用全分辨率图像;AR_IMAGE_PROC_IN_HALF:使用半分辨率图像,默认模式。
if (arImageProcMode == AR_IMAGE_PROC_IN_FULL) {
fprintf(stderr, "ProcMode (X) : FULL IMAGE\n");
} else {
fprintf(stderr, "ProcMode (X) : HALF IMAGE\n");
}
// 3:定义绘制后台显示的配置模式:AR_DRAW_BY_GL_DRAW_PIXELS:使用GL_DRAW_PIXELS函数;AR_DRAW_BY_TEXTURE_MAPPING:使用四方格进行纹理映射模式;
if (arglDrawModeGet(arglContextSettings) == AR_DRAW_BY_GL_DRAW_PIXELS) {
fprintf(stderr, "DrawMode (C) : GL_DRAW_PIXELS\n");
} else if (arglTexmapModeGet(arglContextSettings) == AR_DRAW_TEXTURE_FULL_IMAGE) {
fprintf(stderr, "DrawMode (C) : TEXTURE MAPPING (FULL RESOLUTION)\n");
} else {
fprintf(stderr, "DrawMode (C) : TEXTURE MAPPING (HALF RESOLUTION)\n");
}
// 4:这个可能的值:AR_TEMPLATE_MATCHING_COLOR:颜色模板;AR_TEMPLATE_MATCHING_BW:BW模板?
if (arTemplateMatchingMode == AR_TEMPLATE_MATCHING_COLOR) {
fprintf(stderr, "TemplateMatchingMode (M) : Color Template\n");
} else {
fprintf(stderr, "TemplateMatchingMode (M) : BW Template\n");
}
// 5:这个可能的值:-AR_MATCHING_WITHOUT_PCA:没有PCA;-AR_MATCHING_WITH_PCA:有PCA,默认模式。
if (arMatchingPCAMode == AR_MATCHING_WITHOUT_PCA) {
fprintf(stderr, "MatchingPCAMode (P) : Without PCA\n");
} else {
fprintf(stderr, "MatchingPCAMode (P) : With PCA\n");
}
}
static void Display(void)
{
GLdouble p[16];
GLdouble m[16];
//第一步:清除屏幕,显示最新的后台缓冲帧
glDrawBuffer(GL_BACK);//指定哪个颜色缓存将要被绘制;当像素颜色被写到帧缓存时,他们被写进由glDrawBuffer指定的颜色缓存区:GL_NONE没有缓存区要被写入;GL_FRONT_LEFT只有左前缓存区被写入;类似的都是这样。这里是后缓存区被绘制。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//为绘制新帧清除之前帧的颜色缓存和深度缓存。
arglDispImage(gARTImage,&gARTCparam,1.0,gArglSettings);//zoom = 1.0.在后台缓存中显示相机图像作为背景,这个必须在渲染3D目标前调用。根据你的argDrawMode,argTexnapMode和内部图像的格式,调用的OpenGL函数是不同的。
arVideoCapNext();//开启下一帧,这个函数应该每一帧调用一次,它可以允许视频驱动完成常规任务,给视频捕获器信号表示最近的视频帧已经处理完毕,视频驱动可以重新使用被原来帧占用的内存。最好的调用地方是在arglDispImage和argDispImage之后。在一些操作系统中相当于一个no-op,空指令。返回:成功为0,-1是视频驱动遇到错误。
gARTImage = NULL;//当调用了arVideoCapNext(),此时图像数据不再有效。等待接收下一帧图像。这里以上是清除屏幕,绘制后台最新的图像帧!!
//第二步:根据相机参数设置投影矩阵
//投影变换:创建一个透视投影矩阵,传递给OpenGL设置模型视景矩阵。
arglCameraFrustumRH(&gARTCparam,VIEW_DISTANCE_MIN,VIEW_DISTANCE_MAX10,p);//把相机参数转换成OpenGL投影矩阵16的数组,然后直接导入,生成的三维物体就会匹配真实的相机视场。
glMatrixMode(GL_PROJECTION);
glLoadMatrixd(p);
glMatrixMode(GL_MODELVIEW);
//视景变换:
glLoadIdentity();//重置当前指定的矩阵为单位矩阵
//目标随摄像机移动的光照和几何一致性应该在这里,必须在视景变换之前指明。
//第三步:寻找标识,找到了之后就定位相机观察到的标识,在标识上放置系统原点
if(gPatt_found){//这里是找到标识物了!!!!!!!
//Calculate the camera position relative to the marker.
//计算相机相对于标识的位置。
//Replace VIEW_SCALEFACTOR with 1.0 to make one drawing unit equal to 1.0 ARToolKit units(mm为单位)
arglCameraViewRH(gPatt_trans,m,VIEW_SCALEFACTOR);//创建一个视景矩阵,传递给OpenGL设置虚拟相机的视景变换。转换标识转换矩阵成OpenGL视景矩阵,这16个值就是真实相机的位置和方向,使用他们设置虚拟相机的位置,使三维目标准确的放置在物理标识上。
glLoadMatrixd(m);//这里是设置虚拟相机的位置。
//第四步:在标识上画出虚拟物体。每次执行这个绘制函数时,OpenGL系统的原点就在物理标识的中间,注意不要绘制-z部分的东西。
//所有的三维目标相对于标识的光照和几何一致性在这里
DrawCube();//绘制一个带颜色的正方体
}
//All 2D overlays go here.
//none
glutSwapBuffers();//设置双缓存显示。
}
//手动改变显示窗口大小时调用的程序,当窗口大小变化时,为了防止物体变形,这时要重设投影转换矩阵,设置视口转换矩阵,以及视图转换矩阵。
static void Reshape(int w,int h)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0,0,(GLsizei) w,(GLsizei) h);//获得窗口的大小,负责把视景体截取的图像按照怎样的高和宽显示到屏幕上
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//当窗口的可视化改变时,调用该函数,包括第一次可视化也要调用。每次循环至少调用一次Idle函数。
static void Visibility(int visible)
{
if(visible == GLUT_VISIBLE){
glutIdleFunc(Idle);
} else{
glutIdleFunc(NULL);
}
}
static void Idle(void)
{
static int ms_prev;
int ms;
float s_elapsed;
ARUint8 *image;
ARMarkerInfo *marker_info;
int marker_num;
int j,k;
//计算上次运行Idle函数到现在用了多长时间
ms = glutGet(GLUT_ELAPSED_TIME);
s_elapsed = (float)(ms - ms_prev) * 0.001;
if(s_elapsed < 0.01f) return; //大于100HZ时不更新
ms_prev = ms;
//Update drawing.
DrawCubeUpdate(s_elapsed);//本帧比上一帧四方体旋转角度;
//捕获一帧图像
if((image = arVideoGetImage()) != NULL){
gARTImage = image; //缓存这一帧图像
gCallCountMarkerDetect++;//增加ARToolKit 帧频计数器
//在图像帧中检测标识
if(arDetectMarker(gARTImage,gARTThreshhold,&marker_info,&marker_num) < 0){
exit(-1);
}
//从检测出的形似标识物的数组中按确信度匹配标识
k = -1;
for(j = 0;j < marker_num;j++){
if(marker_info[j].id == gPatt_id){
if(k == -1) k = j;//检测到的第一个标识
else if(marker_info[j].cf > marker_info[k].cf) k = j;//找到最高可信度的那个
}
}
if(k != -1){
//得到标识物和真实相机的变换矩阵
arGetTransMat(&(marker_info[k]),gPatt_centre,gPatt_width,gPatt_trans);
gPatt_found = TRUE;
} else{
gPatt_found = FALSE;
}
glutPostRedisplay();//告诉glut,要显示的内容已经改变了。标记当前窗口需要重新绘制。在下一次循环中,display函数将被回调,重新绘制窗口。节省CPU资源,防止display函数被频繁调用,即使没有更新。
}
}
static void Keyboard(unsigned char key, int x, int y)
{
int mode;
switch (key) {
case 0x1B: // Quit.
case 'Q':
case 'q':
Quit();
break;
case ' ':
gDrawRotate = !gDrawRotate;//旋转立方体
break;
case 'C':
case 'c'://绘制模式从DRAW_PIXELS到AR_DRAW_BY_TEXTURE_MAPPING和FULL_IMAGE 到HALF_IMAGE 再到PIXELS;绘制模式的变化,纹理映射的分辨率变化
mode = arglDrawModeGet(gArglSettings);
if (mode == AR_DRAW_BY_GL_DRAW_PIXELS) {
arglDrawModeSet(gArglSettings, AR_DRAW_BY_TEXTURE_MAPPING);
arglTexmapModeSet(gArglSettings, AR_DRAW_TEXTURE_FULL_IMAGE);
} else {
mode = arglTexmapModeGet(gArglSettings);
if (mode == AR_DRAW_TEXTURE_FULL_IMAGE)
arglTexmapModeSet(gArglSettings, AR_DRAW_TEXTURE_HALF_IMAGE);
else
arglDrawModeSet(gArglSettings, AR_DRAW_BY_GL_DRAW_PIXELS);
}
fprintf(stderr, "*** Camera - %f (frame/sec)\n", (double)gCallCountMarkerDetect/arUtilTimer());
gCallCountMarkerDetect = 0;
arUtilTimerReset();
debugReportMode(gArglSettings);
break;
case 'D':
case 'd':
arDebug = !arDebug;//这是ARToolKit内部变量,激活debug模式,0不debug,1debug,默认0;
break;
case '?':
case '/':
printf("Keys:\n");
printf(" q or [esc] Quit demo.\n");
printf("space rorate the cube\n");
printf(" c Change arglDrawMode and arglTexmapMode.\n");
printf(" d Activate / deactivate debug mode.\n");
printf(" ? or / Show this help.\n");
printf("\nAdditionally, the ARVideo library supplied the following help text:\n");
arVideoDispOption();//显示视频选项
break;
default:
break;
}
}
// Something to look at, draw a rotating colour cube.
static void DrawCube(void)
{
// Colour cube data.
static GLuint polyList = 0;
float fSize = 0.5f;
long f, i;
const GLfloat cube_vertices [8][3] = {
{1.0, 1.0, 1.0}, {1.0, -1.0, 1.0}, {-1.0, -1.0, 1.0}, {-1.0, 1.0, 1.0},
{1.0, 1.0, -1.0}, {1.0, -1.0, -1.0}, {-1.0, -1.0, -1.0}, {-1.0, 1.0, -1.0} };
const GLfloat cube_vertex_colors [8][3] = {
{1.0, 1.0, 1.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 1.0},
{1.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 1.0} };
GLint cube_num_faces = 6;
const short cube_faces [6][4] = {
{3, 2, 1, 0}, {2, 3, 7, 6}, {0, 1, 5, 4}, {3, 0, 4, 7}, {1, 2, 6, 5}, {4, 5, 6, 7} };
if (!polyList) {
polyList = glGenLists (1);//glGenLists()唯一的标识一个显示列表
glNewList(polyList, GL_COMPILE);//用于对显示列表进行定界。第一个参数是一个整形索引值,由glGenLists()指定,第二个参数指只是编译。
glBegin (GL_QUADS);//画四方体
for (f = 0; f < cube_num_faces; f++)
for (i = 0; i < 4; i++) {
glColor3f (cube_vertex_colors[cube_faces[f][i]][0], cube_vertex_colors[cube_faces[f][i]][1], cube_vertex_colors[cube_faces[f][i]][2]);
glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize, cube_vertices[cube_faces[f][i]][1] * fSize, cube_vertices[cube_faces[f][i]][2] * fSize);
}
glEnd ();
glColor3f (0.0, 0.0, 0.0);
for (f = 0; f < cube_num_faces; f++) {
glBegin (GL_LINE_LOOP);
for (i = 0; i < 4; i++)
glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize, cube_vertices[cube_faces[f][i]][1] * fSize, cube_vertices[cube_faces[f][i]][2] * fSize);
glEnd ();
}
glEndList ();
}
glPushMatrix(); // Save world coordinate system.//保存当前矩阵
glTranslatef(0.0, 0.0, 0.5); // Place base of cube on marker surface.往z轴移动0.5
glRotatef(gDrawRotateAngle, 0.0, 0.0, 1.0); // Rotate about z axis.根据参数进行选准
glDisable(GL_LIGHTING); // Just use colours.//不使能光照。
glCallList(polyList); // Draw the cube.
glPopMatrix(); // Restore world coordinate system.//弹出栈顶的矩阵
}
static void DrawCubeUpdate(float timeDelta)//每秒转动45度,按帧频计算每帧转动角度。
{
if (gDrawRotate) {
gDrawRotateAngle += timeDelta * 45.0f; // Rotate cube at 45 degrees per second.
if (gDrawRotateAngle > 360.0f) gDrawRotateAngle -= 360.0f;
}
}
//清除使用过的内存,释放资源。
static void Quit(void)
{
arglCleanup(gArglSettings);
arVideoCapStop();
arVideoClose();
exit(0);
}