bazar  1.3.1
multigl.cpp
Go to the documentation of this file.
1 
8 #include <iostream>
9 #include <vector>
10 #include <cv.h>
11 #include <highgui.h>
12 
13 #ifdef HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16 
17 #include <calib/camera.h>
18 #include "multigrab.h"
19 
20 #ifdef HAVE_APPLE_OPENGL_FRAMEWORK
21 #include <GLUT/glut.h>
22 #else
23 #include <GL/glut.h>
24 #endif
25 
27 
30 int current_cam = 0;
32 bool frameOK=false;
34 bool cacheLight=false;
35 bool dynamicLight=false;
36 bool sphereObject=false;
37 
38 static void photo_start();
39 static void geom_calib_start(bool cache);
40 
42 static void
43 reshape(int width, int height)
44 {
45  GLfloat h = (GLfloat) height / (GLfloat) width;
46 
47  glViewport(0, 0, (GLint) width, (GLint) height);
48  glutPostRedisplay();
49 }
50 
52 static void usage(const char *s) {
53  cerr << "usage:\n" << s
54  << "[-m <model image>] [-r]\n"
55  " -m specifies model image\n"
56  " -r do not load any data\n"
57  " -t train a new classifier\n"
58  " -g recompute geometric calibration\n"
59  " -l rebuild irradiance map from scratch\n";
60  exit(1);
61 }
62 
63 
71 static bool init( int argc, char** argv )
72 {
73  bool redo_geom=false;
74  bool redo_training=false;
75  bool redo_lighting=false;
76 
77  char *modelFile = "model.bmp";
78  // parse command line
79  for (int i=1; i<argc; i++) {
80  if (strcmp(argv[i], "-m") ==0) {
81  if (i==argc-1) usage(argv[0]);
82  modelFile = argv[i+1];
83  i++;
84  } else if (strcmp(argv[i], "-r")==0) {
85  redo_geom=redo_training=redo_lighting=true;
86  } else if (strcmp(argv[i], "-g")==0) {
87  redo_geom=redo_lighting=true;
88  } else if (strcmp(argv[i], "-l")==0) {
89  redo_lighting=true;
90  } else if (strcmp(argv[i], "-t")==0) {
91  redo_training=true;
92  } else if (argv[i][0]=='-') {
93  usage(argv[0]);
94  }
95  }
96 
97  cacheLight = !redo_lighting;
98 
99  multi = new MultiGrab(modelFile);
100 
101  if( multi->init(!redo_training) ==0 )
102  {
103  cerr <<"Initialization error.\n";
104  return false;
105  }
106 
107  geom_calib_start(!redo_geom);
108 
109  return true;
110 }
111 
117 static void keyboard(unsigned char c, int x, int y)
118 {
119  switch (c) {
120  case 'n' :
121  case '+' : if (current_cam < multi->cams.size()-1)
122  current_cam++;
123  break;
124  case 'p':
125  case '-': if (current_cam >= 1)
126  current_cam--;
127  break;
128  case 'q': exit(0); break;
129  case 'd': dynamicLight = !dynamicLight; break;
130  case 'o': sphereObject = !sphereObject; break;
131  case 'f': glutFullScreen(); break;
132  }
133  glutPostRedisplay();
134 }
135 
136 static void emptyWindow() {
137  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
138 }
139 
140 int main(int argc, char *argv[])
141 {
142 
143  glutInit(&argc, argv);
144  glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
145 
146  glutCreateWindow("Multi-Cam Teapot augmentation");
147  glutDisplayFunc(emptyWindow);
148 
149  if (!init(argc,argv)) return -1;
150 
151  cvDestroyAllWindows();
152 
153  glutKeyboardFunc(keyboard);
154  glutMainLoop();
155  return 0; /* ANSI C requires main to return int. */
156 }
157 
159 static bool drawBackground(IplTexture *tex)
160 {
161  if (!tex || !tex->getIm()) return false;
162 
163  IplImage *im = tex->getIm();
164  int w = im->width-1;
165  int h = im->height-1;
166 
167  glMatrixMode(GL_PROJECTION);
168  glLoadIdentity();
169  glMatrixMode(GL_MODELVIEW);
170  glLoadIdentity();
171 
172  glDisable(GL_BLEND);
173  glDisable(GL_DEPTH_TEST);
174 
175  tex->loadTexture();
176 
177  glBegin(GL_QUADS);
178  glColor4f(1,1,1,1);
179 
180  glTexCoord2f(tex->u(0), tex->v(0));
181  glVertex2f(-1, 1);
182 
183  glTexCoord2f(tex->u(w), tex->v(0));
184  glVertex2f(1, 1);
185 
186  glTexCoord2f(tex->u(w), tex->v(h));
187  glVertex2f(1, -1);
188 
189  glTexCoord2f(tex->u(0), tex->v(h));
190  glVertex2f(-1, -1);
191  glEnd();
192 
193  tex->disableTexture();
194 
195  return true;
196 }
197 
205 static void geom_calib_draw(void)
206 {
207  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
208 
209  glDisable(GL_LIGHTING);
210  drawBackground(frameTexture);
211 
212  if (!multi) return;
213 
214  IplImage *im = multi->cams[current_cam]->frame;
215  planar_object_recognizer &detector(multi->cams[current_cam]->detector);
216  if (!im) return;
217 
218  glMatrixMode(GL_PROJECTION);
219  glLoadIdentity();
220  glOrtho(0, im->width-1, im->height-1, 0, -1, 1);
221 
222  glMatrixMode(GL_MODELVIEW);
223  glLoadIdentity();
224 
225  glDisable(GL_BLEND);
226  glDisable(GL_LIGHTING);
227  glDisable(GL_DEPTH_TEST);
228 
229  if (detector.object_is_detected) {
230 
231  glPointSize(2);
232  glBegin(GL_POINTS);
233  glColor4f(0,1,0,1);
234  for (int i=0; i<detector.match_number; ++i) {
235 
236  image_object_point_match * match = detector.matches+i;
237  if (match->inlier) {
238  int s = (int)(match->image_point->scale);
239  float x=PyrImage::convCoordf(match->image_point->u, s, 0);
240  float y=PyrImage::convCoordf(match->image_point->v, s, 0);
241  glVertex2f(x,y);
242  }
243  }
244  glEnd();
245  }
246 
247  glutSwapBuffers();
248 }
249 
253 static void geom_calib_end()
254 {
255 
256  if (!multi->model.augm.LoadOptimalStructureFromFile("camera_c.txt", "camera_r_t.txt"))
257  {
258  cout << "failed to load camera calibration.\n";
259  exit(-1);
260  }
261  glutIdleFunc(0);
262  //glutDisplayFunc(0);
263  delete calib;
264  calib=0;
265 }
266 
272 static void geom_calib_idle(void)
273 {
274  // acquire images
275  multi->grabFrames();
276 
277  // detect the calibration object in every image
278  // (this loop could be paralelized)
279  int nbdet=0;
280  for (int i=0; i<multi->cams.size(); ++i) {
281  if (multi->cams[i]->detect()) nbdet++;
282  }
283 
284  if(!frameTexture) frameTexture = new IplTexture;
285  frameTexture->setImage(multi->cams[current_cam]->frame);
286 
287  if (nbdet>0) {
288  for (int i=0; i<multi->cams.size(); ++i) {
289  if (multi->cams[i]->detector.object_is_detected) {
290  add_detected_homography(i, multi->cams[i]->detector, *calib);
291  } else {
292  calib->AddHomography(i);
293  }
294  }
296  }
297 
298  if (geom_calib_nb_homography>=150) {
299  if (calib->Calibrate(
300  50, // max hom
301  (multi->cams.size() > 1 ? 1:2), // padding or random
302  (multi->cams.size() > 1 ? 0:3),
303  1, // padding ratio 1/2
304  0,
305  0,
306  0.0078125, //alpha
307  0.9, //beta
308  0.001953125,//gamma
309  10, // iter
310  0.05, //eps
311  3 //postfilter eps
312  ))
313  {
315  geom_calib_end();
316  photo_start();
317  return;
318  }
319  }
320 
321  glutPostRedisplay();
322 }
323 
327 static void geom_calib_start(bool cache)
328 {
329  if (cache && multi->model.augm.LoadOptimalStructureFromFile("camera_c.txt", "camera_r_t.txt")) {
330  photo_start();
331  return;
332  }
333 
334  // construct a CamCalibration object and register all the cameras
335  calib = new CamCalibration();
336 
337  for (int i=0; i<multi->cams.size(); ++i) {
338  calib->AddCamera(multi->cams[i]->width, multi->cams[i]->height);
339  }
340 
342  glutDisplayFunc(geom_calib_draw);
343  glutIdleFunc(geom_calib_idle);
344 }
345 
346 //#define DEBUG_SHADER
351 static void photo_draw(void)
352 {
353  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
354 
355  glDisable(GL_LIGHTING);
356  drawBackground(frameTexture);
357 
358  if (!multi) return;
359 
360  IplImage *im = multi->model.image;
361  if (!im) return;
362 
363  if (frameOK) {
364  // Fetch object -> image, world->image and world -> object matrices
365  CvMat *proj = multi->model.augm.GetProjectionMatrix(current_cam);
366  CvMat *world = multi->model.augm.GetObjectToWorld();
367 
368  Mat3x4 moveObject, rot, obj2World, movedRT_;
369  moveObject.setTranslate(im->width/2,im->height/2,-120*3/4);
370  rot.setRotate(Vec3(1,0,0),2*M_PI*180.0/360.0);
371  moveObject.mul(rot);
372  CvMat cvMoveObject = cvMat(3,4,CV_64FC1, moveObject.m);
373  CvMat movedRT=cvMat(3,4,CV_64FC1,movedRT_.m);
374 
375  double a_proj[3][4];
376  for( int i = 0; i < 3; i++ )
377  for( int j = 0; j < 4; j++ ) {
378  a_proj[i][j] = cvmGet( proj, i, j );
379  obj2World.m[i][j] = cvmGet(world, i, j);
380  }
381  cvReleaseMat(&proj);
382  CamCalibration::Mat3x4Mul( world, &cvMoveObject, &movedRT);
383 
384  // translate into OpenGL PROJECTION and MODELVIEW matrices
386  c.loadTdir(a_proj, multi->cams[current_cam]->frame->width, multi->cams[current_cam]->frame->height);
387  c.flip();
388  c.setPlanes(100,1000000);
389  c.setGlProjection();
390  c.setGlModelView();
391 
392  // fill the z-buffer for the calibration target
393  // by drawing a transparent polygon
394  glEnable(GL_DEPTH_TEST);
395  glDisable(GL_LIGHTING);
396 
397  glEnable(GL_BLEND);
398  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
399 
400  glDisable(GL_CULL_FACE);
401  glBegin(GL_QUADS);
402 
403 #ifndef DEBUG_SHADER
404  // transparent back face, for z-buffer only
405  glColor4f(1,1,1,0);
406  glNormal3f(0,0,-1);
407 
408  glVertex3f(multi->model.corners[0].x,multi->model.corners[0].y,0);
409  glVertex3f(multi->model.corners[1].x,multi->model.corners[1].y,0);
410  glVertex3f(multi->model.corners[2].x,multi->model.corners[2].y,0);
411  glVertex3f(multi->model.corners[3].x,multi->model.corners[3].y,0);
412 
413 #else
414  // we want to relight a color present on the model image
415  // with an irradiance coming from the irradiance map
416  CvScalar color = cvGet2D(multi->model.image, multi->model.image->height/2, multi->model.image->width/2);
417  float normal[3] = {
418  obj2World.m[0][2],
419  obj2World.m[1][2],
420  obj2World.m[2][2]};
421 
422  CvScalar irradiance = multi->model.map.readMap(normal);
423 
424  // the camera has some gain and bias
425  const float *g = multi->model.map.getGain(current_cam);
426  const float *b = multi->model.map.getBias(current_cam);
427 
428  // relight the 3 RGB channels. The bias value expects 0 black 1 white,
429  // but the image are stored with a white value of 255: Conversion is required.
430  for (int i=0; i<3; i++) {
431  color.val[i] = (g[i]*(color.val[i]/255.0)*irradiance.val[i] + b[i]);
432  }
433  glColor3d(color.val[2], color.val[1], color.val[0]);
434 
435  float half[2] = {
436  (multi->model.corners[0].x + multi->model.corners[1].x)/2,
437  (multi->model.corners[2].x + multi->model.corners[3].x)/2,
438  };
439  glNormal3f(0,0,1);
440  glVertex3f(multi->model.corners[0].x,multi->model.corners[0].y,0);
441  glVertex3f(half[0],multi->model.corners[1].y,0);
442  glVertex3f(half[1],multi->model.corners[2].y,0);
443  glVertex3f(multi->model.corners[3].x,multi->model.corners[3].y,0);
444 #endif
445  glEnd();
446 
447 #ifndef DEBUG_SHADER
448  // apply the object transformation matrix
449  Mat3x4 w2e(c.getWorldToEyeMat());
450  w2e.mul(moveObject);
451  c.setWorldToEyeMat(w2e);
452  c.setGlModelView();
453 #endif
454 
455  if (multi->model.map.isReady()) {
456  glDisable(GL_LIGHTING);
457 #ifdef DEBUG_SHADER
458  multi->model.map.enableShader(current_cam, world);
459 #else
460  multi->model.map.enableShader(current_cam, &movedRT);
461 #endif
462  } else {
463  GLfloat light_diffuse[] = {1.0, 1, 1, 1.0};
464  GLfloat light_position[] = {500, 400.0, 500.0, 1};
465  Mat3x4 w2obj;
466  w2obj.setInverseByTranspose(obj2World);
467  w2obj.transform(light_position, light_position);
468  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
469  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
470  glEnable(GL_LIGHT0);
471  glEnable(GL_LIGHTING);
472  }
473 
474  cvReleaseMat(&world);
475  {
476  CvScalar c =cvGet2D(multi->model.image,
477  multi->model.image->height/2,
478  multi->model.image->width/2);
479  glColor3d(c.val[2], c.val[1], c.val[0]);
480 #ifndef DEBUG_SHADER
481  glEnable(GL_CULL_FACE);
482  if (sphereObject) {
483  glCullFace(GL_BACK);
484  glutSolidSphere(120, 16, 16);
485  } else {
486  glCullFace(GL_FRONT);
487  // this rotation is the inverse of the rotation
488  // that glutSolidTeapot() does itself. It is necessary
489  // to maintain modelView matrix consistant with the normal
490  // transformation matrix.
491  // scale and translation do not mess with normals: no need to worry.
492  glRotatef(-270, 1.0, 0.0, 0.0);
493  glutSolidTeapot(120);
494  }
495 #else
496  glDisable(GL_CULL_FACE);
497  glBegin(GL_QUADS);
498  glNormal3f(0,0,-1);
499  glVertex3f(half[0],multi->model.corners[0].y,0);
500  glVertex3f(multi->model.corners[1].x,multi->model.corners[1].y,0);
501  glVertex3f(multi->model.corners[2].x,multi->model.corners[2].y,0);
502  glVertex3f(half[1],multi->model.corners[3].y,0);
503  glEnd();
504  glutSolidSphere(120, 16, 16);
505 #endif
506  }
507  if (multi->model.map.isReady())
508  multi->model.map.disableShader();
509  else
510  glDisable(GL_LIGHTING);
511  }
512  if (multi->model.map.isReady()) {
513  multi->model.map.map.loadTexture();
514  glDisable(GL_DEPTH_TEST);
515  glDisable(GL_CULL_FACE);
516 
517  glMatrixMode(GL_PROJECTION);
518  glLoadIdentity();
519  glMatrixMode(GL_MODELVIEW);
520  glLoadIdentity();
521  glBegin(GL_QUADS);
522  {
523  glColor4f(1,1,1,1);
524  glTexCoord2d(0,0);
525  glVertex2d(.5, 1);
526  glTexCoord2d(1,0);
527  glVertex2d(1,1);
528  glTexCoord2d(1,1);
529  glVertex2d(1,.8);
530  glTexCoord2d(0,1);
531  glVertex2d(.5,.8);
532  }
533  glEnd();
534  glEnable(GL_DEPTH_TEST);
535  multi->model.map.map.disableTexture();
536  }
537 
538 
539  glutSwapBuffers();
540 }
541 
548 static void photo_idle()
549 {
550  // acquire images
551  multi->grabFrames();
552 
553  // detect the calibration object in every image
554  // (this loop could be paralelized)
555  int nbdet=0;
556  for (int i=0; i<multi->cams.size(); ++i) {
557  if (multi->cams[i]->detect()) nbdet++;
558  }
559 
560  if(!frameTexture) frameTexture = new IplTexture;
561  frameTexture->setImage(multi->cams[current_cam]->frame);
562 
563  frameOK=false;
564  if (nbdet>0) {
565  multi->model.augm.Clear();
566  for (int i=0; i<multi->cams.size(); ++i) {
567  if (multi->cams[i]->detector.object_is_detected) {
568  add_detected_homography(i, multi->cams[i]->detector, multi->model.augm);
569  } else {
570  multi->model.augm.AddHomography();
571  }
572  }
573  frameOK = multi->model.augm.Accomodate(4, 1e-4);
574  }
575 
576  if (frameOK) {
577  // fetch surface normal in world coordinates
578  CvMat *mat = multi->model.augm.GetObjectToWorld();
579  float normal[3];
580  for (int j=0;j<3;j++) normal[j] = cvGet2D(mat, j, 2).val[0];
581  cvReleaseMat(&mat);
582 
583  // During photometric calibration phase or
584  // for light update...
585  if (!multi->model.map.isReady() || dynamicLight) {
586  for (int i=0; i<multi->cams.size();++i) {
587  // ..collect lighting measures
588  if (multi->cams[i]->detector.object_is_detected) {
589  nbLightMeasures++;
590  multi->model.map.addNormal(normal, *multi->cams[i]->lc, i);
591  }
592  }
593  }
594 
595  // when required, compute all the lighting parameters
596  if (!multi->model.map.isReady() && multi->model.map.nbNormals() > 80) {
597  if (multi->model.map.computeLightParams()) {
598  multi->model.map.save();
599  }
600  }
601  }
602  glutPostRedisplay();
603 }
604 
606 static void photo_start()
607 {
608  // allocate light collectors
609  multi->allocLightCollector();
610 
611  if (cacheLight) multi->model.map.load();
612 
613  nbLightMeasures=0;
614  glutIdleFunc(photo_idle);
615  glutDisplayFunc(photo_draw);
616 }
617 
618