2 Copyright 2008, 2009, 2010 Julian Oliver <julian@julianoliver.com>
3 and Damian Stewart <damian@frey.co.nz>, based on BazAR which is
4 Copyright 2005, 2006 Computer Vision Lab, 3 Ecole Polytechnique
5 Federale de Lausanne (EPFL), Switzerland.
6 Distributed under the terms of the GNU General Public License v3.
8 This file is part of The Artvertiser.
10 The Artvertiser is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
15 The Artvertiser is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public License
21 along with The Artvertiser. If not, see <http://www.gnu.org/licenses/>.
24 #include "calibmodel.h"
25 #include "multigrab.h"
27 char CalibModel::progress_string
[2048];
29 CalibModel::CalibModel()
34 win
= "The Artvertiser 0.4";
35 train_working_image
= 0;
36 interactive_train_running
= false;
37 interactive_train_should_stop
= false;
38 debounce_green
= false;
39 debounce_redblue
= false;
40 strcpy(progress_string
, "");
41 learn_running
= false;
43 cvInitFont(&train_font
, CV_FONT_HERSHEY_SIMPLEX
, .5, .5, 0, 0, CV_AA
);
46 CalibModel::~CalibModel()
48 if (image
) cvReleaseImage(&image
);
49 if ( train_working_image
)
51 cvReleaseImage( &train_working_image
);
52 cvReleaseImage( &train_shot
);
56 void CalibModel::useModelFile(const char* file
)
61 CalibModel
*objectPtr
=0;
63 void CalibModel::onMouse(int event
, int x
, int y
, int flags
)
66 if ( state
== ARTVERT_CORNERS
)
67 ptr
= artvert_corners
;
70 if (event
== CV_EVENT_LBUTTONDOWN
)
72 // try to grab something
74 for (int i
=0; i
<4; i
++)
78 if (sqrt((double)(dx
*dx
+dy
*dy
)) <10) {
91 if (event
== CV_EVENT_LBUTTONUP
)
97 bool CalibModel::buildCached(int nbcam
, CvCapture
*capture
,
98 bool cache
, planar_object_recognizer
&detector
,
99 bool running_on_binoculars
)
104 printf("buildCached about to grab a lock\n");
109 detector
.ransac_dist_threshold
= 5;
110 detector
.max_ransac_iterations
= 800;
111 //detector.ransac_stop_support = 50;
112 //detector.non_linear_refine_threshold = 15.0f;
113 //detector.point_detector_tau = 10;
115 // A lower threshold will allow detection in harder conditions, but
116 // might lead to false positives.
117 detector
.match_score_threshold
=.03f
;
119 detector
.min_view_rate
=.1;
120 detector
.views_number
= 1000;
122 //detector.min_view_rate = .2;
124 static const int MAX_MODEL_KEYPOINTS
= 500; // maximum number of keypoints on the model
125 static const int PATCH_SIZE
= 32; // patch size in pixels
126 static const int YAPE_RADIUS
= 5; // yape radius
127 static const int NUM_TREES
= 12; // num classifier trees
128 static const int NUM_GAUSSIAN_LEVELS
= 3; // num gaussian levels
129 /*static const int MAX_MODEL_KEYPOINTS = 500; // maximum number of keypoints on the model
130 static const int PATCH_SIZE = 32; // patch size in pixels
131 static const int YAPE_RADIUS = 5; // yape radius
132 static const int NUM_TREES = 12; // num classifier trees
133 static const int NUM_GAUSSIAN_LEVELS = 3; // num gaussian levels*/
136 // Should we train or load the classifier ?
138 printf("model.buildCached() trying to load from %s...\n", modelfile
);
139 if(cache
&& detector
.build_with_cache(
140 string(modelfile
), // mode image file name
141 /*500, // maximum number of keypoints on the model
142 //200, // maximum number of keypoints on the model
143 32, // patch size in pixels
144 5, // yape radius. Use 3,5 or 7.
145 //3, // yape radius. Use 3,5 or 7.
146 12, // number of trees for the classifier. Somewhere between 12-50
147 //20, // number of trees for the classifier. Somewhere between 12-50
148 3*/ // number of levels in the gaussian pyramid
150 MAX_MODEL_KEYPOINTS
, // max keypoints
151 PATCH_SIZE
, // patch size
152 YAPE_RADIUS
, // yape radius. Use 3,5 or 7.
153 NUM_TREES
, // number of trees for the classifier. Somewhere between 12-50
154 NUM_GAUSSIAN_LEVELS
// number of levels in the gaussian pyramid
158 // loading worked. Remember the region of interest.
159 corners
[0].x
= detector
.new_images_generator
.u_corner1
;
160 corners
[0].y
= detector
.new_images_generator
.v_corner1
;
161 corners
[1].x
= detector
.new_images_generator
.u_corner2
;
162 corners
[1].y
= detector
.new_images_generator
.v_corner2
;
163 corners
[2].x
= detector
.new_images_generator
.u_corner3
;
164 corners
[2].y
= detector
.new_images_generator
.v_corner3
;
165 corners
[3].x
= detector
.new_images_generator
.u_corner4
;
166 corners
[3].y
= detector
.new_images_generator
.v_corner4
;
168 MultiThreadCapture
* mtc
= MultiThreadCaptureManager::getInstance()->getCaptureForCam(capture
);
170 IplImage* init_image = NULL;
174 got = mtc->getCopyOfLastFrame( &init_image );
177 !usleep( 10*1000 ) &&
179 if ( init_image == NULL )
181 printf("getCopyOfLastFrame timed out: capture failed\n");
187 cvReleaseImage( &image
);
188 image
= cvLoadImage(modelfile
, mtc
->getNumChannelsRaw()==3 );
189 image_width
= image
->width
;
190 image_height
= image
->height
;
191 //cvReleaseImage(&init_image);
193 /*printf("dumping loaded cache:\n");
201 cvReleaseImage( &image
);
203 // ask the user the take a shot of the model
205 if( running_on_binoculars
)
206 result
= interactiveTrainBinoculars();
208 result
= interactiveSetup(capture
);
211 printf("interactiveSetup failed\n");
218 printf("interactiveSetup succeeded\n");
221 image_width
= image
->width
;
222 image_height
= image
->height
;
224 // train the classifier to detect this model
225 //if (!detector.build(image, 500, 32, 3, 12, 3,0, 0))
226 //if (!detector.build(image, 200, 32, 5, 20, 3,0, 0))
227 int working_roi
[8] = { corners
[0].x
, corners
[0].y
,
228 corners
[1].x
, corners
[1].y
,
229 corners
[2].x
, corners
[2].y
,
230 corners
[3].x
, corners
[3].y
232 //LEARNPROGRESSION progress;
233 //LEARNPROGRESSION progress = 0;
235 printf("about to call detector.build...\n");
236 learn_running
= true;
237 strcpy(progress_string
, "preparing..");
238 if (!detector
.build(image
,
239 MAX_MODEL_KEYPOINTS
, // max keypoints
240 PATCH_SIZE
, // patch size
241 YAPE_RADIUS
, // yape radius. Use 3,5 or 7.
242 NUM_TREES
, // number of trees for the classifier. Somewhere between 12-50
243 NUM_GAUSSIAN_LEVELS
, // number of levels in the gaussian pyramid
244 &learnProgressionFunc
,
248 learn_running
= false;
249 printf("build based on interactiveSetup failed\n");
255 printf("detector.build succeeded\n");
256 strcpy( progress_string
, "saving..\n");
258 if (!cvSaveImage(modelfile
, image
))
260 printf("saving input image failed\n");
261 learn_running
= false;
267 // and the region of interest (ROI)
268 string roifn
= string(modelfile
) + ".roi";
269 ofstream
roif(roifn
.c_str());
272 learn_running
= false;
275 printf("saving .roi file failed\n");
278 for (int i
=0;i
<4; i
++)
279 roif
<< corners
[i
].x
<< " " << corners
[i
].y
<< "\n";
282 // and the artvert corners
283 roifn
= string(modelfile
) + ".artvertroi";
284 ofstream
artvert_roif(roifn
.c_str());
285 if (!artvert_roif
.good())
287 learn_running
= false;
290 printf("saving .artvertroi file failed\n");
293 for (int i
=0;i
<4; i
++)
294 artvert_roif
<< artvert_corners
[i
].x
<< " " << artvert_corners
[i
].y
<< "\n";
295 artvert_roif
.close();
297 // and the trained classifier
298 string classifier_directory
= string(modelfile
)+".classifier";
299 detector
.save(classifier_directory
);
301 string stable_points_filename
= string(modelfile
)+"_stable_points.bmp";
302 printf("saving stable points to %s\n", stable_points_filename
.c_str());
303 detector
.save_image_of_model_points(PATCH_SIZE
, stable_points_filename
.c_str() );
305 const char* initial_points_filename
= "initial_model_points.bmp";
306 string initial_points_new_filename
= string(modelfile
)+"_initial_points.bmp";
307 printf("renaming %s to %s\n", initial_points_filename
, initial_points_new_filename
.c_str() );
308 rename(initial_points_filename
, initial_points_new_filename
.c_str() );
310 /*printf("dumping trained cache:\n");
313 learn_running
= false;
317 assert( detector
.isReady() );
320 for (int i
=0; i
<4; i
++)
322 cn
[i
][0] = corners
[i
].x
;
323 cn
[i
][1] = corners
[i
].y
;
324 cout
<< corners
[i
].x
<< " " << corners
[i
].y
<< endl
;
327 // prepare the light calibration reference
328 return map
.init(nbcam
, image
, cn
, 8, 6);
331 static void putText(IplImage
*im
, const char *text
, CvPoint p
, CvFont
*f1
)
333 cvPutText(im
,text
,p
,f1
, cvScalar(0,255, 255));
337 IplImage
*myRetrieveFrame(CvCapture
*capture
)
339 #ifdef USE_MULTITHREADCAPTURE
340 assert(false && "don't call myRetrieveFrame when USE_MULTITHREADCAPTURE");
343 static IplImage
*s
=0;
344 IplImage
*frame
=cvRetrieveFrame(capture
);
345 if (frame
== 0) return 0;
347 if (frame
->nChannels
==1) {
348 printf(" PERFORMANCE WARNING: myRetrieveFrame converting colour\n");
349 if (!s
) s
=cvCreateImage(cvSize(frame
->width
,frame
->height
), IPL_DEPTH_8U
, 3);
350 cvCvtColor(frame
,s
,CV_GRAY2BGR
);
354 printf(" PERFORMANCE WARNING: myRetrieveFrame flipping\n");
355 if (!s
) s
=cvCreateImage(cvSize(frame
->width
,frame
->height
), IPL_DEPTH_8U
, 3);
363 IplImage
*myQueryFrame(CvCapture
*capture
)
365 #ifdef USE_MULTITHREADCAPTURE
366 assert(false && "don't call myQueryFrame when USE_MULTITHREADCAPTURE");
369 cvGrabFrame(capture
);
370 return myRetrieveFrame(capture
);
374 bool CalibModel::interactiveTrainBinoculars()
378 train_should_proceed
= false;
379 // we want the interactive training to run
380 interactive_train_running
= true;
381 interactive_train_should_stop
= false;
382 debounce_green
= true;
383 debounce_redblue
= true;
388 while ( interactive_train_running
&& !interactive_train_should_stop
&& timeout
>0 )
390 printf("waiting for interactiveTrainBinoculars to complete .. %i\n", timeout
);
395 return train_should_proceed
;
398 void CalibModel::interactiveTrainBinocularsUpdate( IplImage
* frame
,
403 if ( !interactive_train_running
)
406 if ( interactive_train_should_stop
)
408 interactive_train_running
= false;
413 if ( train_working_image
== 0 )
415 train_working_image
= cvCreateImage( cvGetSize( frame
), frame
->depth
, frame
->nChannels
);
416 train_shot
= cvCreateImage( cvGetSize( frame
), frame
->depth
, frame
->nChannels
);
420 bool R__
= ( button_red
&&!button_green
&&!button_blue
);
421 bool _G_
= (!button_red
&& button_green
&&!button_blue
);
422 bool __B
= (!button_red
&&!button_green
&& button_blue
);
423 bool RG_
= ( button_red
&& button_green
&&!button_blue
);
424 bool _GB
= (!button_red
&& button_green
&& button_blue
);
425 bool R_B
= ( button_red
&&!button_green
&& button_blue
);
426 bool RGB
= ( button_red
&& button_green
&& button_blue
);
430 // don't allow just green if we're trying to debounce
432 debounce_green
= false;
433 else if ( debounce_green
&& _G_
)
436 // don't allow red+blue if we're trying to debounce
437 if ( !button_red
|| !button_blue
)
438 debounce_redblue
= false;
439 else if ( debounce_redblue
&& R_B
)
454 cvCopy( frame
, train_shot
);
459 corners
[1].x
= frame
->width
-d
;
461 corners
[2].x
= frame
->width
-d
;
462 corners
[2].y
= frame
->height
-d
;
464 corners
[3].y
= frame
->height
-d
;
467 x
= corners
[corner_index
].x
;
468 y
= corners
[corner_index
].y
;
469 // debounce the green button
470 debounce_green
= true;
475 interactive_train_running
= false;
476 debounce_redblue
= true;
480 cvCopy( frame
, train_working_image
);
481 putText(train_working_image
, modelfile
, cvPoint(3,20), &train_font
);
482 putText(train_working_image
,"Please take a frontal view", cvPoint(3,40), &train_font
);
483 putText(train_working_image
,"of a textured planar surface", cvPoint(3,60), &train_font
);
484 putText(train_working_image
,"and press green", cvPoint(3,80), &train_font
);
485 putText(train_working_image
,"Press red+blue to abort", cvPoint(3,100), &train_font
);
489 cvCopy( train_shot
, train_working_image
);
490 putText(train_working_image
, modelfile
, cvPoint(3,20), &train_font
);
491 putText(train_working_image
, "Move green corners to match the", cvPoint(3,40), &train_font
);
492 putText(train_working_image
, "calibration target", cvPoint(3,60), &train_font
);
493 putText(train_working_image
, "Press red+blue to restart", cvPoint(3,80), &train_font
);
494 putText(train_working_image
, "Press green when ready", cvPoint(3,100), &train_font
);
505 // update corner if necessary
507 corners
[corner_index
].x
= x
;
509 corners
[corner_index
].y
= y
;
510 // continue checking buttons
514 corners
[corner_index
].x
= x
;
515 corners
[corner_index
].y
= y
;
517 if ( corner_index
> 3 )
521 for ( int i
=0; i
<4; i
++ )
522 artvert_corners
[i
] = corners
[i
];
523 state
= ARTVERT_CORNERS
;
527 x
= artvert_corners
[corner_index
].x
;
528 y
= artvert_corners
[corner_index
].y
;
532 x
= corners
[corner_index
].x
;
533 y
= corners
[corner_index
].y
;
536 debounce_green
= true;
542 if ( corner_index
< 0 )
546 x
= corners
[corner_index
].x
;
547 y
= corners
[corner_index
].y
;
549 debounce_redblue
= true;
553 cvPolyLine(train_working_image
, &ptr
, &four
, 1, 1,
555 cvCircle( train_working_image
, corners
[corner_index
], 10, cvScalar(0,255,0));
559 case ARTVERT_CORNERS
:
560 cvCopy( train_shot
, train_working_image
);
561 putText(train_working_image
, modelfile
, cvPoint(3,20), &train_font
);
562 putText(train_working_image
, "Move red corners to match the", cvPoint(3,40), &train_font
);
563 putText(train_working_image
, "artvert target area;", cvPoint(3,60), &train_font
);
564 putText(train_working_image
, "Press red+blue to restart", cvPoint(3,80), &train_font
);
565 putText(train_working_image
, "Press green when ready", cvPoint(3,100), &train_font
);
576 // update corner if necessary
578 artvert_corners
[corner_index
].x
= x
;
580 artvert_corners
[corner_index
].y
= y
;
581 // continue checking buttons
585 artvert_corners
[corner_index
].x
= x
;
586 artvert_corners
[corner_index
].y
= y
;
588 if ( corner_index
> 3 )
592 cvReleaseImage( &image
);
593 image
= cvCreateImage( cvGetSize( train_shot
), train_shot
->depth
, train_shot
->nChannels
);
594 cvCopy( train_shot
, image
);
595 train_should_proceed
= true;
596 interactive_train_running
= false;
600 x
= artvert_corners
[corner_index
].x
;
601 y
= artvert_corners
[corner_index
].y
;
603 debounce_green
= true;
609 if ( corner_index
< 0 )
613 x
= artvert_corners
[corner_index
].x
;
614 y
= artvert_corners
[corner_index
].y
;
616 debounce_redblue
= true;
621 cvPolyLine(train_working_image
, &ptr
, &four
, 1, 1,
623 ptr
= artvert_corners
;
624 cvPolyLine(train_working_image
, &ptr
, &four
, 1, 1,
626 cvCircle( train_working_image
, artvert_corners
[corner_index
], 10, cvScalar(0,0,255));
631 train_texture
.setImage( train_working_image
);
635 void CalibModel::interactiveTrainBinocularsDraw()
637 if ( !interactive_train_running
)
640 IplTexture
* tex
= &train_texture
;
642 IplImage
*im
= tex
->getIm();
644 int h
= im
->height
-1;
646 glMatrixMode(GL_PROJECTION
);
648 glMatrixMode(GL_MODELVIEW
);
652 glDisable(GL_DEPTH_TEST
);
659 glTexCoord2f(tex
->u(0), tex
->v(0));
661 glTexCoord2f(tex
->u(w
), tex
->v(0));
663 glTexCoord2f(tex
->u(w
), tex
->v(h
));
665 glTexCoord2f(tex
->u(0), tex
->v(h
));
669 tex
->disableTexture();
674 bool CalibModel::interactiveSetup(CvCapture
*capture
)
680 CvFont font
, fontbold
;
682 cvInitFont(&font
, CV_FONT_HERSHEY_SIMPLEX
, .5, .5, 0, 0, CV_AA
);
684 cvNamedWindow(win
, CV_WINDOW_AUTOSIZE
);
688 cvSetMouseCallback(win
, onMouseStatic
, this);
692 IplImage
*frame_gray
, *frame
= NULL
;
694 MultiThreadCapture
* mtc
= MultiThreadCaptureManager::getInstance()->getCaptureForCam(capture
);
698 got
= mtc
->getLastDetectFrame( &frame_gray
, &frame
, ×tamp
, /*blocking*/true );
702 (timeout
-=100) > 0 );
705 printf("capture failed\n");
709 IplImage
*shot
=0, *text
=0;
713 bool accepted
=false;
714 bool artvert_accepted
= false;
718 char k
= cvWaitKey(10);
720 if (k
==27 || k
=='q') {
721 if (shot
) cvReleaseImage(&shot
);
722 if (text
) cvReleaseImage(&text
);
726 // clear text or grab the image to display
727 if (!pause
|| shot
==0) {
728 bool got
= mtc
->getLastDetectFrame( &frame_gray
, &frame
, NULL
,/*block until available*/true );
732 text
=cvCreateImage(cvGetSize(frame
), IPL_DEPTH_8U
, 3);
736 corners
[1].x
= frame
->width
-d
;
738 corners
[2].x
= frame
->width
-d
;
739 corners
[2].y
= frame
->height
-d
;
741 corners
[3].y
= frame
->height
-d
;
743 if (frame
->nChannels
==1)
744 cvCvtColor(frame
, text
, CV_GRAY2BGR
);
748 if (shot
->nChannels
==1)
749 cvCvtColor(shot
, text
, CV_GRAY2BGR
);
756 // display text / react to keyboard
761 if (shot
) cvCopy(frame
,shot
);
762 else shot
= cvCloneImage(frame
);
767 putText(text
, modelfile
, cvPoint(3,20), &font
);
768 putText(text
,"Please take a frontal view", cvPoint(3,40), &font
);
769 putText(text
,"of a textured planar surface", cvPoint(3,60), &font
);
770 putText(text
,"and press space", cvPoint(3,80), &font
);
774 putText(text
, modelfile
, cvPoint(3,20), &font
);
775 putText(text
, "Drag green corners to match the", cvPoint(3,40), &font
);
776 putText(text
, "calibration target", cvPoint(3,60), &font
);
777 putText(text
, "press 'r' to restart", cvPoint(3,80), &font
);
778 putText(text
, "press space when ready", cvPoint(3,100), &font
);
784 for ( int i
=0;i
<4; i
++ )
786 artvert_corners
[i
].x
= corners
[i
].x
;
787 artvert_corners
[i
].y
= corners
[i
].y
;
789 state
= ARTVERT_CORNERS
;
792 cvPolyLine(text
, &ptr
, &four
, 1, 1,
795 case ARTVERT_CORNERS
:
796 putText(text
, "Drag red corners to match the", cvPoint(3,20), &font
);
797 putText(text
, "artvert target area;", cvPoint(3,40), &font
);
798 putText(text
, "press 'r' to restart", cvPoint(3,60), &font
);
799 putText(text
, "press space when ready", cvPoint(3,80), &font
);
808 cvPolyLine(text
, &ptr
, &four
, 1, 1,
810 ptr
= artvert_corners
;
811 cvPolyLine(text
, &ptr
, &four
, 1, 1,
815 cvShowImage(win
, text
);
818 cvReleaseImage(&text
);
821 cvDestroyWindow( win
);
822 // make sure the destroy window succeeds
828 void CalibModel::onMouseStatic(int event
, int x
, int y
, int flags
, void* param
)
831 ((CalibModel
*)param
)->onMouse(event
,x
,y
,flags
);
833 objectPtr
->onMouse(event
,x
,y
,flags
);
835 cerr
<< "onMouseStatic(): null-pointer.\n";
839 void CalibModel::learnProgressionFunc( int phase
, int current
, int total
)
841 sprintf(progress_string
, "learning, phase %i/4: %4.1f%%", phase
+1, 100.0f
*float(current
)/total
);
844 printf("\rphase %i/4: %4i/%4i ", phase
+1, current
, total
);
847 /*for ( int i=0; i<4; i++ )
850 strcat(progress_string, "[----------------------------------------]\n" );
851 else if ( phase == i )
853 float finishedPct = float(current)/float(total);
854 int count = finishedPct*40;
855 strcat(progress_string, "[");
856 for ( int j=0; j<count; j++ )
858 strcat(progress_string, "-");
860 for ( int j=0; j<40-count; j++ )
862 strcat(progress_string, " ");
864 strcat(progress_string, "]\n");
867 strcat( progress_string, "[ ]\n");