- morning panick victory
[The-Artvertiser.git] / artvertiser / artvertiser.cpp
blobe1b0b6cf09227406b3141ee155b939998cc96ffb
1 /*
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)
7 * any later version.
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
12 * more details.
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
26 * on plain init.
28 * Usage:
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
48 #include "multigrab.h"
50 // read from arduino
51 #include <stdio.h> /* Standard input/output definitions */
52 #include <stdlib.h>
53 #include <stdint.h> /* Standard types */
54 #include <string.h> /* String function definitions */
55 #include <unistd.h> /* UNIX standard function definitions */
56 #include <errno.h> /* Error number definitions */
57 #include <fcntl.h> /* File control definitions */
58 #include <errno.h> /* Error number definitions */
59 #include <fcntl.h> /* File control definitions */
60 #include <termios.h> /* POSIX terminal control definitions */
62 #include <iostream>
63 #include <sstream> // for conv int->str
64 #include <vector>
65 #include <opencv/cv.h>
66 #include <highgui.h>
67 #include <map>
69 #include <stdio.h>
70 #include <time.h>
72 #ifdef HAVE_CONFIG_H
73 #include <config.h>
74 #endif
76 #include <calib/camera.h>
78 #ifdef HAVE_APPLE_OPENGL_FRAMEWORK
79 #include <GLUT/glut.h>
80 #else
81 #include <GL/glut.h>
82 #endif
84 #include "/usr/include/freetype2/freetype/config/ftconfig.h"
85 #include <FTGL/ftgl.h>
87 #include "FProfiler/FProfiler.h"
89 // framerate counter
90 #include "framerate.h"
92 #include <list>
93 using namespace std;
95 // matrix tracker
96 #include "MatrixTracker/MatrixTracker.h"
98 #define IsRGB(s) ((s[0] == 'R') && (s[1] == 'G') && (s[2] == 'B'))
99 #define IsBGR(s) ((s[0] == 'B') && (s[1] == 'G') && (s[2] == 'R'))
101 #ifndef GL_CLAMP_TO_BORDER
102 #define GL_CLAMP_TO_BORDER 0x812D
103 #endif
104 #define GL_MIRROR_CLAMP_EXT 0x8742
106 #define DEFAULT_WIDTH 640
107 #define DEFAULT_HEIGHT 480
108 #define DEFAULT_V4LDEVICE 0
110 #define NUMARTVERTS 5
112 // buttons via arduino
113 // button_state is bitmapped so as to handle multiple button presses at once
114 char button_state = 0;
115 bool button_state_changed = false;
116 const char BUTTON_RED = 0b001;
117 const char BUTTON_GREEN = 0b010;
118 const char BUTTON_BLUE = 0b100;
119 // serial comms
120 int serialport_init(const char* serialport, int baud);
121 int serialport_read_until(int fd, char* buf, char until);
122 bool serial_thread_should_exit = false;
123 bool serial_thread_is_running = false;
124 pthread_t serial_thread;
125 void startSerialThread();
126 void shutdownSerialThread();
127 void* serialThreadFunc( void* );
128 bool running_on_binoculars = false;
131 // we continue tracking for 1 second, then fade for 3
132 static const float SECONDS_LOST_TRACK = 0.5f;
133 static const float SECONDS_LOST_FADE = 1.0f;
134 static const float MAX_FADE_SHOW = 0.9f;
135 static const float MAX_FADE_NORMAL = 1.0f;
137 static const int DEFAULT_CAPTURE_FPS = 20;
139 //#define WIDTH 320
140 //#define HEIGHT 240
142 MultiGrab *multi=0;
143 CamCalibration *calib=0;
144 CvPoint projPts[4];
145 IplTexture *raw_frame_texture=0;
146 FTime raw_frame_timestamp;
147 IplTexture *tex=0;
148 IplImage *image = 0;
149 CvCapture *capture = 0;
150 CvCapture *avi_capture = 0;
151 IplImage *avi_image = 0;
152 IplImage *avi_frame = 0;
153 //IplImage *model_image = 0;
154 IplImage *this_frame = 0;
155 IplImage *last_frame = 0;
156 IplImage *diff= 0;
157 IplImage *bit_frame= 0;
159 int v4l_device = DEFAULT_V4LDEVICE;
160 int video_width = DEFAULT_WIDTH;
161 int video_height = DEFAULT_HEIGHT;
162 int detect_width = DEFAULT_WIDTH;
163 int detect_height = DEFAULT_HEIGHT;
164 int desired_capture_fps = DEFAULT_CAPTURE_FPS;
166 // load some images. hard-coded for know until i get the path parsing together.
167 IplImage *image1 = cvLoadImage("artvert1.png");
168 IplImage *image2 = cvLoadImage("artvert2.png");
169 IplImage *image3 = cvLoadImage("artvert3.png");
170 IplImage *image4 = cvLoadImage("artvert4.png");
171 IplImage *image5 = cvLoadImage("artvert5.png");
172 IplImage *fallback_artvert_image = cvLoadImage("artvert1.png");
174 // matrix tracker
175 MatrixTracker matrix_tracker;
177 // define a container struct for each artvert
178 struct artvert_struct
180 const char *artvert;
181 IplImage *image;
182 const char *date;
183 const char *author;
184 const char *advert;
185 const char *street;
188 typedef vector<artvert_struct> artverts_list;
189 artverts_list artverts(5);
191 // create a vector for the images and initialise it.
192 typedef vector<IplImage> imgVec;
194 bool frame_ok=false;
195 bool cache_light=false;
196 bool dynamic_light=false;
197 bool sphere_object=false;
198 bool avi_play=false;
199 bool avi_play_init=false;
200 bool lbutton_down = false;
201 bool label = false;
203 bool track_kalman = false;
204 bool delay_video = true;
205 // how many frames to delay the video
206 static const int VIDEO_DELAY_FRAMES=7;
208 double a_proj[3][4];
209 double old_a_proj[3][4];
210 float fade = 0.0;
211 FTime last_frame_caught_time;
212 FTime frame_timer;
213 float draw_fps;
214 int difference = 0;
215 int have_proj = 0;
216 int nb_light_measures=0;
217 int geom_calib_nb_homography;
218 int current_cam = 0;
219 int avi_init = 0;
220 int augment = 1;
221 int cnt=0;
223 vector<int> roi_vec;
224 CvPoint2D32f *c1 = new CvPoint2D32f[4];
225 vector<int> artvert_roi_vec;
227 char *image_path;
229 class Artvert
231 public:
232 Artvert() {
233 artvert_image=0;
234 model_file="model.bmp";
235 artvert_image_file="artvert1.png";
236 artvert_is_movie= false;
237 artist = "unknown artist";
238 advert = "unknown advert";
239 name = "unnamed artvert";
240 avi_capture = NULL;
241 avi_image = NULL;
242 avi_play_init = false;
244 ~Artvert()
246 if ( artvert_image )
247 cvReleaseImage( &artvert_image );
248 if ( avi_capture )
249 cvReleaseCapture( &avi_capture );
250 if ( avi_image )
251 cvReleaseImage( &avi_image );
254 IplImage* getArtvertImage()
256 if ( artvert_is_movie )
259 if ( !avi_capture )
261 avi_capture = cvCaptureFromAVI( artvert_movie_file.c_str() );
262 avi_play_init = false;
265 IplImage *avi_frame = 0;
266 avi_frame = cvQueryFrame( avi_capture );
267 if ( avi_frame == 0 )
268 return fallback_artvert_image;
269 if ( avi_image == 0 )
270 avi_image = cvCreateImage( cvGetSize(avi_frame), avi_frame->depth, avi_frame->nChannels );
271 cvCopy( avi_frame, avi_image );
272 avi_image->origin = avi_frame->origin;
273 GLenum format = IsBGR(avi_image->channelSeq) ? GL_BGR_EXT : GL_RGBA;
275 if (!avi_play_init)
277 glGenTextures(1, &imageID);
278 glBindTexture(GL_TEXTURE_2D, imageID);
279 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
280 avi_play_init=true;
282 return avi_image;
284 else
286 if ( !artvert_image )
288 printf("loading artvert image '%s'\n", artvert_image_file.c_str() );
289 artvert_image = cvLoadImage( artvert_image_file.c_str() );
291 if ( !artvert_image )
293 fprintf(stderr, "couldn't load artvert image '%s'\n", artvert_image_file.c_str() );
294 artvert_image = fallback_artvert_image;
296 return artvert_image;
300 string model_file;
301 string artvert_image_file;
302 string artist;
303 string advert;
304 string name;
305 bool artvert_is_movie;
306 string artvert_movie_file;
307 private:
308 CvCapture* avi_capture;
309 IplImage* avi_image;
310 bool avi_play_init;
311 GLuint imageID;
313 IplImage* artvert_image;
316 vector< Artvert > artvert_list;
317 bool new_artvert_switching_in_progress = false;
318 int current_artvert_index=-1;
319 bool new_artvert_requested = false;
320 int new_artvert_requested_index = 0;
321 vector< bool > model_file_needs_training;
322 #include "ofxXmlSettings/ofxXmlSettings.h"
324 // detection thread
325 pthread_t detection_thread;
326 double detection_fps = 0.0;
327 static void shutdownDetectionThread();
328 static void startDetectionThread( int thread_priority = 0 /* only takes effect if root */ );
329 static void* detectionThreadFunc( void* _data );
330 bool detection_thread_should_exit = false;
331 bool detection_thread_running = false;
334 static void start();
335 static void geomCalibStart(bool cache);
337 // initialise a couple of fonts.
338 CvFont font, fontbold;
340 // Gl format for texturing
341 GLenum format;
342 GLuint imageID;
344 // ftgl font setup
345 static FTFont *ftglFont;
347 // interface
348 bool show_status = false;
349 bool show_profile_results = false;
350 string status_string = "";
352 // menu
353 // hide after 5s
354 #define MENU_HIDE_TIME 5.0f
355 bool menu_show = false;
356 FTime menu_timer;
357 bool menu_is_showing = false;
358 void updateMenu();
359 void drawMenu();
361 /* use this to read paths from the file system
363 string getExtension(const string &file)
365 string::size_type dot = file.rfind('.');
366 string lcpath = file;
367 string suffix;
368 transform(lcpath.begin(), lcpath.end(), lcpath.begin(), tolower);
369 if(dot != string::npos)
371 suffix = lcpath.substr(dot + 1);
373 return suffix;
379 string getSettingsString()
381 planar_object_recognizer &detector(multi->cams[current_cam]->detector);
382 static char detector_settings_string[2048];
383 sprintf( detector_settings_string, "1.ransac dist %4.2f 2.iter %i detected points %i match count %i,\n"
384 "3.refine %6.4f 4.score %6.4f 5.best_support thresh %2i 6.tau %2i\n"
385 "smoothing: 7.position %5.3f 8.position_z %5.3f \n frames back: 9.raw %2i 0.returned %2i",
386 detector.ransac_dist_threshold_ui,
387 detector.max_ransac_iterations_ui,
388 detector.detected_point_number,
389 detector.match_number,
390 detector.non_linear_refine_threshold_ui,
391 detector.match_score_threshold_ui,
392 detector.best_support_thresh_ui,
393 detector.point_detector_tau_ui,
394 matrix_tracker.getPositionSmoothing(),
395 matrix_tracker.getPositionZSmoothing(),
396 matrix_tracker.getFramesBackRaw(),
397 matrix_tracker.getFramesBackReturned() );
399 return detector_settings_string;
403 std::string date(int now)
405 time_t rawtime;
406 struct tm *timeinfo;
407 char tBuffer[80];
408 time ( &rawtime );
409 timeinfo = localtime ( &rawtime );
410 strftime (tBuffer,80,"%I:%M:%S:%p, %d %b %Y",timeinfo);
411 string timeStr;
412 stringstream _timeStr;
413 _timeStr << tBuffer;
414 timeStr = _timeStr.str();
415 return timeStr;
419 // mouse input using GLUT.
420 void entry(int state)
422 if (state == GLUT_ENTERED)
423 cout << "Mouse Entered" << endl;
424 else
425 cout << "Mouse Left" << endl;
428 void mouse(int button, int state, int x, int y)
430 if (button == GLUT_RIGHT_BUTTON)
432 if (state == GLUT_DOWN)
434 label = true;
437 else
439 label = false;
442 else if (button == GLUT_LEFT_BUTTON)
444 if (state == GLUT_DOWN)
446 lbutton_down = true;
447 if (cnt >= NUMARTVERTS-1)
449 cnt = 0;
451 else
453 cnt ++;
456 else
457 lbutton_down = false;
459 cout << "we are on image " << cnt << endl;
462 // text drawing function
463 static void drawText(IplImage *img, const char *text, CvPoint point, CvFont *font, CvScalar colour, double size)
465 cvInitFont( font, CV_FONT_HERSHEY_DUPLEX, size, size, 0, 1, CV_AA);
466 //cvInitFont( font, CV_FONT_HERSHEY_PLAIN, size, size, 0, 1, CV_AA);
467 cvPutText(img, text, point, font, colour);
470 // read in ROI coords from txt file into vector.
471 static vector<int> readROI(const char *filename)
473 cout << filename << endl;
474 string l;
475 ifstream roi(filename);
476 vector<int> v;
477 int coord;
478 char s[10];
479 char *s1;
480 int lines = 0;
482 if (roi.is_open())
484 while (!roi.eof())
486 getline(roi, l);
487 strcpy(s, l.c_str());
488 s1 = strtok(s, " ");
490 while (s1 != NULL)
492 //roi_vec.push_back(atoi(s1));
493 v.push_back(atoi(s1));
494 s1 = strtok(NULL, " ,");
497 roi.close();
499 else
501 cout << "roi file not found" << endl;
503 return v;
506 //! GLUT callback on window size change
507 static void reshape(int width, int height)
509 //GLfloat h = (GLfloat) height / (GLfloat) width;
510 int winWidth = video_width;
511 int winHeight = video_height;
512 glViewport(0,0,winWidth, winHeight);
513 glutPostRedisplay();
516 //! Print a command line help and exit.
517 static void usage(const char *s)
519 cerr << "usage:\n" << s
520 << " [-m <model image>] [-m <model image>] ... \n "
521 " [-ml <model images file .xml>] [-r] [-t] [-g] [-a <path>] [-l] [-vd <num>] [-vs <width> <height>]\n"
522 " [-ds <width> <height>] [-fps <fps>]\n\n"
523 //" -a <path> specify path to AVI (instead of v4l device)\n"
524 " -b <path> specify path to AVI (instead of v4l device), ignores -vs\n"
525 " -m specifies model image (may be used multiple times)\n"
526 " -ml <path> load model images from <path> (respects additional -m paths)\n"
527 " -r do not load any data\n"
528 " -t train a new classifier\n"
529 " -g recompute geometric calibration\n"
530 " -a <path> load an AVI movie as an artvert\n"
531 " -i <path> load an image as an artvert\n"
532 " -l rebuild irradiance map from scratch\n"
533 " -vd <num> V4L video device number (0-n)\n"
534 " -vs <width> <height> video width and height (default 640x480)\n"
535 " -ds <width> <height> frame size at which to run the detector (default to video width/height)\n"
536 " -fps <fps> desired fps at which to run the image capture\n\n";
537 exit(1);
542 void exit_handler()
544 printf("in exit_handler\n");
545 // shutdown serial
546 if ( serial_thread_is_running )
548 printf("stopping serial\n");
549 shutdownSerialThread();
551 // shutdown detection thread
552 if ( detection_thread_running )
554 printf("stopping detection\n");
555 shutdownDetectionThread();
557 // shutdown capture
558 if ( multi )
560 printf("stopping multithread capture\n");
561 multi->cams[0]->shutdownMultiThreadCapture();
563 // delete cameras
564 if ( multi )
566 printf("deleteing multi\n");
567 delete multi;
571 int serialport_init(const char* serialport, int baud)
573 struct termios toptions;
574 int fd;
576 //fprintf(stderr,"init_serialport: opening port %s @ %d bps\n",
577 // serialport,baud);
579 fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
580 if (fd == -1) {
581 perror("init_serialport: Unable to open port ");
582 return -1;
585 if (tcgetattr(fd, &toptions) < 0) {
586 perror("init_serialport: Couldn't get term attributes");
587 return -1;
589 speed_t brate = baud; // let you override switch below if needed
590 switch(baud) {
591 case 4800: brate=B4800; break;
592 case 9600: brate=B9600; break;
593 #ifdef B14400
594 case 14400: brate=B14400; break;
595 #endif
596 case 19200: brate=B19200; break;
597 #ifdef B28800
598 case 28800: brate=B28800; break;
599 #endif
600 case 38400: brate=B38400; break;
601 case 57600: brate=B57600; break;
602 case 115200: brate=B115200; break;
604 cfsetispeed(&toptions, brate);
605 cfsetospeed(&toptions, brate);
607 // 8N1
608 toptions.c_cflag &= ~PARENB;
609 toptions.c_cflag &= ~CSTOPB;
610 toptions.c_cflag &= ~CSIZE;
611 toptions.c_cflag |= CS8;
612 // no flow control
613 toptions.c_cflag &= ~CRTSCTS;
615 toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
616 toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
618 toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
619 toptions.c_oflag &= ~OPOST; // make raw
621 // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
622 toptions.c_cc[VMIN] = 0;
623 toptions.c_cc[VTIME] = 20;
625 if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
626 perror("init_serialport: Couldn't set term attributes");
627 return -1;
630 return fd;
634 // arduino serial port read
635 int serialport_read_until(int fd, char* buf, char until)
637 char b[1];
638 int i=0;
639 // 1000ms timeout
640 int timeout = 1000*1000;
641 do {
642 int n = read(fd, b, 1); // read a char at a time
643 if( n==0||n==-1 ) {
644 timeout -= 100;
645 usleep( 100 ); // wait 100 usec try again
646 continue;
648 buf[i] = b[0]; i++;
649 } while( b[0] != until && timeout > 0 );
651 if ( timeout<=0 )
652 fprintf(stderr, "serialport_read_until timed out\n");
654 buf[i] = 0; // null terminate the string
655 return 0;
659 bool loadOrTrain( int new_index )
661 // fetch data
662 if ( new_index < 0 || new_index >= artvert_list.size() )
664 fprintf(stderr,"loadOrTrain: invalid index %i (artvert_list has %i members)\n", new_index, artvert_list.size() );
665 return false;
668 // switching model..
669 new_artvert_switching_in_progress = true;
670 bool wants_training = model_file_needs_training[new_index];
671 string model_file = artvert_list[new_index].model_file;
672 // do we need to switch, or can we use the same model file?
673 if ( ( current_artvert_index < 0 || current_artvert_index >= artvert_list.size() ) ||
674 model_file != artvert_list[current_artvert_index].model_file )
676 // load
677 bool trained = multi->loadOrTrainCache( wants_training, model_file.c_str(), running_on_binoculars );
678 if ( !trained )
680 // fail
681 current_artvert_index = -1;
682 new_artvert_switching_in_progress = false;
683 return false;
687 // copy char model_file before munging with strcat
688 char s[1024];
689 strcpy (s, model_file.c_str());
690 strcat(s, ".roi");
691 roi_vec = readROI(s);
693 strcpy( s, model_file.c_str() );
694 strcat(s, ".artvertroi");
695 artvert_roi_vec = readROI(s);
696 if ( artvert_roi_vec.empty() )
698 // use roi_vec
699 artvert_roi_vec.insert( artvert_roi_vec.begin(), roi_vec.begin(), roi_vec.end() );
702 // load model_image for use with diffing, later
703 // model_image = cvLoadImage(model_file.c_str());
705 c1[0].x = roi_vec[0];
706 c1[0].y = roi_vec[1];
707 c1[1].x = roi_vec[2];
708 c1[1].y = roi_vec[3];
709 c1[2].x = roi_vec[4];
710 c1[2].y = roi_vec[5];
711 c1[3].x = roi_vec[6];
712 c1[3].y = roi_vec[7];
715 // update current index
716 current_artvert_index = new_index;
718 // no longer switching
719 new_artvert_switching_in_progress = false;
721 return true;
727 /*!\brief Initialize everything
729 * - Parse the command line
730 * - Initialize all the cameras
731 * - Load or interactively build a model, with its classifier.
732 * - Set the GLUT callbacks for geometric calibration or, if already done, for photometric calibration.
735 static bool init( int argc, char** argv )
737 // register exit function
738 atexit( &exit_handler );
740 // dump opencv version
741 printf("built with opencv version %s\n", CV_VERSION );
743 // more from before init should be moved here
744 bool redo_lighting=false;
745 bool redo_training = false;
746 bool redo_geom = false;
747 char *avi_bg_path="";
748 bool got_ds = false;
749 bool got_fps = false;
750 bool video_source_is_avi = false;
751 char *model_file_list_file = NULL;
753 // parse command line
754 for (int i=1; i<argc; i++)
756 if (strcmp(argv[i], "-m") ==0)
758 if (i==argc-1)
759 usage(argv[0]);
760 Artvert a;
761 a.model_file = argv[i+1];
762 a.advert = "cmdline "+a.model_file;
763 // store
764 artvert_list.push_back( a );
765 printf(" -m: adding model image '%s'\n", argv[i+1] );
766 i++;
768 else if ( strcmp(argv[i], "-binoc" ) == 0 )
770 running_on_binoculars = true;
771 printf(" -binoc: running on binoculars\n");
773 else if ( strcmp(argv[i], "-ml" )== 0 )
775 if ( i==argc-1)
776 usage(argv[0]);
777 model_file_list_file = argv[i+1];
778 printf(" -ml: loading model image list from '%s'\n", argv[i+1] );
779 i++;
781 else if (strcmp(argv[i], "-r")==0)
783 redo_geom=redo_training=redo_lighting=true;
785 else if (strcmp(argv[i], "-g")==0)
787 redo_geom=true;
789 else if (strcmp(argv[i], "-l")==0)
791 redo_lighting=true;
793 else if (strcmp(argv[i], "-t")==0)
795 redo_training=true;
796 printf( "-t: redoing training\n");
798 else if (strcmp(argv[i], "-a")==0)
800 avi_capture=cvCaptureFromAVI(argv[i+1]);
801 avi_play=true;
803 else if (strcmp(argv[i], "-i")==0)
805 IplImage *image1 = cvLoadImage(argv[i]+1);
807 else if (strcmp(argv[i], "-b")==0)
809 video_source_is_avi = true;
810 avi_bg_path=argv[i+1];
811 printf(" -b: loading from avi '%s'\n", avi_bg_path );
813 else if (strcmp(argv[i], "-i")==0)
815 image_path=argv[i+1];
817 else if ( strcmp(argv[i], "-fps")==0 )
819 desired_capture_fps=atoi(argv[i+1]);
820 got_fps = true;
821 i++;
823 else if (strcmp(argv[i], "-vd")==0)
825 v4l_device=atoi(argv[i+1]);
826 printf(" -vd: using v4l device %i\n", v4l_device);
827 i++;
829 else if (strcmp(argv[i], "-vs")==0)
831 video_width=atoi(argv[i+1]);
832 video_height=atoi(argv[i+2]);
833 if ( !got_ds )
835 // also set detect size (if not already set)
836 detect_width = video_width;
837 detect_height = video_height;
839 printf(" -vs: video size is %ix%i\n", video_width, video_height );
840 if ( video_width == 0 || video_height == 0 )
842 usage(argv[0]);
843 exit(1);
845 i+=2;
847 else if ( strcmp(argv[i], "-ds")==0 )
849 detect_width = atoi(argv[i+1]);
850 detect_height = atoi(argv[i+2]);
851 printf(" -ds: detection frame size is %ix%i\n", detect_width, detect_height );
852 if ( detect_width == 0 || detect_height == 0 )
854 usage(argv[0]);
855 exit(1);
857 got_ds = true;
858 i+=2;
860 else if (argv[i][0]=='-')
862 usage(argv[0]);
868 // read model files from model_file_list_file
869 if ( model_file_list_file != NULL )
871 // try to open
872 ofxXmlSettings data;
873 data.loadFile( model_file_list_file );
875 if ( data.getNumTags( "artverts" ) == 1 )
877 data.pushTag( "artverts" );
878 int num_filenames = data.getNumTags( "advert" );
879 printf(" -ml: opened %s, %i adverts\n", model_file_list_file, num_filenames );
880 for ( int i=0; i<num_filenames; i++ )
882 data.pushTag("advert", i);
883 Artvert a;
884 a.model_file = data.getValue( "model_filename", "model.bmp" );
885 a.advert = data.getValue( "advert", "unknown advert" );
886 int num_artverts = data.getNumTags( "artvert" );
887 printf(" -ml: got advert, model file '%s', advert '%s', %i artverts\n", a.model_file.c_str(), a.advert.c_str(), num_artverts );
888 for ( int j=0; j<num_artverts; j++ )
890 data.pushTag("artvert", j );
891 a.name = data.getValue( "name", "unnamed" );
892 a.artist = data.getValue( "artist", "unknown artist" );
893 if ( data.getNumTags("movie_filename") != 0 )
895 // load a movie
896 a.artvert_is_movie = true;
897 a.artvert_movie_file = data.getValue("movie_filename", "artvertmovie1.mp4" );
899 else
901 // load an image
902 a.artvert_image_file = data.getValue( "image_filename", "artvert1.png" );
904 printf(" %i: %s:%s:%s\n", j, a.name.c_str(), a.artist.c_str(),
905 a.artvert_is_movie?(a.artvert_movie_file+"( movie)").c_str() : a.artvert_image_file.c_str() );
907 artvert_list.push_back( a );
908 data.popTag();
910 data.popTag();
912 data.popTag();
914 else
916 printf(" -ml: error reading '%s': couldn't find 'artverts' tag\n", model_file_list_file );
920 // check if model file list is empty
921 if ( artvert_list.empty() )
923 // add default
924 Artvert a;
925 artvert_list.push_back( a );
928 // set up training flags
929 for ( int i=0; i<artvert_list.size(); i++ )
930 model_file_needs_training.push_back( redo_training );
932 // check for video size arg if necessary
933 if ( video_source_is_avi )
935 // try to read from video
936 CvCapture* temp_cap = cvCaptureFromAVI(avi_bg_path);
937 video_width = (int)cvGetCaptureProperty( temp_cap, CV_CAP_PROP_FRAME_WIDTH );
938 video_height = (int)cvGetCaptureProperty( temp_cap, CV_CAP_PROP_FRAME_HEIGHT );
939 int video_fps = (int)cvGetCaptureProperty( temp_cap, CV_CAP_PROP_FPS );
940 printf(" -b: read video width/height %i/%i from avi (ignoring -vs)\n", video_width, video_height );
941 if ( !got_ds )
943 detect_width = video_width;
944 detect_height = video_height;
946 if ( !got_fps )
948 desired_capture_fps = video_fps;
950 cvReleaseCapture(&temp_cap);
953 //cout << avi_bg_path << endl;
954 cache_light = !redo_lighting;
956 glutReshapeWindow( video_width, video_height );
958 multi = new MultiGrab();
960 if( multi->init(avi_bg_path, video_width, video_height, v4l_device,
961 detect_width, detect_height, desired_capture_fps ) ==0)
963 cerr <<"Initialization error.\n";
964 return false;
968 artvert_struct artvert1 = {"Arrebato, 1980", image1, "Feb, 2009", "Iván Zulueta", "Polo", "Madrid, Spain"};
969 artvert_struct artvert2 = {"name2", image2, "2008", "simon innings", "Helmut Lang", "Parlance Avenue"};
970 artvert_struct artvert3 = {"name3", image3, "2008", "simon innings", "Loreal", "Parlance Avenue"};
971 artvert_struct artvert4 = {"name4", image4, "2008", "simon innings", "Hugo Boss", "Parlance Avenue"};
972 artvert_struct artvert5 = {"name5", image5, "2008", "simon innings", "Burger King", "Parlance Avenue"};
974 artverts[0] = artvert1;
975 artverts[1] = artvert2;
976 artverts[2] = artvert3;
977 artverts[3] = artvert4;
978 artverts[4] = artvert5;
981 // load geometry
982 loadOrTrain(0);
984 // try to load geom cache + start the run loop
985 geomCalibStart(!redo_geom);
987 // start detection
988 startDetectionThread( 1 /* priority, only if running as root */ );
990 last_frame_caught_time.SetNow();
991 frame_timer.SetNow();
993 // start serial
994 startSerialThread();
996 printf("init() finished\n");
997 return true;
1000 /*! The keyboard callback: reacts to '+' and '-' to change the viewed cam, 'q' exits.
1001 * 'd' turns on/off the dynamic lightmap update.
1002 * 'f' goes fullscreen.
1004 static void keyboard(unsigned char c, int x, int y)
1006 char old_button_state = button_state;
1007 const char* filename;
1008 switch (c)
1010 case 'n' :
1011 if (augment == 1)
1012 augment = 0;
1013 else
1014 augment = 1;
1015 case '+' :
1016 if (current_cam < multi->cams.size()-1)
1017 current_cam++;
1018 break;
1019 case '-':
1020 if (current_cam >= 1)
1021 current_cam--;
1022 break;
1023 case 'q':
1024 case 27 /*esc*/:
1025 exit(0);
1026 break;
1027 case 'l':
1028 dynamic_light = !dynamic_light;
1029 break;
1030 case 'd':
1031 delay_video = !delay_video;
1032 break;
1033 case 'a':
1034 if (avi_play == true)
1035 avi_play = false;
1036 else
1037 avi_play = true;
1038 case 'f':
1039 glutFullScreen();
1040 break;
1041 case 'k':
1042 track_kalman = !track_kalman;
1043 break;
1044 case 'S':
1045 show_status = !show_status;
1046 break;
1047 case 'p':
1048 show_profile_results = true;
1049 break;
1050 case 'P':
1051 FProfiler::Clear();
1052 break;
1053 case 'i':
1054 if (cnt >= NUMARTVERTS-1)
1055 cnt = 0;
1056 else
1057 cnt ++;
1058 cout << "we are on image " << cnt << endl;
1059 break;
1060 case '[':
1061 button_state |= BUTTON_RED;
1062 break;
1063 case ']':
1064 button_state |= BUTTON_GREEN;
1065 break;
1066 case '\\':
1067 button_state |= BUTTON_BLUE;
1068 break;
1070 default:
1071 break;
1074 if ( multi && show_status )
1076 planar_object_recognizer &detector(multi->cams[current_cam]->detector);
1077 bool something = true;
1078 switch (c)
1080 // detector settings
1081 case '1':
1082 detector.ransac_dist_threshold_ui*=1.02f;
1083 break;
1084 case '!':
1085 detector.ransac_dist_threshold_ui/=1.02f;
1086 break;
1087 case '2':
1088 detector.max_ransac_iterations_ui+=10;
1089 break;
1090 case '@':
1091 detector.max_ransac_iterations_ui-=10;
1092 break;
1093 case '3':
1094 detector.non_linear_refine_threshold_ui*=1.02f;
1095 break;
1096 case '#':
1097 detector.non_linear_refine_threshold_ui/=1.02f;
1098 break;
1099 case '4':
1100 detector.match_score_threshold_ui*=1.02f;
1101 break;
1102 case '$':
1103 detector.match_score_threshold_ui/=1.02f;
1104 break;
1105 case '5':
1106 detector.best_support_thresh_ui++;
1107 break;
1108 case '%':
1109 detector.best_support_thresh_ui--;
1110 break;
1111 case '6':
1112 detector.point_detector_tau_ui++;
1113 break;
1114 case '^':
1115 detector.point_detector_tau_ui--;
1116 break;
1117 case '7':
1118 matrix_tracker.increasePositionSmoothing();
1119 break;
1120 case '&':
1121 matrix_tracker.decreasePositionSmoothing();
1122 break;
1123 case '8':
1124 matrix_tracker.increasePositionZSmoothing();
1125 break;
1126 case '*':
1127 matrix_tracker.decreasePositionZSmoothing();
1128 break;
1129 case '9':
1130 matrix_tracker.increaseFramesBackRaw();
1131 break;
1132 case '(':
1133 matrix_tracker.decreaseFramesBackRaw();
1134 break;
1135 case '0':
1136 matrix_tracker.increaseFramesBackReturned();
1137 break;
1138 case ')':
1139 matrix_tracker.decreaseFramesBackReturned();
1140 break;
1143 default:
1144 something = false;
1145 break;
1147 if ( something )
1149 printf("%s\n", getSettingsString().c_str());
1154 glutPostRedisplay();
1156 if ( old_button_state != button_state )
1157 button_state_changed = true;
1160 static void keyboardReleased(unsigned char c, int x, int y)
1162 char old_button_state = button_state;
1163 switch ( c )
1165 case '[':
1166 button_state &= (BUTTON_GREEN | BUTTON_BLUE);
1167 break;
1168 case ']':
1169 button_state &= (BUTTON_RED | BUTTON_BLUE);
1170 break;
1171 case '\\':
1172 button_state &= (BUTTON_GREEN | BUTTON_RED);
1173 break;
1174 default:
1175 break;
1177 if ( old_button_state != button_state )
1178 button_state_changed = true;
1180 static void emptyWindow()
1182 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1185 int main(int argc, char *argv[])
1187 glutInit(&argc, argv);
1188 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
1189 //glutInitWindowSize(video_width,video_height); // hard set the init window size
1190 //glutInitWindowSize(800,450); // hard set the init window size
1193 glutDisplayFunc(emptyWindow);
1194 //glutReshapeFunc(reshape);
1195 //glutCreateWindow("The Artvertiser 0.4");
1196 glutMouseFunc(mouse);
1197 glutEntryFunc(entry);
1199 glutGameModeString("1024x768:16@60");
1200 glutEnterGameMode();
1201 glutSetCursor(GLUT_CURSOR_NONE);
1203 if (!init(argc,argv)) return -1;
1206 //ftglFont = new FTBufferFont("/usr/share/fonts/truetype/freefont/FreeMono.ttf");
1207 ftglFont = new FTBufferFont("fonts/FreeSans.ttf");
1208 ftglFont->FaceSize(12);
1209 ftglFont->CharMap(ft_encoding_unicode);
1211 //cvDestroyAllWindows();
1212 //cvWaitKey(0);
1214 glutKeyboardFunc(keyboard);
1215 glutKeyboardUpFunc(keyboardReleased);
1216 printf("*** entering glutMainLoop()\n");
1217 glutMainLoop();
1218 glutLeaveGameMode();
1219 return 0; /* ANSI C requires main to return int. */
1222 //!\brief Draw a frame contained in an IplTexture object on an OpenGL viewport.
1223 static bool drawBackground(IplTexture *tex)
1225 //printf("draw background\n");
1226 if (!tex || !tex->getIm()) return false;
1227 //printf("drawBackground: drawing frame with timestamp %f\n", raw_frame_timestamp.ToSeconds() );
1229 IplImage *im = tex->getIm();
1230 int w = im->width-1;
1231 int h = im->height-1;
1233 glMatrixMode(GL_PROJECTION);
1234 glLoadIdentity();
1235 glMatrixMode(GL_MODELVIEW);
1236 glLoadIdentity();
1238 glDisable(GL_BLEND);
1239 glDisable(GL_DEPTH_TEST);
1241 tex->loadTexture();
1243 glBegin(GL_QUADS);
1244 glColor4f(1,1,1,1);
1246 glTexCoord2f(tex->u(0), tex->v(0));
1247 glVertex2f(-1, 1);
1248 glTexCoord2f(tex->u(w), tex->v(0));
1249 glVertex2f(1, 1);
1250 glTexCoord2f(tex->u(w), tex->v(h));
1251 glVertex2f(1, -1);
1252 glTexCoord2f(tex->u(0), tex->v(h));
1253 glVertex2f(-1, -1);
1254 glEnd();
1256 tex->disableTexture();
1258 return true;
1261 /*! \brief Draw all the points
1264 static void drawDetectedPoints(int frame_width, int frame_height)
1266 if (!multi) return;
1268 planar_object_recognizer &detector(multi->cams[current_cam]->detector);
1270 glMatrixMode(GL_PROJECTION);
1271 glLoadIdentity();
1272 glOrtho(0, frame_width-1, frame_height-1, 0, -1, 1);
1274 glMatrixMode(GL_MODELVIEW);
1275 glLoadIdentity();
1277 glDisable(GL_BLEND);
1278 glDisable(GL_LIGHTING);
1279 glDisable(GL_DEPTH_TEST);
1282 glPointSize(2);
1283 glBegin(GL_POINTS);
1284 // draw all detected points
1285 glColor4f(0,1,0,1);
1286 for ( int i=0; detector.isReady() && i<detector.detected_point_number; ++i)
1288 keypoint& kp = detector.detected_points[i];
1289 int s = kp.scale;
1290 float x =PyrImage::convCoordf(kp.u, s, 0);
1291 float y =PyrImage::convCoordf(kp.v, s, 0);
1292 glVertex2f(x,y);
1294 // draw matching points red
1295 if ( detector.object_is_detected )
1297 glColor4f( 1, 0, 0, 1 );
1298 for (int i=0; i<detector.match_number; ++i)
1300 image_object_point_match * match = detector.matches+i;
1301 if (match->inlier)
1303 int s = (int)(match->image_point->scale);
1304 float x=PyrImage::convCoordf(match->image_point->u, s, 0);
1305 float y=PyrImage::convCoordf(match->image_point->v, s, 0);
1306 glVertex2f(x,y);
1310 glEnd();
1316 /*! \brief A draw callback during camera calibration
1318 * GLUT calls that function during camera calibration when repainting the
1319 * window is required.
1320 * During geometric calibration, no 3D is known: we just plot 2d points
1321 * where some feature points have been recognized.
1323 static void geomCalibDraw(void)
1325 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1327 glDisable(GL_LIGHTING);
1328 drawBackground(raw_frame_texture);
1330 if (!multi) return;
1332 IplImage *im = multi->cams[current_cam]->getLastProcessedFrame();
1333 planar_object_recognizer &detector(multi->cams[current_cam]->detector);
1334 if (!im) return;
1336 if (!detector.isReady()) return;
1337 detector.lock();
1339 glMatrixMode(GL_PROJECTION);
1340 glLoadIdentity();
1341 glOrtho(0, im->width-1, im->height-1, 0, -1, 1);
1343 glMatrixMode(GL_MODELVIEW);
1344 glLoadIdentity();
1346 glDisable(GL_BLEND);
1347 glDisable(GL_LIGHTING);
1348 glDisable(GL_DEPTH_TEST);
1350 if (detector.object_is_detected)
1352 glPointSize(2);
1353 glBegin(GL_POINTS);
1354 glColor4f(0,1,0,1);
1355 for (int i=0; i<detector.match_number; ++i)
1357 image_object_point_match * match = detector.matches+i;
1358 if (match->inlier)
1360 int s = (int)(match->image_point->scale);
1361 float x=PyrImage::convCoordf(match->image_point->u, s, 0);
1362 float y=PyrImage::convCoordf(match->image_point->v, s, 0);
1363 glVertex2f(x,y);
1366 glEnd();
1369 detector.unlock();
1371 glutSwapBuffers();
1374 /*!\brief Called when geometric calibration ends. It makes
1375 * sure that the CamAugmentation object is ready to work.
1377 static void geomCalibEnd()
1380 if (!multi->model.augm.LoadOptimalStructureFromFile("camera_c.txt", "camera_r_t.txt"))
1382 cout << "failed to load camera calibration.\n";
1383 exit(-1);
1385 glutIdleFunc(0);
1386 //glutDisplayFunc(0);
1387 delete calib;
1388 calib=0;
1391 /*! Called by GLUT during geometric calibration when there's nothing else to do.
1392 * This function grab frames from camera(s), run the 2D detection on every image,
1393 * and keep the result in memory for calibration. When enough homographies have
1394 * been detected, it tries to actually calibrate the cameras.
1396 static void geomCalibIdle(void)
1398 // detect the calibration object in every image
1399 // (this loop could be paralelized)
1400 int nbdet=0;
1401 for (int i=0; i<multi->cams.size(); ++i)
1403 bool dummy = false;
1404 if (multi->cams[i]->detect(dummy, dummy)) nbdet++;
1408 if(!raw_frame_texture) raw_frame_texture = new IplTexture;
1409 IplImage* raw_frame = raw_frame_texture->getImage();
1410 multi->cams[current_cam]->getLastDrawFrame( &raw_frame );
1411 raw_frame_texture->setImage(raw_frame);
1412 //raw_frame_texture->setImage(multi->cams[current_cam]->frame);
1414 if (nbdet>0)
1416 for (int i=0; i<multi->cams.size(); ++i)
1418 if (multi->cams[i]->detector.object_is_detected)
1420 add_detected_homography(i, multi->cams[i]->detector, *calib);
1422 else
1424 calib->AddHomography(i);
1427 geom_calib_nb_homography++;
1430 printf("geom calib: %.2f%%\n", 100.0f*geom_calib_nb_homography/150.0f );
1432 if (geom_calib_nb_homography>=150)
1434 if (calib->Calibrate(
1435 50, // max hom
1436 (multi->cams.size() > 1 ? 1:2), // padding or random
1437 (multi->cams.size() > 1 ? 0:3),
1438 1, // padding ratio 1/2
1441 0.0078125, //alpha
1442 0.9, //beta
1443 0.001953125,//gamma
1444 10, // iter
1445 0.05, //eps
1446 3 //postfilter eps
1449 calib->PrintOptimizedResultsToFile1();
1450 geomCalibEnd();
1451 start();
1452 return;
1455 glutPostRedisplay();
1458 /*!\brief Start geometric calibration. If the calibration can be loaded from disk,
1459 * continue directly with photometric calibration.
1461 static void geomCalibStart(bool cache)
1463 if (cache && multi->model.augm.LoadOptimalStructureFromFile("camera_c.txt", "camera_r_t.txt"))
1465 start();
1466 return;
1469 // construct a CamCalibration object and register all the cameras
1470 calib = new CamCalibration();
1472 for (int i=0; i<multi->cams.size(); ++i)
1474 calib->AddCamera(multi->cams[i]->width, multi->cams[i]->height);
1477 geom_calib_nb_homography=0;
1478 glutDisplayFunc(geomCalibDraw);
1479 glutIdleFunc(geomCalibIdle);
1484 static void drawAugmentation()
1487 // we know that im is not NULL already
1488 // IplImage *im = multi->model.image;
1490 //for ( int tracked_or_raw=0; tracked_or_raw<2; tracked_or_raw++ )
1493 // Fetch object -> image, world->image and world -> object matrices
1495 CvMat *world;
1496 /*if ( tracked_or_raw == 1 )
1498 // fetch from model:
1499 world = multi->model.augm.GetObjectToWorld();
1501 else*/
1504 // or fetch interpolated position
1505 world = cvCreateMat( 3, 4, CV_64FC1 );
1507 //printf(". now we want interpolated pose for %f\n", raw_frame_timestamp.ToSeconds() );
1508 if ( track_kalman )
1509 matrix_tracker.getInterpolatedPoseKalman( world,
1510 multi->cams[0]->getFrameIndexForTime( raw_frame_timestamp ) );
1511 else
1512 matrix_tracker.getInterpolatedPose( world, raw_frame_timestamp );
1515 /*// apply a scale factor
1516 float scalef = 1.0f;
1517 for ( int i=0; i<3; i++ )
1518 cvmSet(world, i, i, scalef*cvmGet( world, i, i ));*/
1521 // instead of this:
1522 /*CvMat *proj = multi->model.augm.GetProjectionMatrix(current_cam);
1523 CvMat *old_proj = multi->model.augm.GetProjectionMatrix(current_cam);*/
1524 // we make our own project matrix:
1525 // fetch the pre-projection matrix from model.augm
1526 CvMat* proj = multi->model.augm.GetPreProjectionMatrix(current_cam);
1527 // multiply by the object-to-world matrix
1528 CamCalibration::Mat3x4Mul( proj, world, proj );
1530 Mat3x4 moveObject, rot, obj2World, movedRT_;
1531 moveObject.setTranslate( multi->model.getImageWidth()/2, multi->model.getImageHeight()/2,
1532 -120*3/4);
1533 // apply rotation
1534 rot.setRotate(Vec3(1,0,0),2*M_PI*180.0/360.0);
1535 //rot.setIdentity();
1536 moveObject.mul(rot);
1537 //moveObject.scale
1539 CvMat cvMoveObject = cvMat(3,4,CV_64FC1, moveObject.m);
1540 CvMat movedRT=cvMat(3,4,CV_64FC1,movedRT_.m);
1543 // pose only during movement
1544 //if (pixel_shift >= 200 || !have_proj)
1545 if ( true )
1547 // memcpy or vectorisation speedup?
1548 for( int i = 0; i < 3; i++ )
1550 for( int j = 0; j < 4; j++ )
1552 a_proj[i][j] = cvmGet( proj, i, j );
1553 obj2World.m[i][j] = cvmGet(world, i, j);
1557 have_proj = 1;
1558 memcpy(old_a_proj, a_proj, sizeof(a_proj));
1560 else // copy last known good projection over current
1562 memcpy(a_proj, old_a_proj, sizeof(old_a_proj));
1564 // dump the matrix
1566 printf("found matrix: %8.4f %8.4f %8.4f %8.4f\n"
1567 " %8.4f %8.4f %8.4f %8.4f\n"
1568 " %8.4f %8.4f %8.4f %8.4f\n",
1569 a_proj[0][0], a_proj[0][1], a_proj[0][2], a_proj[0][3],
1570 a_proj[1][0], a_proj[1][1], a_proj[1][2], a_proj[1][3],
1571 a_proj[2][0], a_proj[2][1], a_proj[2][2], a_proj[2][3]
1572 );*/
1575 CamCalibration::Mat3x4Mul( world, &cvMoveObject, &movedRT);
1576 // translate into OpenGL PROJECTION and MODELVIEW matrices
1577 PerspectiveCamera c;
1578 //c.loadTdir(a_proj, multi->cams[0]->frame->width, multi->cams[0]->frame->height);
1579 c.loadTdir(a_proj, multi->cams[0]->detect_width, multi->cams[0]->detect_height );
1580 c.flip();
1581 c.setPlanes(100,1000000); // near/far clip planes
1582 cvReleaseMat(&proj);
1584 // must set the model view after drawing the background.
1585 c.setGlProjection();
1586 c.setGlModelView();
1588 /*! this is the beginning of prototype code for an occlusion mask built
1589 * by comparison of the tracked plane with that of the model image
1591 // create a copy of frame texture and warp to model image perspective
1592 CvPoint2D32f *c2 = new CvPoint2D32f[4];
1593 // update corner points of ROI in pixel space
1594 c2[0].x = cvRound(multi->cams[0]->detector.detected_u_corner1);
1595 c2[0].y = cvRound(multi->cams[0]->detector.detected_v_corner1);
1596 c2[1].x = cvRound(multi->cams[0]->detector.detected_u_corner2);
1597 c2[1].y = cvRound(multi->cams[0]->detector.detected_v_corner2);
1598 c2[2].x = cvRound(multi->cams[0]->detector.detected_u_corner3);
1599 c2[2].y = cvRound(multi->cams[0]->detector.detected_v_corner3);
1600 c2[3].x = cvRound(multi->cams[0]->detector.detected_u_corner4);
1601 c2[3].y = cvRound(multi->cams[0]->detector.detected_v_corner4);
1603 CvMat* mmat = cvCreateMat(3,3, CV_32FC1);
1604 IplImage *warped = cvCreateImage(cvSize(model_image->width, model_image->height), 8, 3);
1605 mmat = cvGetPerspectiveTransform(c2, c1, mmat);
1606 cvWarpPerspective(raw_frame_texture->getIm(), warped, mmat);
1607 cvReleaseMat(&mmat);
1609 // find difference between model image and frame
1610 IplImage *i1=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
1611 IplImage *i2=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
1612 IplImage *diff=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
1613 IplImage *display=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
1615 cvCvtColor(im, i1,CV_BGR2GRAY);
1616 cvCvtColor(warped, i2,CV_BGR2GRAY);
1617 cvAbsDiff(i2,i1,diff);
1618 cvThreshold(diff, display, 35, 255, CV_THRESH_BINARY);
1620 cvReleaseImage(&warped);
1621 cvSaveImage("checkdiff.png", display);
1624 /* circles at corners of ROI. useful for debugging.
1625 cvCircle(raw_frame_texture->getIm(), c1, 10, CV_RGB(255, 0, 0), 2);
1626 cvCircle(raw_frame_texture->getIm(), c2, 10, CV_RGB(255, 0, 0), 2);
1627 cvCircle(raw_frame_texture->getIm(), c3, 10, CV_RGB(255, 0, 0), 2);
1628 cvCircle(raw_frame_texture->getIm(), c4, 10, CV_RGB(255, 0, 0), 2);
1630 tex = new IplTexture;
1631 tex->setImage(raw_frame_texture->getIm());
1632 drawBackground(tex);
1637 #ifndef DEBUG_SHADER
1639 glEnable(GL_DEPTH_TEST);
1640 glEnable(GL_BLEND);
1641 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1642 glDisable(GL_CULL_FACE);
1643 // multiply texture colour by surface colour of poly
1644 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1645 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
1647 if (avi_play == true)
1649 IplImage *avi_frame = 0;
1650 IplImage *avi_image = 0;
1651 avi_frame = cvQueryFrame( avi_capture );
1652 avi_image = cvCreateImage(cvSize(video_width/2, video_height/2), 8, 3);
1653 cvResize(avi_frame, avi_image, 0);
1654 avi_image->origin = avi_frame->origin;
1655 GLenum format = IsBGR(avi_image->channelSeq) ? GL_BGR_EXT : GL_RGBA;
1657 if (!avi_play_init)
1659 glGenTextures(1, &imageID);
1660 glBindTexture(GL_TEXTURE_2D, imageID);
1661 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1662 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1663 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1664 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, avi_image->width, avi_image->height, 0, format, GL_UNSIGNED_BYTE, avi_image->imageData);
1665 avi_play_init=true;
1667 else
1669 glBindTexture(GL_TEXTURE_2D, imageID);
1670 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, avi_image->width, avi_image->height, format, GL_UNSIGNED_BYTE, avi_image->imageData);
1673 else
1675 if ( current_artvert_index >= 0 &&
1676 current_artvert_index < artvert_list.size() )
1678 glBindTexture(GL_TEXTURE_2D, imageID);
1679 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1680 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1681 IplImage* image = artvert_list.at(current_artvert_index).getArtvertImage();
1682 GLenum format = IsBGR(image->channelSeq) ? GL_BGR_EXT : GL_RGBA;
1683 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, format, GL_UNSIGNED_BYTE, image->imageData);
1687 glEnable(GL_TEXTURE_2D);
1689 glHint(GL_POLYGON_SMOOTH, GL_NICEST);
1690 glEnable(GL_POLYGON_SMOOTH);
1693 #ifndef DEBUG_SHADER
1694 // apply the object transformation matrix
1695 Mat3x4 w2e(c.getWorldToEyeMat());
1696 w2e.mul(moveObject);
1697 c.setWorldToEyeMat(w2e);
1698 c.setGlModelView();
1699 #endif
1701 if (multi->model.map.isReady())
1703 glDisable(GL_LIGHTING);
1704 #ifdef DEBUG_SHADER
1705 multi->model.map.enableShader(current_cam, world);
1706 #else
1707 multi->model.map.enableShader(current_cam, &movedRT);
1708 #endif
1712 glColor4f(1.0, 1.0, 1.0, fade);
1714 glBegin(GL_QUADS);
1715 glTexCoord2f(0, 0);
1716 glVertex3f(artvert_roi_vec[0], artvert_roi_vec[1], 0);
1717 glTexCoord2f(1, 0);
1718 glVertex3f(artvert_roi_vec[2], artvert_roi_vec[3], 0);
1719 glTexCoord2f(1, 1);
1720 glVertex3f(artvert_roi_vec[4], artvert_roi_vec[5], 0);
1721 glTexCoord2f(0, 1);
1722 glVertex3f(artvert_roi_vec[6], artvert_roi_vec[7], 0);
1723 glEnd();
1725 glDisable(GL_TEXTURE_2D);
1727 /*! 'label' is a boolean set by the right mouse button and toggles the
1728 * in-scene artvert label.
1731 if (label)
1733 glBegin(GL_LINE_LOOP);
1734 glColor3f(0.0, 1.0, 0.0);
1735 glVertex3f(roi_vec[0]-10, roi_vec[1]-10, 0);
1736 glVertex3f(roi_vec[2]+10, roi_vec[3]-10, 0);
1737 glVertex3f(roi_vec[4]+10, roi_vec[5]+10, 0);
1738 glVertex3f(roi_vec[6]-10, roi_vec[7]+10, 0);
1739 glVertex3f(roi_vec[0]-10, roi_vec[1]-10, 0);
1740 glEnd();
1742 glTranslatef(roi_vec[2]+12, roi_vec[3], 0);
1743 glRotatef(180, 1.0, 0.0, 0.0);
1744 glRotatef(-45, 0.0, 1.0, 0.0);
1745 glColor4f(0.0, 1.0, 0.0, 1);
1747 glBegin(GL_LINE_LOOP);
1748 glVertex3f(0, 10, -.2);
1749 glVertex3f(150, 10, -.2);
1750 glVertex3f(150, -60, -.2);
1751 glVertex3f(0, -60, -.2);
1752 glEnd();
1754 glColor4f(0.0, 1.0, 0.0, .5);
1756 glBegin(GL_QUADS);
1757 glVertex3f(0, 10, -.2);
1758 glVertex3f(150, 10, -.2);
1759 glVertex3f(150, -60, -.2);
1760 glVertex3f(0, -60, -.2);
1761 glEnd();
1763 // render the text in the label
1764 glColor4f(1.0, 1.0, 1.0, 1);
1765 ftglFont->Render(artverts[cnt].artvert);
1766 glTranslatef(0, -12, 0);
1767 ftglFont->Render(artverts[cnt].date);
1768 glTranslatef(0, -12, 0);
1769 ftglFont->Render(artverts[cnt].author);
1770 glTranslatef(0, -12, 0);
1771 ftglFont->Render(artverts[cnt].advert);
1772 glTranslatef(0, -12, 0);
1773 ftglFont->Render(artverts[cnt].street);
1776 #else
1777 #endif
1778 //glEnd();
1780 /*cvReleaseMat(&world);
1782 CvScalar c =cvGet2D(multi->model.image, multi->model.image->height/2, multi->model.image->width/2);
1783 glColor3d(c.val[2], c.val[1], c.val[0]);
1785 if (multi->model.map.isReady())
1786 multi->model.map.disableShader();
1787 else
1788 glDisable(GL_LIGHTING);*/
1790 if ( avi_play == true )
1792 cvReleaseImage(&avi_image);
1793 cvReleaseImage(&avi_frame);
1799 //#define DEBUG_SHADER
1800 /*! The paint callback during photometric calibration and augmentation. In this
1801 * case, we have access to 3D data. Thus, we can augment the calibration target
1802 * with cool stuff.
1804 static void draw()
1806 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1807 glDisable(GL_LIGHTING);
1809 drawBackground(raw_frame_texture);
1811 string cnt_str;
1812 stringstream cnt_out;
1813 cnt_out << cnt;
1814 cnt_str = cnt_out.str();
1816 //IplImage *pre_mask = cvCreateImage(cvSize(WIDTH, HEIGHT), 8, 3);
1818 if (!multi)
1819 return;
1821 int now = glutGet(GLUT_ELAPSED_TIME);
1822 /* elapsed time
1823 cout << now/1000.0 << endl;
1827 // fade
1828 double elapsed = frame_timer.Update();
1829 if ( frame_ok )
1831 last_frame_caught_time.SetNow();
1832 // increase fade
1833 if ( fade < (show_status?MAX_FADE_SHOW:MAX_FADE_NORMAL) )
1835 fade += (1.0f/SECONDS_LOST_FADE)*elapsed;
1837 else
1838 fade = show_status?MAX_FADE_SHOW:MAX_FADE_NORMAL;
1839 //printf("frame_ok: fade %f\n", fade );
1841 else
1843 FTime now;
1844 now.SetNow();
1845 double elapsed_since_last_caught = (now-last_frame_caught_time).ToSeconds();
1846 if ( elapsed_since_last_caught > SECONDS_LOST_TRACK )
1848 if ( fade > 0.0f )
1849 fade -= (1.0f/SECONDS_LOST_FADE)*elapsed;
1850 else
1851 fade = 0.0f;
1853 //printf("frame_ok: fade %f, elapsed since last caught %f\n", fade, elapsed_since_last_caught );
1855 //printf("frame %s, lost_count %f -> fade pct %4.2f, fade %4.2f\n", frame_ok?"y":"n", frame_lost_count, fade_pct, fade );
1857 // draw augmentation
1858 if ( fade > 0 && augment == 1)
1860 drawAugmentation();
1863 // calculate fps
1864 draw_fps = (draw_fps*7.0+1.0/elapsed)/8.0f;
1866 glLoadIdentity();
1867 // we need to setup a new projection matrix for the title font.
1868 glMatrixMode(GL_PROJECTION);
1869 glLoadIdentity();
1870 glMatrixMode(GL_MODELVIEW);
1871 glLoadIdentity();
1872 glTranslatef(-.98, 0.9, 0.0);
1873 glScalef(.003, .003, .003);
1874 ftglFont->FaceSize(32);
1875 glColor4f(1.0, 1.0, 1.0, 1);
1876 ftglFont->Render("the artvertiser 0.4");
1877 glTranslatef(0, -(video_height+30), 0);
1878 glColor4f(1.0, 1.0, 1.0, .6);
1879 //ftglFont->FaceSize(16);
1880 //ftglFont->Render(date(now).c_str());
1881 if (frame_ok == 1 and (now/1000)%2== 0)
1883 glTranslatef(video_width-295, video_height+35, 0);
1884 glColor4f(0.0, 1.0, 0.0, .8);
1885 glBegin(GL_TRIANGLES);
1886 glVertex3f(140, 0, 0);
1887 glVertex3f(150, 10, 0);
1888 glVertex3f(140, 20, 0);
1889 glEnd();
1890 glTranslatef(70, 5, 0);
1891 ftglFont->FaceSize(16);
1892 ftglFont->Render("tracking");
1895 // reset the ftgl font size for next pass
1896 ftglFont->FaceSize(12);
1899 if ( show_status )
1901 //printf("draw status\n");
1903 drawDetectedPoints( raw_frame_texture->getIm()->width, raw_frame_texture->getIm()->height );
1905 char detect_fps_string[256];
1906 sprintf(detect_fps_string, "draw fps: %4.2f\ndetection fps: %4.2f", draw_fps, detection_fps );
1907 drawGlutString( detect_fps_string, 1.0f, 0.2f, 0.2f, 0.7, 0.94 );
1909 // now status string
1910 string draw_string = status_string;
1911 if ( multi )
1913 // show detector settings
1914 draw_string += "\n" + getSettingsString();
1916 drawGlutString( draw_string.c_str(), 1.0f, 0.2f, 0.2f, 0.01f, 0.2f );
1919 drawMenu();
1922 glutSwapBuffers();
1923 cvReleaseImage(&image); // cleanup used image
1924 glFlush();
1929 void startSerialThread()
1931 pthread_attr_t thread_attr;
1932 pthread_attr_init(&thread_attr);
1933 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
1934 // launch the thread
1935 pthread_create( &serial_thread, &thread_attr, serialThreadFunc, NULL );
1936 serial_thread_is_running = true;
1937 pthread_attr_destroy( &thread_attr );
1940 void shutdownSerialThread()
1942 // kill the thread
1943 serial_thread_should_exit = true;
1944 void* ret;
1945 pthread_join( serial_thread, &ret );
1946 serial_thread_is_running = false;
1950 void* serialThreadFunc( void* data )
1952 // arduino vars
1953 int fd = 0;
1954 char serialport[256];
1955 int baudrate = B9600; // default
1956 char buf[256];
1957 char buf2[256];
1958 int rc,n;
1960 fd = serialport_init( "/dev/ttyUSB0", 9600 );
1961 printf("fd said %i, errno %i\n", fd, errno );
1963 while ( !serial_thread_should_exit )
1965 int read = serialport_read_until(fd, buf, '\n');
1966 //printf("read: %s then %s\n",buf2, buf);
1967 if ( (read==0) && strlen( buf ) >= 4 /*includes final \n*/ )
1969 bool button_red = (buf[0]=='1');
1970 bool button_green = (buf[1]=='1');
1971 bool button_blue = (buf[2]=='1');
1972 // printf("buttons: %s %s %s", button_red?"red":" ", button_green?"green":" ", button_blue?"blue":" ");
1973 // bitmapped, to access all 7 press combinations
1974 char new_button_state =
1975 ( button_green ? BUTTON_GREEN : 0 ) |
1976 ( button_blue ? BUTTON_BLUE : 0 ) |
1977 ( button_red ? BUTTON_RED : 0 );
1978 // deal with debounce
1979 if ( new_button_state != button_state )
1981 printf( "serialport read %s -> 0x%x (old was 0x%x)\n",
1982 buf, new_button_state, button_state );
1983 button_state_changed = true;
1984 button_state = new_button_state;
1987 usleep(3*1000);
1990 close(fd);
1993 static void startDetectionThread( int thread_priority )
1995 pthread_attr_t thread_attr;
1996 pthread_attr_init(&thread_attr);
1997 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
1998 // launch the thread
1999 pthread_create( &detection_thread, &thread_attr, detectionThreadFunc, NULL );
2000 if ( thread_priority > 0 )
2002 printf("attempting to set detection thread priority to %i\n", thread_priority );
2003 struct sched_param param;
2004 param.sched_priority = thread_priority;
2006 int res = pthread_setschedparam( detection_thread, SCHED_RR, &param );
2007 if ( res != 0 )
2009 fprintf(stderr,"pthread_setschedparam failed: %s\n",
2010 (res == ENOSYS) ? "ENOSYS" :
2011 (res == EINVAL) ? "EINVAL" :
2012 (res == ENOTSUP) ? "ENOTSUP" :
2013 (res == EPERM) ? "EPERM" :
2014 (res == ESRCH) ? "ESRCH" :
2015 "???"
2019 detection_thread_running = true;
2020 pthread_attr_destroy( &thread_attr );
2023 static void shutdownDetectionThread()
2025 // kill the thread
2026 detection_thread_should_exit = true;
2027 void* ret;
2028 pthread_join( detection_thread, &ret );
2029 detection_thread_running = false;
2032 static void* detectionThreadFunc( void* _data )
2035 FTime detection_thread_timer;
2036 detection_thread_timer.SetNow();
2038 while ( !detection_thread_should_exit )
2040 PROFILE_THIS_BLOCK("detection_thread");
2042 if ( new_artvert_requested )
2044 // no longer draw
2045 frame_ok = false;
2046 // go with the loading
2047 loadOrTrain(new_artvert_requested_index);
2048 new_artvert_requested = false;
2051 bool frame_retrieved = false;
2052 bool frame_retrieved_and_ok = multi->cams[0]->detect( frame_retrieved, frame_ok );
2053 if( frame_retrieved )
2055 double elapsed = detection_thread_timer.Update();
2056 detection_fps = (detection_fps*0.0 + (1.0/elapsed))/1.0;
2058 if ( !frame_retrieved_and_ok )
2060 PROFILE_THIS_BLOCK("sleep till next");
2061 usleep( 10000 );
2062 continue;
2065 if ( detection_thread_should_exit )
2066 break;
2068 multi->model.augm.Clear();
2069 if (multi->cams[0]->detector.object_is_detected)
2071 add_detected_homography(0, multi->cams[0]->detector, multi->model.augm);
2073 else
2075 multi->model.augm.AddHomography();
2078 frame_ok = multi->model.augm.Accomodate(4, 1e-4);
2080 if (frame_ok)
2082 // fetch surface normal in world coordinates
2083 CvMat *mat = multi->model.augm.GetObjectToWorld();
2084 float normal[3];
2085 for (int j=0; j<3; j++)
2086 normal[j] = cvGet2D(mat, j, 2).val[0];
2088 // continue to track
2089 if ( track_kalman )
2090 matrix_tracker.addPoseKalman( mat, multi->cams[0]->getFrameIndexForTime(
2091 multi->cams[0]->getLastProcessedFrameTimestamp() ) );
2092 else
2093 matrix_tracker.addPose( mat, multi->cams[0]->getLastProcessedFrameTimestamp() );
2095 cvReleaseMat(&mat);
2100 printf("detection thread exiting\n");
2102 pthread_exit(0);
2106 /*! GLUT calls this during photometric calibration or augmentation phase when
2107 * there's nothing else to do. This function does the 2D detection and bundle
2108 * adjusts the 3D pose of the calibration pattern.
2110 static void idle()
2114 // detect the calibration object in every image
2115 // (this loop could be paralelized)
2116 int nbdet=1;
2118 if(!raw_frame_texture) raw_frame_texture = new IplTexture;
2120 PROFILE_SECTION_PUSH("getting last frame");
2122 if ( delay_video )
2124 IplImage* captured_frame;
2125 multi->cams[current_cam]->getLastDrawFrame( &captured_frame, &raw_frame_timestamp );
2127 static list< pair<IplImage*, FTime> > frameRingBuffer;
2128 while ( frameRingBuffer.size()<VIDEO_DELAY_FRAMES )
2130 IplImage* first_frame = cvCreateImage( cvGetSize( captured_frame ), captured_frame->depth, captured_frame->nChannels );
2131 cvCopy( captured_frame, first_frame );
2132 frameRingBuffer.push_back( make_pair( first_frame, raw_frame_timestamp ) );
2135 IplImage* ringbuffered = frameRingBuffer.front().first;
2136 cvCopy( captured_frame, ringbuffered );
2137 frameRingBuffer.push_back( make_pair( ringbuffered, raw_frame_timestamp ) );
2139 frameRingBuffer.pop_front();
2141 IplImage* raw_frame = frameRingBuffer.front().first;
2142 raw_frame_timestamp = frameRingBuffer.front().second;
2144 raw_frame_texture->setImage(raw_frame);
2146 else
2148 IplImage* raw_frame = raw_frame_texture->getImage();
2149 multi->cams[current_cam]->getLastDrawFrame( &raw_frame, &raw_frame_timestamp );
2150 raw_frame_texture->setImage(raw_frame);
2153 PROFILE_SECTION_POP();
2155 updateMenu();
2157 //doDetection();
2159 glutPostRedisplay();
2161 PROFILE_SECTION_POP();
2163 if ( show_profile_results )
2165 // show profiler output
2166 printf("showing results\n");
2167 FProfiler::Display( FProfiler::SORT_TIME/*SORT_EXECUTION*/ );
2168 show_profile_results = false;
2172 //! Starts photometric calibration.
2173 static void start()
2175 glutIdleFunc(idle);
2176 glutDisplayFunc(draw);
2182 // menu
2183 bool menu_show = false;
2184 FTime menu_timer;
2185 bool menu_is_showing = false;
2186 bool menu_up = false;
2187 bool menu_down = false;
2188 void updateMenu();
2189 void drawMenu();
2191 int menu_index = 0;
2193 void updateMenu()
2195 // update timer
2196 if ( menu_is_showing )
2198 FTime now;
2199 now.SetNow();
2200 if ( (now-menu_timer).ToSeconds() > MENU_HIDE_TIME )
2202 menu_is_showing = false;
2206 if ( !button_state_changed )
2207 return;
2209 printf("menu sees new button state: %s %s %s\n",
2210 button_state&BUTTON_RED?"red":" ",
2211 button_state&BUTTON_GREEN?"green":" ",
2212 button_state&BUTTON_BLUE?"blue":" ");
2214 // clear changed flag
2215 button_state_changed = false;
2217 if ( ( button_state == BUTTON_GREEN ) && !menu_is_showing )
2219 menu_is_showing = true;
2220 menu_timer.SetNow();
2221 if ( menu_index >= artvert_list.size() )
2222 menu_index = artvert_list.size()-1;
2223 // done
2224 return;
2227 // only process rest of buttons if menu is showing
2228 if ( !menu_is_showing )
2229 return;
2231 // navigation
2232 if( button_state == BUTTON_BLUE )
2234 menu_index++;
2235 if ( menu_index >= artvert_list.size() )
2236 menu_index = 0;
2237 menu_timer.SetNow();
2239 if ( button_state == BUTTON_RED )
2241 menu_index--;
2242 if ( menu_index < 0 )
2243 menu_index = artvert_list.size()-1;
2244 menu_timer.SetNow();
2247 // accept?
2248 if ( button_state == BUTTON_GREEN )
2251 new_artvert_requested_index = menu_index;
2252 new_artvert_requested = true;
2254 menu_is_showing = false;
2264 void drawMenu()
2266 if ( !menu_is_showing )
2268 // draw switching text?
2269 if ( new_artvert_switching_in_progress )
2271 glMatrixMode(GL_PROJECTION);
2272 glLoadIdentity();
2273 glMatrixMode(GL_MODELVIEW);
2274 glLoadIdentity();
2275 glTranslatef(-.8, 0.65, 0.0);
2276 glScalef(.003, .003, .003);
2277 ftglFont->FaceSize(24);
2278 glColor4f(0.0, 1.0, 0.0, 1);
2280 ftglFont->Render("changing artvert...");
2284 return;
2287 // draw menu header
2289 // draw loop
2290 glMatrixMode(GL_PROJECTION);
2291 glLoadIdentity();
2292 glMatrixMode(GL_MODELVIEW);
2293 glLoadIdentity();
2294 glEnable(GL_BLEND);
2295 glTranslatef(-.85, 0.65, 0.0);
2296 glScalef(.003, .003, .003);
2297 ftglFont->FaceSize(24);
2298 glColor4f(.25, 1.0, 0.0, 1);
2300 ftglFont->Render("Select artvert:");
2301 glTranslatef( 0, -26, 0 );
2303 for ( int i=0; i<artvert_list.size(); i++ )
2305 string advert = artvert_list[i].advert;
2306 string name = artvert_list[i].name;
2307 string artist = artvert_list[i].artist;
2308 string line = string(" ") + advert + " : '" + name + "' by " + artist;
2310 if ( i == menu_index )
2312 glColor4f( 1,0.37,0,1 );
2314 else
2316 glColor4f( .25f, 1.f, 0.0f, 1 );
2318 ftglFont->Render(line.c_str());
2319 glTranslatef(0, -26, 0 );
2328 //EOF