2 * Credits: Julian Oliver, 2008-2009 <julian@julianoliver.com>.
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation, either version 3 of the License, or (at your option)
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
17 * This code builds upon BazAR, in particular 'multigl.cpp'. It has been
18 * modified to support texture and video-texture mapping to an OpenGL plane over
19 * the ROI. The ROI in the model image is now read in from a file generated by
20 * the training process. Pose estimation stabilisation, augmentation fades,
21 * fonts, mouse input hooks, augmentation over archival video and other bits have
22 * pieces have been added also.
24 * I've fixed a bug in BazAR's planar_object_recognizer::build_with_cache where
25 * corner values for the ROI were only being set immediately after training, not
30 * There are four ways to use Artvertiser.
32 * With video substitution of the ROI:
34 * ./artvertiser -m <model file> -a <avi file>
36 * With video substitution of the ROI and capture from an AVI file
38 * ./artvertiser -m <model file> -a <avi file> -b <avi file>
40 * With image substitution of the ROI and capture from a v4l device
42 * ./artvertiser -m <model file>
44 * See defines below for setting capture window size and V4L device index
49 #include <sstream> // for conv int->str
63 #include <calib/camera.h>
64 #include "multigrab.h"
66 #ifdef HAVE_APPLE_OPENGL_FRAMEWORK
67 #include <GLUT/glut.h>
72 #include "/usr/include/freetype2/freetype/config/ftconfig.h"
73 #include <FTGL/ftgl.h>
75 #include "FProfiler/FProfiler.h"
78 #include "framerate.h"
81 #define IsRGB(s) ((s[0] == 'R') && (s[1] == 'G') && (s[2] == 'B'))
82 #define IsBGR(s) ((s[0] == 'B') && (s[1] == 'G') && (s[2] == 'R'))
84 #ifndef GL_CLAMP_TO_BORDER
85 #define GL_CLAMP_TO_BORDER 0x812D
87 #define GL_MIRROR_CLAMP_EXT 0x8742
99 CamCalibration
*calib
=0;
101 IplTexture
*frameTexture
=0;
104 CvCapture
*capture
= 0;
105 CvCapture
*avi_capture
= 0;
106 IplImage
*avi_image
= 0;
107 IplImage
*avi_frame
= 0;
108 IplImage
*model_image
= 0;
109 IplImage
*this_frame
= 0;
110 IplImage
*last_frame
= 0;
112 IplImage
*bit_frame
= 0;
114 int v4l_device
= V4LDEVICE
;
115 int video_width
= WIDTH
;
116 int video_height
= HEIGHT
;
117 int detect_width
= WIDTH
;
118 int detect_height
= HEIGHT
;
120 // load some images. hard-coded for know until i get the path parsing together.
121 IplImage
*image1
= cvLoadImage("artvert1.png");
122 IplImage
*image2
= cvLoadImage("artvert2.png");
123 IplImage
*image3
= cvLoadImage("artvert3.png");
124 IplImage
*image4
= cvLoadImage("artvert4.png");
125 IplImage
*image5
= cvLoadImage("artvert5.png");
127 // define a container struct for each artvert
128 struct artvert_struct
138 typedef vector
<artvert_struct
> artverts_list
;
139 artverts_list
artverts(5);
141 // create a vector for the images and initialise it.
142 typedef vector
<IplImage
> imgVec
;
145 bool cache_light
=false;
146 bool dynamic_light
=false;
147 bool sphere_object
=false;
149 bool avi_play_init
=false;
150 bool lbutton_down
= false;
154 double old_a_proj
[3][4];
156 float fade_speed
= 2;
159 int nb_light_measures
=0;
160 int geom_calib_nb_homography
;
168 CvPoint2D32f
*c1
= new CvPoint2D32f
[4];
171 char *model_file
= "model.bmp";
174 static void geomCalibStart(bool cache
);
176 // initialise a couple of fonts.
177 CvFont font
, fontbold
;
179 // Gl format for texturing
184 static FTFont
*ftglFont
;
187 bool show_status
= false;
188 bool show_profile_results
= false;
189 string status_string
= "";
191 /* use this to read paths from the file system
193 string getExtension(const string &file)
195 string::size_type dot = file.rfind('.');
196 string lcpath = file;
198 transform(lcpath.begin(), lcpath.end(), lcpath.begin(), tolower);
199 if(dot != string::npos)
201 suffix = lcpath.substr(dot + 1);
207 std::string
date(int now
)
213 timeinfo
= localtime ( &rawtime
);
214 strftime (tBuffer
,80,"%I:%M:%S:%p, %d %b %Y",timeinfo
);
216 stringstream _timeStr
;
218 timeStr
= _timeStr
.str();
223 // mouse input using GLUT.
224 void entry(int state
)
226 if (state
== GLUT_ENTERED
)
227 cout
<< "Mouse Entered" << endl
;
229 cout
<< "Mouse Left" << endl
;
232 void mouse(int button
, int state
, int x
, int y
)
234 if (button
== GLUT_RIGHT_BUTTON
)
236 if (state
== GLUT_DOWN
)
246 else if (button
== GLUT_LEFT_BUTTON
)
248 if (state
== GLUT_DOWN
)
251 if (cnt
>= NUMARTVERTS
-1)
261 lbutton_down
= false;
263 cout
<< "we are on image " << cnt
<< endl
;
266 // text drawing function
267 static void drawText(IplImage
*img
, const char *text
, CvPoint point
, CvFont
*font
, CvScalar colour
, double size
)
269 cvInitFont( font
, CV_FONT_HERSHEY_DUPLEX
, size
, size
, 0, 1, CV_AA
);
270 //cvInitFont( font, CV_FONT_HERSHEY_PLAIN, size, size, 0, 1, CV_AA);
271 cvPutText(img
, text
, point
, font
, colour
);
274 // read in ROI coords from txt file into vector.
275 static vector
<int> readROI(const char *filename
)
277 cout
<< filename
<< endl
;
279 ifstream
roi(filename
);
291 strcpy(s
, l
.c_str());
296 //roi_vec.push_back(atoi(s1));
297 v
.push_back(atoi(s1
));
298 s1
= strtok(NULL
, " ,");
305 cout
<< "roi file not found" << endl
;
310 //! GLUT callback on window size change
311 static void reshape(int width
, int height
)
313 //GLfloat h = (GLfloat) height / (GLfloat) width;
314 int winWidth
= video_width
;
315 int winHeight
= video_height
;
316 glViewport(0,0,winWidth
, winHeight
);
320 //! Print a command line help and exit.
321 static void usage(const char *s
)
323 cerr
<< "usage:\n" << s
324 << "[-m <model image>] [-r]\n"
325 //" -a <path> specify path to AVI (instead of v4l device)\n"
326 " -b <path> specify path to AVI (instead of v4l device) MUST ALSO SPECIFY -vs"
327 " -m specifies model image\n"
328 " -r do not load any data\n"
329 " -t train a new classifier\n"
330 " -g recompute geometric calibration\n"
331 " -a <path> load an AVI movie as an artvert\n"
332 " -l rebuild irradiance map from scratch\n"
333 " -vd <num> V4L video device number (0-n)\n"
334 " -vs <width> <height> video width and height (default 640x480)\n"
335 " -ds <width> <height> frame size at which to run the detector (default to video width/height)\n";
343 printf("in exit_handler\n");
344 // shutdown the capture
350 /*!\brief Initialize everything
352 * - Parse the command line
353 * - Initialize all the cameras
354 * - Load or interactively build a model, with its classifier.
355 * - Set the GLUT callbacks for geometric calibration or, if already done, for photometric calibration.
358 static bool init( int argc
, char** argv
)
360 // register exit function
361 atexit( &exit_handler
);
363 // more from before init should be moved here
364 bool redo_geom
=false;
365 bool redo_training
=false;
366 bool redo_lighting
=false;
367 char *avi_bg_path
="";
369 bool video_source_is_avi
= false;
371 // parse command line
372 for (int i
=1; i
<argc
; i
++)
374 if (strcmp(argv
[i
], "-m") ==0)
376 if (i
==argc
-1) usage(argv
[0]);
377 model_file
= argv
[i
+1];
380 else if (strcmp(argv
[i
], "-r")==0)
382 redo_geom
=redo_training
=redo_lighting
=true;
384 else if (strcmp(argv
[i
], "-g")==0)
388 else if (strcmp(argv
[i
], "-l")==0)
392 else if (strcmp(argv
[i
], "-t")==0)
396 else if (strcmp(argv
[i
], "-a")==0)
398 avi_capture
=cvCaptureFromAVI(argv
[i
+1]);
401 else if (strcmp(argv
[i
], "-b")==0)
403 video_source_is_avi
= true;
404 avi_bg_path
=argv
[i
+1];
405 printf(" -b: loading from avi '%s'\n", avi_bg_path
);
407 else if (strcmp(argv
[i
], "-i")==0)
409 image_path
=argv
[i
+1];
411 else if (strcmp(argv
[i
], "-vd")==0)
413 v4l_device
=atoi(argv
[i
+1]);
414 printf(" -vd: using v4l device %i\n", v4l_device
);
417 else if (strcmp(argv
[i
], "-vs")==0)
419 video_width
=atoi(argv
[i
+1]);
420 video_height
=atoi(argv
[i
+2]);
423 // also set detect size (if not already set)
424 detect_width
= video_width
;
425 detect_height
= video_height
;
427 printf(" -vs: video size is %ix%i\n", video_width
, video_height
);
428 if ( video_width
== 0 || video_height
== 0 )
435 else if ( strcmp(argv
[i
], "-ds")==0 )
437 detect_width
= atoi(argv
[i
+1]);
438 detect_height
= atoi(argv
[i
+2]);
439 printf(" -ds: detection frame size is %ix%i\n", detect_width
, detect_height
);
440 if ( detect_width
== 0 || detect_height
== 0 )
448 else if (argv
[i
][0]=='-')
455 // check for video size arg if necessary
456 if ( video_source_is_avi
)
458 // try to read from video
459 CvCapture
* temp_cap
= cvCaptureFromAVI(avi_bg_path
);
460 video_width
= (int)cvGetCaptureProperty( temp_cap
, CV_CAP_PROP_FRAME_WIDTH
);
461 video_height
= (int)cvGetCaptureProperty( temp_cap
, CV_CAP_PROP_FRAME_HEIGHT
);
462 printf(" -b: read video width/height %i/%i from avi (ignoring -vs)\n", video_width
, video_height
);
465 detect_width
= video_width
;
466 detect_height
= video_height
;
468 cvReleaseCapture(&temp_cap
);
471 //cout << avi_bg_path << endl;
472 cache_light
= !redo_lighting
;
474 multi
= new MultiGrab(model_file
);
476 if( multi
->init(!redo_training
, model_file
, avi_bg_path
, video_width
, video_height
, v4l_device
, detect_width
, detect_height
) ==0)
478 cerr
<<"Initialization error.\n";
482 geomCalibStart(!redo_geom
);
484 artvert_struct artvert1
= {"Arrebato, 1980", image1
, "Feb, 2009", "Iván Zulueta", "Polo", "Madrid, Spain"};
485 artvert_struct artvert2
= {"name2", image2
, "2008", "simon innings", "Helmut Lang", "Parlance Avenue"};
486 artvert_struct artvert3
= {"name3", image3
, "2008", "simon innings", "Loreal", "Parlance Avenue"};
487 artvert_struct artvert4
= {"name4", image4
, "2008", "simon innings", "Hugo Boss", "Parlance Avenue"};
488 artvert_struct artvert5
= {"name5", image5
, "2008", "simon innings", "Burger King", "Parlance Avenue"};
490 artverts
[0] = artvert1
;
491 artverts
[1] = artvert2
;
492 artverts
[2] = artvert3
;
493 artverts
[3] = artvert4
;
494 artverts
[4] = artvert5
;
496 // copy char model_file before munging with strcat
497 char s
[strlen(model_file
)];
498 strcpy (s
, model_file
);
500 roi_vec
= readROI(s
);
502 // load model_image for use with diffing, later
503 model_image
= cvLoadImage(model_file
);
505 c1
[0].x
= roi_vec
[0];
506 c1
[0].y
= roi_vec
[1];
507 c1
[1].x
= roi_vec
[2];
508 c1
[1].y
= roi_vec
[3];
509 c1
[2].x
= roi_vec
[4];
510 c1
[2].y
= roi_vec
[5];
511 c1
[3].x
= roi_vec
[6];
512 c1
[3].y
= roi_vec
[7];
517 /*! The keyboard callback: reacts to '+' and '-' to change the viewed cam, 'q' exits.
518 * 'd' turns on/off the dynamic lightmap update.
519 * 'f' goes fullscreen.
521 static void keyboard(unsigned char c
, int x
, int y
)
531 if (current_cam
< multi
->cams
.size()-1)
535 if (current_cam
>= 1)
543 dynamic_light
= !dynamic_light
;
546 if (avi_play
== true)
554 show_status
= !show_status
;
557 show_profile_results
= true;
563 if (cnt
>= NUMARTVERTS
-1)
567 cout
<< "we are on image " << cnt
<< endl
;
573 if ( multi
&& show_status
)
575 planar_object_recognizer
&detector(multi
->cams
[current_cam
]->detector
);
580 detector
.ransac_dist_threshold
*=1.02f
;
583 detector
.ransac_dist_threshold
/=1.02f
;
586 detector
.ransac_stop_support
++;
589 detector
.ransac_stop_support
--;
592 detector
.max_ransac_iterations
+=10;
595 detector
.max_ransac_iterations
-=10;
598 detector
.non_linear_refine_threshold
*=1.02f
;
601 detector
.non_linear_refine_threshold
/=1.02f
;
604 detector
.match_score_threshold
*=1.02f
;
607 detector
.match_score_threshold
/=1.02f
;
610 detector
.min_view_rate
*=1.02f
;
613 detector
.min_view_rate
*=1.02f
;
616 detector
.point_detector_tau
++;
619 detector
.point_detector_tau
--;
628 static void emptyWindow()
630 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
633 int main(int argc
, char *argv
[])
635 glutInit(&argc
, argv
);
636 glutInitDisplayMode(GLUT_RGB
| GLUT_DEPTH
| GLUT_DOUBLE
);
637 glutInitWindowSize(video_width
,video_height
); // hard set the init window size
638 //glutInitWindowSize(800,450); // hard set the init window size
641 glutDisplayFunc(emptyWindow
);
642 glutReshapeFunc(reshape
);
643 glutCreateWindow("The Artvertiser 0.4");
644 glutMouseFunc(mouse
);
645 glutEntryFunc(entry
);
647 if (!init(argc
,argv
)) return -1;
650 //ftglFont = new FTBufferFont("/usr/share/fonts/truetype/freefont/FreeMono.ttf");
651 ftglFont
= new FTBufferFont("fonts/FreeSans.ttf");
652 ftglFont
->FaceSize(12);
653 ftglFont
->CharMap(ft_encoding_unicode
);
655 cvDestroyAllWindows();
657 glutKeyboardFunc(keyboard
);
659 return 0; /* ANSI C requires main to return int. */
662 //!\brief Draw a frame contained in an IplTexture object on an OpenGL viewport.
663 static bool drawBackground(IplTexture
*tex
)
665 if (!tex
|| !tex
->getIm()) return false;
667 IplImage
*im
= tex
->getIm();
669 int h
= im
->height
-1;
671 glMatrixMode(GL_PROJECTION
);
673 glMatrixMode(GL_MODELVIEW
);
677 glDisable(GL_DEPTH_TEST
);
684 glTexCoord2f(tex
->u(0), tex
->v(0));
686 glTexCoord2f(tex
->u(w
), tex
->v(0));
688 glTexCoord2f(tex
->u(w
), tex
->v(h
));
690 glTexCoord2f(tex
->u(0), tex
->v(h
));
694 tex
->disableTexture();
699 /*! \brief A draw callback during camera calibration
701 * GLUT calls that function during camera calibration when repainting the
702 * window is required.
703 * During geometric calibration, no 3D is known: we just plot 2d points
704 * where some feature points have been recognized.
706 static void geomCalibDraw(void)
708 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
710 glDisable(GL_LIGHTING
);
711 drawBackground(frameTexture
);
715 IplImage
*im
= multi
->cams
[current_cam
]->frame
;
716 planar_object_recognizer
&detector(multi
->cams
[current_cam
]->detector
);
719 glMatrixMode(GL_PROJECTION
);
721 glOrtho(0, im
->width
-1, im
->height
-1, 0, -1, 1);
723 glMatrixMode(GL_MODELVIEW
);
727 glDisable(GL_LIGHTING
);
728 glDisable(GL_DEPTH_TEST
);
730 if (detector
.object_is_detected
)
735 for (int i
=0; i
<detector
.match_number
; ++i
)
737 image_object_point_match
* match
= detector
.matches
+i
;
740 int s
= (int)(match
->image_point
->scale
);
741 float x
=PyrImage::convCoordf(match
->image_point
->u
, s
, 0);
742 float y
=PyrImage::convCoordf(match
->image_point
->v
, s
, 0);
752 /*!\brief Called when geometric calibration ends. It makes
753 * sure that the CamAugmentation object is ready to work.
755 static void geomCalibEnd()
758 if (!multi
->model
.augm
.LoadOptimalStructureFromFile("camera_c.txt", "camera_r_t.txt"))
760 cout
<< "failed to load camera calibration.\n";
764 //glutDisplayFunc(0);
769 /*! Called by GLUT during geometric calibration when there's nothing else to do.
770 * This function grab frames from camera(s), run the 2D detection on every image,
771 * and keep the result in memory for calibration. When enough homographies have
772 * been detected, it tries to actually calibrate the cameras.
774 static void geomCalibIdle(void)
779 // detect the calibration object in every image
780 // (this loop could be paralelized)
782 for (int i
=0; i
<multi
->cams
.size(); ++i
)
784 if (multi
->cams
[i
]->detect()) nbdet
++;
787 if(!frameTexture
) frameTexture
= new IplTexture
;
788 frameTexture
->setImage(multi
->cams
[current_cam
]->frame
);
792 for (int i
=0; i
<multi
->cams
.size(); ++i
)
794 if (multi
->cams
[i
]->detector
.object_is_detected
)
796 add_detected_homography(i
, multi
->cams
[i
]->detector
, *calib
);
800 calib
->AddHomography(i
);
803 geom_calib_nb_homography
++;
806 if (geom_calib_nb_homography
>=150)
808 if (calib
->Calibrate(
810 (multi
->cams
.size() > 1 ? 1:2), // padding or random
811 (multi
->cams
.size() > 1 ? 0:3),
812 1, // padding ratio 1/2
823 calib
->PrintOptimizedResultsToFile1();
832 /*!\brief Start geometric calibration. If the calibration can be loaded from disk,
833 * continue directly with photometric calibration.
835 static void geomCalibStart(bool cache
)
837 if (cache
&& multi
->model
.augm
.LoadOptimalStructureFromFile("camera_c.txt", "camera_r_t.txt"))
843 // construct a CamCalibration object and register all the cameras
844 calib
= new CamCalibration();
846 for (int i
=0; i
<multi
->cams
.size(); ++i
)
848 calib
->AddCamera(multi
->cams
[i
]->width
, multi
->cams
[i
]->height
);
851 geom_calib_nb_homography
=0;
852 glutDisplayFunc(geomCalibDraw
);
853 glutIdleFunc(geomCalibIdle
);
856 //#define DEBUG_SHADER
857 /*! The paint callback during photometric calibration and augmentation. In this
858 * case, we have access to 3D data. Thus, we can augment the calibration target
861 static void draw(void)
864 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
865 glDisable(GL_LIGHTING
);
867 drawBackground(frameTexture
);
870 stringstream cnt_out
;
872 cnt_str
= cnt_out
.str();
874 //IplImage *pre_mask = cvCreateImage(cvSize(WIDTH, HEIGHT), 8, 3);
878 IplImage
*im
= multi
->model
.image
;
882 int now
= glutGet(GLUT_ELAPSED_TIME
);
884 cout << now/1000.0 << endl;
888 if (frame_ok
and augment
== 1)
893 // here i calculate pixel shift to help us remove pose estimation jitter
894 IplImage
*_frame
= frameTexture
->getIm();
898 this_frame
=cvCreateImage( cvSize(video_width
, video_height
),8,1);
899 bit_frame
=cvCreateImage(cvSize(video_width
, video_height
),8,1);
900 last_frame
=cvCloneImage(this_frame
);
901 diff
=cvCreateImage(cvSize(video_width
, video_height
), 8 ,1);
905 cvCvtColor(_frame
, this_frame
,CV_BGR2GRAY
);
906 cvAbsDiff(this_frame
,last_frame
,diff
);
908 cvThreshold(diff
,bit_frame
,30,255,CV_THRESH_BINARY
);
909 last_frame
=cvCloneImage(this_frame
);
910 // calculate white pixels (pixel shift)
911 int pixel_shift
= cvCountNonZero(bit_frame
);
912 //cvSaveImage("diff.png", bit_frame);
913 //cout << pixel_shift << endl;
915 // Fetch object -> image, world->image and world -> object matrices
916 CvMat
*proj
= multi
->model
.augm
.GetProjectionMatrix(current_cam
);
917 CvMat
*old_proj
= multi
->model
.augm
.GetProjectionMatrix(current_cam
);
918 CvMat
*world
= multi
->model
.augm
.GetObjectToWorld();
919 Mat3x4 moveObject
, rot
, obj2World
, movedRT_
;
920 moveObject
.setTranslate(im
->width
/2,im
->height
/2,-120*3/4);
921 rot
.setRotate(Vec3(1,0,0),2*M_PI
*180.0/360.0);
923 CvMat cvMoveObject
= cvMat(3,4,CV_64FC1
, moveObject
.m
);
924 CvMat movedRT
=cvMat(3,4,CV_64FC1
,movedRT_
.m
);
926 // pose only during movement
927 if (pixel_shift
>= 200 || !have_proj
)
929 // memcpy or vectorisation speedup?
930 for( int i
= 0; i
< 3; i
++ )
932 for( int j
= 0; j
< 4; j
++ )
934 a_proj
[i
][j
] = cvmGet( proj
, i
, j
);
935 obj2World
.m
[i
][j
] = cvmGet(world
, i
, j
);
939 memcpy(old_a_proj
, a_proj
, sizeof(a_proj
));
941 else // copy last known good projection over current
943 memcpy(a_proj
, old_a_proj
, sizeof(old_a_proj
));
946 CamCalibration::Mat3x4Mul( world
, &cvMoveObject
, &movedRT
);
947 // translate into OpenGL PROJECTION and MODELVIEW matrices
949 //c.loadTdir(a_proj, multi->cams[0]->frame->width, multi->cams[0]->frame->height);
950 c
.loadTdir(a_proj
, multi
->cams
[0]->detect_width
, multi
->cams
[0]->detect_height
);
952 c
.setPlanes(100,1000000); // near/far clip planes
955 // must set the model view after drawing the background.
959 /*! this is the beginning of prototype code for an occlusion mask built
960 * by comparison of the tracked plane with that of the model image
962 // create a copy of frame texture and warp to model image perspective
963 CvPoint2D32f *c2 = new CvPoint2D32f[4];
964 // update corner points of ROI in pixel space
965 c2[0].x = cvRound(multi->cams[0]->detector.detected_u_corner1);
966 c2[0].y = cvRound(multi->cams[0]->detector.detected_v_corner1);
967 c2[1].x = cvRound(multi->cams[0]->detector.detected_u_corner2);
968 c2[1].y = cvRound(multi->cams[0]->detector.detected_v_corner2);
969 c2[2].x = cvRound(multi->cams[0]->detector.detected_u_corner3);
970 c2[2].y = cvRound(multi->cams[0]->detector.detected_v_corner3);
971 c2[3].x = cvRound(multi->cams[0]->detector.detected_u_corner4);
972 c2[3].y = cvRound(multi->cams[0]->detector.detected_v_corner4);
974 CvMat* mmat = cvCreateMat(3,3, CV_32FC1);
975 IplImage *warped = cvCreateImage(cvSize(model_image->width, model_image->height), 8, 3);
976 mmat = cvGetPerspectiveTransform(c2, c1, mmat);
977 cvWarpPerspective(frameTexture->getIm(), warped, mmat);
980 // find difference between model image and frame
981 IplImage *i1=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
982 IplImage *i2=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
983 IplImage *diff=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
984 IplImage *display=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
986 cvCvtColor(im, i1,CV_BGR2GRAY);
987 cvCvtColor(warped, i2,CV_BGR2GRAY);
988 cvAbsDiff(i2,i1,diff);
989 cvThreshold(diff, display, 35, 255, CV_THRESH_BINARY);
991 cvReleaseImage(&warped);
992 cvSaveImage("checkdiff.png", display);
995 /* circles at corners of ROI. useful for debugging.
996 cvCircle(frameTexture->getIm(), c1, 10, CV_RGB(255, 0, 0), 2);
997 cvCircle(frameTexture->getIm(), c2, 10, CV_RGB(255, 0, 0), 2);
998 cvCircle(frameTexture->getIm(), c3, 10, CV_RGB(255, 0, 0), 2);
999 cvCircle(frameTexture->getIm(), c4, 10, CV_RGB(255, 0, 0), 2);
1001 tex = new IplTexture;
1002 tex->setImage(frameTexture->getIm());
1003 drawBackground(tex);
1008 #ifndef DEBUG_SHADER
1010 glEnable(GL_DEPTH_TEST
);
1012 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1013 glDisable(GL_CULL_FACE
);
1014 // multiply texture colour by surface colour of poly
1015 glTexEnvf(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
1016 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_NICEST
);
1018 if (avi_play
== true)
1020 IplImage
*avi_frame
= 0;
1021 IplImage
*avi_image
= 0;
1022 avi_frame
= cvQueryFrame( avi_capture
);
1023 avi_image
= cvCreateImage(cvSize(video_width
/2, video_height
/2), 8, 3);
1024 cvResize(avi_frame
, avi_image
, 0);
1025 avi_image
->origin
= avi_frame
->origin
;
1026 GLenum format
= IsBGR(avi_image
->channelSeq
) ? GL_BGR_EXT
: GL_RGBA
;
1030 glGenTextures(1, &imageID
);
1031 glBindTexture(GL_TEXTURE_2D
, imageID
);
1032 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
1033 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
1034 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
1035 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, avi_image
->width
, avi_image
->height
, 0, format
, GL_UNSIGNED_BYTE
, avi_image
->imageData
);
1040 glBindTexture(GL_TEXTURE_2D
, imageID
);
1041 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, avi_image
->width
, avi_image
->height
, format
, GL_UNSIGNED_BYTE
, avi_image
->imageData
);
1046 glBindTexture(GL_TEXTURE_2D
, imageID
);
1047 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
1048 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
1049 IplImage image
= *artverts
[cnt
].image
;
1050 GLenum format
= IsBGR(image
.channelSeq
) ? GL_BGR_EXT
: GL_RGBA
;
1051 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, image
.width
, image
.height
, 0, format
, GL_UNSIGNED_BYTE
, image
.imageData
);
1054 glEnable(GL_TEXTURE_2D
);
1056 glHint(GL_POLYGON_SMOOTH
, GL_NICEST
);
1057 glEnable(GL_POLYGON_SMOOTH
);
1059 // create a simple fade-in when we have a track
1062 fade
+= 0.1 * fade_speed
;
1069 glColor4f(1.0, 1.0, 1.0, fade
);
1073 glVertex3f(roi_vec
[0], roi_vec
[1], 0);
1075 glVertex3f(roi_vec
[2], roi_vec
[3], 0);
1077 glVertex3f(roi_vec
[4], roi_vec
[5], 0);
1079 glVertex3f(roi_vec
[6], roi_vec
[7], 0);
1082 glDisable(GL_TEXTURE_2D
);
1084 /*! 'label' is a boolean set by the right mouse button and toggles the
1085 * in-scene artvert label.
1090 glBegin(GL_LINE_LOOP
);
1091 glColor3f(0.0, 1.0, 0.0);
1092 glVertex3f(roi_vec
[0]-10, roi_vec
[1]-10, 0);
1093 glVertex3f(roi_vec
[2]+10, roi_vec
[3]-10, 0);
1094 glVertex3f(roi_vec
[4]+10, roi_vec
[5]+10, 0);
1095 glVertex3f(roi_vec
[6]-10, roi_vec
[7]+10, 0);
1096 glVertex3f(roi_vec
[0]-10, roi_vec
[1]-10, 0);
1099 glTranslatef(roi_vec
[2]+12, roi_vec
[3], 0);
1100 glRotatef(180, 1.0, 0.0, 0.0);
1101 glRotatef(-45, 0.0, 1.0, 0.0);
1102 glColor4f(0.0, 1.0, 0.0, 1);
1104 glBegin(GL_LINE_LOOP
);
1105 glVertex3f(0, 10, -.2);
1106 glVertex3f(150, 10, -.2);
1107 glVertex3f(150, -60, -.2);
1108 glVertex3f(0, -60, -.2);
1111 glColor4f(0.0, 1.0, 0.0, .5);
1114 glVertex3f(0, 10, -.2);
1115 glVertex3f(150, 10, -.2);
1116 glVertex3f(150, -60, -.2);
1117 glVertex3f(0, -60, -.2);
1120 // render the text in the label
1121 glColor4f(1.0, 1.0, 1.0, 1);
1122 ftglFont
->Render(artverts
[cnt
].artvert
);
1123 glTranslatef(0, -12, 0);
1124 ftglFont
->Render(artverts
[cnt
].date
);
1125 glTranslatef(0, -12, 0);
1126 ftglFont
->Render(artverts
[cnt
].author
);
1127 glTranslatef(0, -12, 0);
1128 ftglFont
->Render(artverts
[cnt
].advert
);
1129 glTranslatef(0, -12, 0);
1130 ftglFont
->Render(artverts
[cnt
].street
);
1137 #ifndef DEBUG_SHADER
1138 // apply the object transformation matrix
1139 Mat3x4
w2e(c
.getWorldToEyeMat());
1140 w2e
.mul(moveObject
);
1141 c
.setWorldToEyeMat(w2e
);
1145 if (multi
->model
.map
.isReady())
1147 glDisable(GL_LIGHTING
);
1149 multi
->model
.map
.enableShader(current_cam
, world
);
1151 multi
->model
.map
.enableShader(current_cam
, &movedRT
);
1154 cvReleaseMat(&world
);
1156 CvScalar c
=cvGet2D(multi
->model
.image
, multi
->model
.image
->height
/2, multi
->model
.image
->width
/2);
1157 glColor3d(c
.val
[2], c
.val
[1], c
.val
[0]);
1159 if (multi
->model
.map
.isReady())
1160 multi
->model
.map
.disableShader();
1162 glDisable(GL_LIGHTING
);
1164 if ( avi_play
== true )
1166 cvReleaseImage(&avi_image
);
1167 cvReleaseImage(&avi_frame
);
1178 // we need to setup a new projection matrix for the title font.
1179 glMatrixMode(GL_PROJECTION
);
1181 glMatrixMode(GL_MODELVIEW
);
1183 glTranslatef(-.98, 0.9, 0.0);
1184 glScalef(.003, .003, .003);
1185 ftglFont
->FaceSize(32);
1186 glColor4f(1.0, 1.0, 1.0, 1);
1187 ftglFont
->Render("the artvertiser 0.4");
1188 glTranslatef(0, -(video_height
+30), 0);
1189 glColor4f(1.0, 1.0, 1.0, .6);
1190 //ftglFont->FaceSize(16);
1191 //ftglFont->Render(date(now).c_str());
1192 if (found_match
== 1 and (now
/1000)%2== 0)
1194 glTranslatef(video_width
-295, video_height
+35, 0);
1195 glColor4f(0.0, 1.0, 0.0, .8);
1196 glBegin(GL_TRIANGLES
);
1197 glVertex3f(140, 0, 0);
1198 glVertex3f(150, 10, 0);
1199 glVertex3f(140, 20, 0);
1201 glTranslatef(70, 5, 0);
1202 ftglFont
->FaceSize(16);
1203 ftglFont
->Render("tracking");
1206 // reset the ftgl font size for next pass
1207 ftglFont
->FaceSize(12);
1212 // not using exactly as defined in framerate.h because it's stupid
1213 frameEnd(0.8f
, 0.8f
, 0.8f
, 0.9, 0.95 );
1216 // now status string
1217 string draw_string
= status_string
;
1220 // show detector settings
1221 planar_object_recognizer
&detector(multi
->cams
[current_cam
]->detector
);
1222 char detector_settings_string
[1024];
1223 sprintf( detector_settings_string
, "ransac dist %4.2f stop %i iter %i detected points %i match count %i,\n"
1224 "refine %6.4f score %6.4f viewrate %6.4f tau %2i",
1225 detector
.ransac_dist_threshold
,
1226 detector
.ransac_stop_support
,
1227 detector
.max_ransac_iterations
,
1228 detector
.detected_point_number
,
1229 detector
.match_number
,
1230 detector
.non_linear_refine_threshold
,
1231 detector
.match_score_threshold
,
1232 detector
.min_view_rate
,
1233 detector
.point_detector_tau
);
1235 draw_string
+= "\n" + string(detector_settings_string
);
1237 drawGlutString( draw_string
.c_str(), 0.8f
, 0.8f
, 0.8f
, 0.01f
, 0.1f
);
1242 cvReleaseImage(&image
); // cleanup used image
1246 /*! GLUT calls this during photometric calibration or augmentation phase when
1247 * there's nothing else to do. This function does the 2D detection and bundle
1248 * adjusts the 3D pose of the calibration pattern.
1252 PROFILE_SECTION_PUSH( "idle loop" );
1255 multi
->grabFrames();
1257 // detect the calibration object in every image
1258 // (this loop could be paralelized)
1261 multi
->cams
[0]->detect();
1263 if(!frameTexture
) frameTexture
= new IplTexture
;
1264 frameTexture
->setImage(multi
->cams
[current_cam
]->frame
);
1268 multi
->model
.augm
.Clear();
1269 if (multi
->cams
[0]->detector
.object_is_detected
)
1271 add_detected_homography(0, multi
->cams
[0]->detector
, multi
->model
.augm
);
1275 multi
->model
.augm
.AddHomography();
1278 frame_ok
= multi
->model
.augm
.Accomodate(4, 1e-4);
1282 // fetch surface normal in world coordinates
1283 CvMat
*mat
= multi
->model
.augm
.GetObjectToWorld();
1285 for (int j
=0; j
<3; j
++) normal
[j
] = cvGet2D(mat
, j
, 2).val
[0];
1290 glutPostRedisplay();
1292 PROFILE_SECTION_POP();
1294 if ( show_profile_results
)
1296 // show profiler output
1297 printf("showing results\n");
1298 FProfiler::Display();
1299 show_profile_results
= false;
1303 //! Starts photometric calibration.
1307 glutDisplayFunc(draw
);