Merge branch 'master' into dev_train_binocular_ui
[The-Artvertiser.git] / artvertiser / artvertiser.cpp
blobbbacab0865500b9cbe640a049db3863021332d81
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 __APPLE__
79 #define HAVE_APPLE_OPENGL_FRAMEWORK
80 #endif
81 #ifdef HAVE_APPLE_OPENGL_FRAMEWORK
82 #include <GLUT/glut.h>
83 #else
84 #include <GL/glut.h>
85 #endif
87 #include "/usr/include/freetype2/freetype/config/ftconfig.h"
88 #include <FTGL/ftgl.h>
90 #include "FProfiler/FProfiler.h"
92 // framerate counter
93 #include "framerate.h"
95 #include <list>
96 using namespace std;
98 // matrix tracker
99 #include "MatrixTracker/MatrixTracker.h"
101 #define IsRGB(s) ((s[0] == 'R') && (s[1] == 'G') && (s[2] == 'B'))
102 #define IsBGR(s) ((s[0] == 'B') && (s[1] == 'G') && (s[2] == 'R'))
104 #ifndef GL_CLAMP_TO_BORDER
105 #define GL_CLAMP_TO_BORDER 0x812D
106 #endif
107 #define GL_MIRROR_CLAMP_EXT 0x8742
109 #define DEFAULT_WIDTH 640
110 #define DEFAULT_HEIGHT 480
111 #define DEFAULT_V4LDEVICE 0
113 #define NUMARTVERTS 5
115 // buttons via arduino
116 // button_state is bitmapped so as to handle multiple button presses at once
117 char button_state = 0;
118 bool button_state_changed = false;
119 const char BUTTON_RED = 0x01;
120 const char BUTTON_GREEN = 0x02;
121 const char BUTTON_BLUE = 0x04;
122 // serial comms
123 int serialport_init(const char* serialport, int baud);
124 int serialport_read_until(int fd, char* buf, char until);
125 bool serial_thread_should_exit = false;
126 bool serial_thread_is_running = false;
127 pthread_t serial_thread;
128 void startSerialThread();
129 void shutdownSerialThread();
130 void* serialThreadFunc( void* );
131 // running on binoculars?
132 bool running_on_binoculars = false;
135 // we continue tracking for 1 second, then fade for 3
136 static const float SECONDS_LOST_TRACK = 0.5f;
137 static const float SECONDS_LOST_FADE = 1.0f;
138 static const float MAX_FADE_SHOW = 0.9f;
139 static const float MAX_FADE_NORMAL = 1.0f;
141 static const int DEFAULT_CAPTURE_FPS = 20;
143 //#define WIDTH 320
144 //#define HEIGHT 240
146 MultiGrab *multi=0;
147 CamCalibration *calib=0;
148 CvPoint projPts[4];
149 IplTexture *raw_frame_texture=0;
150 FTime raw_frame_timestamp;
151 IplTexture *tex=0;
152 IplImage *image = 0;
153 CvCapture *capture = 0;
154 CvCapture *avi_capture = 0;
155 IplImage *avi_image = 0;
156 IplImage *avi_frame = 0;
157 //IplImage *model_image = 0;
158 IplImage *this_frame = 0;
159 IplImage *last_frame = 0;
160 IplImage *diff= 0;
161 IplImage *bit_frame= 0;
163 int v4l_device = DEFAULT_V4LDEVICE;
164 int video_width = DEFAULT_WIDTH;
165 int video_height = DEFAULT_HEIGHT;
166 int detect_width = DEFAULT_WIDTH;
167 int detect_height = DEFAULT_HEIGHT;
168 int desired_capture_fps = DEFAULT_CAPTURE_FPS;
170 // load some images. hard-coded for know until i get the path parsing together.
171 IplImage *image1 = cvLoadImage("artvert1.png");
172 IplImage *image2 = cvLoadImage("artvert2.png");
173 IplImage *image3 = cvLoadImage("artvert3.png");
174 IplImage *image4 = cvLoadImage("artvert4.png");
175 IplImage *image5 = cvLoadImage("artvert5.png");
176 IplImage *fallback_artvert_image = cvLoadImage("artvert1.png");
178 // matrix tracker
179 MatrixTracker matrix_tracker;
181 // define a container struct for each artvert
182 struct artvert_struct
184 const char *artvert;
185 IplImage *image;
186 const char *date;
187 const char *author;
188 const char *advert;
189 const char *street;
192 typedef vector<artvert_struct> artverts_list;
193 artverts_list artverts(5);
195 // create a vector for the images and initialise it.
196 typedef vector<IplImage> imgVec;
198 bool frame_ok=false;
199 bool cache_light=false;
200 bool dynamic_light=false;
201 bool sphere_object=false;
202 bool avi_play=false;
203 bool avi_play_init=false;
204 bool lbutton_down = false;
205 bool label = false;
207 bool track_kalman = false;
208 bool delay_video = true;
209 // how many frames to delay the video
210 static const int VIDEO_DELAY_FRAMES=7;
212 double a_proj[3][4];
213 double old_a_proj[3][4];
214 float fade = 0.0;
215 FTime last_frame_caught_time;
216 FTime frame_timer;
217 float draw_fps;
218 int difference = 0;
219 int have_proj = 0;
220 int nb_light_measures=0;
221 int geom_calib_nb_homography;
222 int current_cam = 0;
223 int avi_init = 0;
224 int augment = 1;
225 int cnt=0;
227 vector<int> roi_vec;
228 CvPoint2D32f *c1 = new CvPoint2D32f[4];
229 vector<int> artvert_roi_vec;
231 char *image_path;
233 class Artvert
235 public:
236 Artvert() {
237 artvert_image=0;
238 model_file="model.bmp";
239 artvert_image_file="artvert1.png";
240 artvert_is_movie= false;
241 artist = "unknown artist";
242 advert = "unknown advert";
243 name = "unnamed artvert";
244 avi_capture = NULL;
245 avi_image = NULL;
246 avi_play_init = false;
248 ~Artvert()
250 if ( artvert_image )
251 cvReleaseImage( &artvert_image );
252 if ( avi_capture )
253 cvReleaseCapture( &avi_capture );
254 if ( avi_image )
255 cvReleaseImage( &avi_image );
258 IplImage* getArtvertImage()
260 if ( artvert_is_movie )
263 if ( !avi_capture )
265 avi_capture = cvCaptureFromAVI( artvert_movie_file.c_str() );
266 avi_play_init = false;
269 IplImage *avi_frame = 0;
270 avi_frame = cvQueryFrame( avi_capture );
271 if ( avi_frame == 0 )
272 return fallback_artvert_image;
273 if ( avi_image == 0 )
274 avi_image = cvCreateImage( cvGetSize(avi_frame), avi_frame->depth, avi_frame->nChannels );
275 cvCopy( avi_frame, avi_image );
276 avi_image->origin = avi_frame->origin;
277 GLenum format = IsBGR(avi_image->channelSeq) ? GL_BGR_EXT : GL_RGBA;
279 if (!avi_play_init)
281 glGenTextures(1, &imageID);
282 glBindTexture(GL_TEXTURE_2D, imageID);
283 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
284 avi_play_init=true;
286 return avi_image;
288 else
290 if ( !artvert_image )
292 printf("loading artvert image '%s'\n", artvert_image_file.c_str() );
293 artvert_image = cvLoadImage( artvert_image_file.c_str() );
295 if ( !artvert_image )
297 fprintf(stderr, "couldn't load artvert image '%s'\n", artvert_image_file.c_str() );
298 artvert_image = fallback_artvert_image;
300 return artvert_image;
304 string model_file;
305 string artvert_image_file;
306 string artist;
307 string advert;
308 string name;
309 bool artvert_is_movie;
310 string artvert_movie_file;
311 private:
312 CvCapture* avi_capture;
313 IplImage* avi_image;
314 bool avi_play_init;
315 GLuint imageID;
317 IplImage* artvert_image;
320 vector< Artvert > artvert_list;
321 bool new_artvert_switching_in_progress = false;
322 int current_artvert_index=-1;
323 bool new_artvert_requested = false;
324 int new_artvert_requested_index = 0;
325 vector< bool > model_file_needs_training;
326 #include "ofxXmlSettings/ofxXmlSettings.h"
328 // detection thread
329 pthread_t detection_thread;
330 double detection_fps = 0.0;
331 static void shutdownDetectionThread();
332 static void startDetectionThread( int thread_priority = 0 /* only takes effect if root */ );
333 static void* detectionThreadFunc( void* _data );
334 bool detection_thread_should_exit = false;
335 bool detection_thread_running = false;
338 static void start();
339 static void geomCalibStart(bool cache);
341 // initialise a couple of fonts.
342 CvFont font, fontbold;
344 // Gl format for texturing
345 GLenum format;
346 GLuint imageID;
348 // ftgl font setup
349 static FTFont *ftglFont;
351 // interface
352 bool show_status = false;
353 bool show_profile_results = false;
354 string status_string = "";
356 // menu
357 // hide after 5s
358 #define MENU_HIDE_TIME 5.0f
359 bool menu_show = false;
360 FTime menu_timer;
361 bool menu_is_showing = false;
362 void updateMenu();
363 void drawMenu();
365 /* use this to read paths from the file system
367 string getExtension(const string &file)
369 string::size_type dot = file.rfind('.');
370 string lcpath = file;
371 string suffix;
372 transform(lcpath.begin(), lcpath.end(), lcpath.begin(), tolower);
373 if(dot != string::npos)
375 suffix = lcpath.substr(dot + 1);
377 return suffix;
383 string getSettingsString()
385 planar_object_recognizer &detector(multi->cams[current_cam]->detector);
386 static char detector_settings_string[2048];
387 sprintf( detector_settings_string, "1.ransac dist %4.2f 2.iter %i detected points %i match count %i,\n"
388 "3.refine %6.4f 4.score %6.4f 5.best_support thresh %2i 6.tau %2i\n"
389 "smoothing: 7.position %5.3f 8.position_z %5.3f \n frames back: 9.raw %2i 0.returned %2i",
390 detector.ransac_dist_threshold_ui,
391 detector.max_ransac_iterations_ui,
392 detector.detected_point_number,
393 detector.match_number,
394 detector.non_linear_refine_threshold_ui,
395 detector.match_score_threshold_ui,
396 detector.best_support_thresh_ui,
397 detector.point_detector_tau_ui,
398 matrix_tracker.getPositionSmoothing(),
399 matrix_tracker.getPositionZSmoothing(),
400 matrix_tracker.getFramesBackRaw(),
401 matrix_tracker.getFramesBackReturned() );
403 return detector_settings_string;
407 std::string date(int now)
409 time_t rawtime;
410 struct tm *timeinfo;
411 char tBuffer[80];
412 time ( &rawtime );
413 timeinfo = localtime ( &rawtime );
414 strftime (tBuffer,80,"%I:%M:%S:%p, %d %b %Y",timeinfo);
415 string timeStr;
416 stringstream _timeStr;
417 _timeStr << tBuffer;
418 timeStr = _timeStr.str();
419 return timeStr;
423 // mouse input using GLUT.
424 void entry(int state)
426 if (state == GLUT_ENTERED)
427 cout << "Mouse Entered" << endl;
428 else
429 cout << "Mouse Left" << endl;
432 void mouse(int button, int state, int x, int y)
434 if (button == GLUT_RIGHT_BUTTON)
436 if (state == GLUT_DOWN)
438 label = true;
441 else
443 label = false;
446 else if (button == GLUT_LEFT_BUTTON)
448 if (state == GLUT_DOWN)
450 lbutton_down = true;
451 if (cnt >= NUMARTVERTS-1)
453 cnt = 0;
455 else
457 cnt ++;
460 else
461 lbutton_down = false;
463 cout << "we are on image " << cnt << endl;
466 // text drawing function
467 static void drawText(IplImage *img, const char *text, CvPoint point, CvFont *font, CvScalar colour, double size)
469 cvInitFont( font, CV_FONT_HERSHEY_DUPLEX, size, size, 0, 1, CV_AA);
470 //cvInitFont( font, CV_FONT_HERSHEY_PLAIN, size, size, 0, 1, CV_AA);
471 cvPutText(img, text, point, font, colour);
474 // read in ROI coords from txt file into vector.
475 static vector<int> readROI(const char *filename)
477 cout << filename << endl;
478 string l;
479 ifstream roi(filename);
480 vector<int> v;
481 int coord;
482 char s[10];
483 char *s1;
484 int lines = 0;
486 if (roi.is_open())
488 while (!roi.eof())
490 getline(roi, l);
491 strcpy(s, l.c_str());
492 s1 = strtok(s, " ");
494 while (s1 != NULL)
496 //roi_vec.push_back(atoi(s1));
497 v.push_back(atoi(s1));
498 s1 = strtok(NULL, " ,");
501 roi.close();
503 else
505 cout << "roi file not found" << endl;
507 return v;
510 //! GLUT callback on window size change
511 static void reshape(int width, int height)
513 //GLfloat h = (GLfloat) height / (GLfloat) width;
514 int winWidth = video_width;
515 int winHeight = video_height;
516 glViewport(0,0,winWidth, winHeight);
517 glutPostRedisplay();
520 //! Print a command line help and exit.
521 static void usage(const char *s)
523 cerr << "usage:\n" << s
524 << " [-m <model image>] [-m <model image>] ... \n "
525 " [-ml <model images file .xml>] [-r] [-t] [-g] [-a <path>] [-l] [-vd <num>] [-vs <width> <height>]\n"
526 " [-ds <width> <height>] [-fps <fps>]\n\n"
527 //" -a <path> specify path to AVI (instead of v4l device)\n"
528 " -b <path> specify path to AVI (instead of v4l device), ignores -vs\n"
529 " -m specifies model image (may be used multiple times)\n"
530 " -ml <path> load model images from <path> (respects additional -m paths)\n"
531 " -r do not load any data\n"
532 " -t train a new classifier\n"
533 " -g recompute geometric calibration\n"
534 " -a <path> load an AVI movie as an artvert\n"
535 " -i <path> load an image as an artvert\n"
536 " -l rebuild irradiance map from scratch\n"
537 " -vd <num> V4L video device number (0-n)\n"
538 " -vs <width> <height> video width and height (default 640x480)\n"
539 " -ds <width> <height> frame size at which to run the detector (default to video width/height)\n"
540 " -fps <fps> desired fps at which to run the image capture\n\n";
541 exit(1);
546 void exit_handler()
548 printf("in exit_handler\n");
549 // shutdown serial
550 if ( serial_thread_is_running )
552 printf("stopping serial\n");
553 shutdownSerialThread();
555 // shutdown detection thread
556 if ( detection_thread_running )
558 printf("stopping detection\n");
559 shutdownDetectionThread();
561 // shutdown capture
562 if ( multi )
564 printf("stopping multithread capture\n");
565 multi->cams[0]->shutdownMultiThreadCapture();
567 // delete cameras
568 if ( multi )
570 printf("deleteing multi\n");
571 delete multi;
575 int serialport_init(const char* serialport, int baud)
577 struct termios toptions;
578 int fd;
580 //fprintf(stderr,"init_serialport: opening port %s @ %d bps\n",
581 // serialport,baud);
583 fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
584 if (fd == -1) {
585 perror("init_serialport: Unable to open port ");
586 return -1;
589 if (tcgetattr(fd, &toptions) < 0) {
590 perror("init_serialport: Couldn't get term attributes");
591 return -1;
593 speed_t brate = baud; // let you override switch below if needed
594 switch(baud) {
595 case 4800: brate=B4800; break;
596 case 9600: brate=B9600; break;
597 #ifdef B14400
598 case 14400: brate=B14400; break;
599 #endif
600 case 19200: brate=B19200; break;
601 #ifdef B28800
602 case 28800: brate=B28800; break;
603 #endif
604 case 38400: brate=B38400; break;
605 case 57600: brate=B57600; break;
606 case 115200: brate=B115200; break;
608 cfsetispeed(&toptions, brate);
609 cfsetospeed(&toptions, brate);
611 // 8N1
612 toptions.c_cflag &= ~PARENB;
613 toptions.c_cflag &= ~CSTOPB;
614 toptions.c_cflag &= ~CSIZE;
615 toptions.c_cflag |= CS8;
616 // no flow control
617 toptions.c_cflag &= ~CRTSCTS;
619 toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
620 toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
622 toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
623 toptions.c_oflag &= ~OPOST; // make raw
625 // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
626 toptions.c_cc[VMIN] = 0;
627 toptions.c_cc[VTIME] = 20;
629 if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
630 perror("init_serialport: Couldn't set term attributes");
631 return -1;
634 return fd;
638 // arduino serial port read
639 int serialport_read_until(int fd, char* buf, char until)
641 char b[1];
642 int i=0;
643 // 1000ms timeout
644 int timeout = 1000*1000;
645 do {
646 int n = read(fd, b, 1); // read a char at a time
647 if( n==0||n==-1 ) {
648 timeout -= 100;
649 usleep( 100 ); // wait 100 usec try again
650 continue;
652 buf[i] = b[0]; i++;
653 } while( b[0] != until && timeout > 0 );
655 if ( timeout<=0 )
656 fprintf(stderr, "serialport_read_until timed out\n");
658 buf[i] = 0; // null terminate the string
659 return 0;
663 bool loadOrTrain( int new_index )
665 // fetch data
666 if ( new_index < 0 || new_index >= artvert_list.size() )
668 fprintf(stderr,"loadOrTrain: invalid index %i (artvert_list has %i members)\n", new_index, (int)artvert_list.size() );
670 return false;
673 // switching model..
674 new_artvert_switching_in_progress = true;
675 bool wants_training = model_file_needs_training[new_index];
676 string model_file = artvert_list[new_index].model_file;
677 // do we need to switch, or can we use the same model file?
678 if ( ( current_artvert_index < 0 || current_artvert_index >= artvert_list.size() ) ||
679 model_file != artvert_list[current_artvert_index].model_file )
681 // load
682 bool trained = multi->loadOrTrainCache( wants_training, model_file.c_str(), running_on_binoculars );
683 if ( !trained )
685 // fail
686 current_artvert_index = -1;
687 new_artvert_switching_in_progress = false;
688 return false;
692 // copy char model_file before munging with strcat
693 char s[1024];
694 strcpy (s, model_file.c_str());
695 strcat(s, ".roi");
696 roi_vec = readROI(s);
698 strcpy( s, model_file.c_str() );
699 strcat(s, ".artvertroi");
700 artvert_roi_vec = readROI(s);
701 if ( artvert_roi_vec.empty() )
703 // use roi_vec
704 artvert_roi_vec.insert( artvert_roi_vec.begin(), roi_vec.begin(), roi_vec.end() );
707 // load model_image for use with diffing, later
708 // model_image = cvLoadImage(model_file.c_str());
710 c1[0].x = roi_vec[0];
711 c1[0].y = roi_vec[1];
712 c1[1].x = roi_vec[2];
713 c1[1].y = roi_vec[3];
714 c1[2].x = roi_vec[4];
715 c1[2].y = roi_vec[5];
716 c1[3].x = roi_vec[6];
717 c1[3].y = roi_vec[7];
720 // update current index
721 current_artvert_index = new_index;
723 // no longer switching
724 new_artvert_switching_in_progress = false;
726 return true;
732 /*!\brief Initialize everything
734 * - Parse the command line
735 * - Initialize all the cameras
736 * - Load or interactively build a model, with its classifier.
737 * - Set the GLUT callbacks for geometric calibration or, if already done, for photometric calibration.
740 static bool init( int argc, char** argv )
742 // register exit function
743 atexit( &exit_handler );
745 // dump opencv version
746 printf("built with opencv version %s\n", CV_VERSION );
748 // more from before init should be moved here
749 bool redo_lighting=false;
750 bool redo_training = false;
751 bool redo_geom = false;
752 char *avi_bg_path="";
753 bool got_ds = false;
754 bool got_fps = false;
755 bool video_source_is_avi = false;
756 char *model_file_list_file = NULL;
758 // parse command line
759 for (int i=1; i<argc; i++)
761 if (strcmp(argv[i], "-m") ==0)
763 if (i==argc-1)
764 usage(argv[0]);
765 Artvert a;
766 a.model_file = argv[i+1];
767 a.advert = "cmdline "+a.model_file;
768 // store
769 artvert_list.push_back( a );
770 printf(" -m: adding model image '%s'\n", argv[i+1] );
771 i++;
773 else if ( strcmp(argv[i], "-binoc")==0 )
775 running_on_binoculars = true;
776 printf(" -binoc: running on binoculars\n");
778 else if ( strcmp(argv[i], "-ml" )== 0 )
780 if ( i==argc-1)
781 usage(argv[0]);
782 model_file_list_file = argv[i+1];
783 printf(" -ml: loading model image list from '%s'\n", argv[i+1] );
784 i++;
786 else if (strcmp(argv[i], "-r")==0)
788 redo_geom=redo_training=redo_lighting=true;
790 else if (strcmp(argv[i], "-g")==0)
792 redo_geom=true;
794 else if (strcmp(argv[i], "-l")==0)
796 redo_lighting=true;
798 else if (strcmp(argv[i], "-t")==0)
800 redo_training=true;
801 printf( "-t: redoing training\n");
803 else if (strcmp(argv[i], "-a")==0)
805 avi_capture=cvCaptureFromAVI(argv[i+1]);
806 avi_play=true;
808 else if (strcmp(argv[i], "-i")==0)
810 IplImage *image1 = cvLoadImage(argv[i]+1);
812 else if (strcmp(argv[i], "-b")==0)
814 video_source_is_avi = true;
815 avi_bg_path=argv[i+1];
816 printf(" -b: loading from avi '%s'\n", avi_bg_path );
818 else if (strcmp(argv[i], "-i")==0)
820 image_path=argv[i+1];
822 else if ( strcmp(argv[i], "-fps")==0 )
824 desired_capture_fps=atoi(argv[i+1]);
825 got_fps = true;
826 i++;
828 else if (strcmp(argv[i], "-vd")==0)
830 v4l_device=atoi(argv[i+1]);
831 printf(" -vd: using v4l device %i\n", v4l_device);
832 i++;
834 else if (strcmp(argv[i], "-vs")==0)
836 video_width=atoi(argv[i+1]);
837 video_height=atoi(argv[i+2]);
838 if ( !got_ds )
840 // also set detect size (if not already set)
841 detect_width = video_width;
842 detect_height = video_height;
844 printf(" -vs: video size is %ix%i\n", video_width, video_height );
845 if ( video_width == 0 || video_height == 0 )
847 usage(argv[0]);
848 exit(1);
850 i+=2;
852 else if ( strcmp(argv[i], "-ds")==0 )
854 detect_width = atoi(argv[i+1]);
855 detect_height = atoi(argv[i+2]);
856 printf(" -ds: detection frame size is %ix%i\n", detect_width, detect_height );
857 if ( detect_width == 0 || detect_height == 0 )
859 usage(argv[0]);
860 exit(1);
862 got_ds = true;
863 i+=2;
865 else if (argv[i][0]=='-')
867 usage(argv[0]);
873 // read model files from model_file_list_file
874 if ( model_file_list_file != NULL )
876 // try to open
877 ofxXmlSettings data;
878 data.loadFile( model_file_list_file );
880 if ( data.getNumTags( "artverts" ) == 1 )
882 data.pushTag( "artverts" );
883 int num_filenames = data.getNumTags( "advert" );
884 printf(" -ml: opened %s, %i adverts\n", model_file_list_file, num_filenames );
885 for ( int i=0; i<num_filenames; i++ )
887 data.pushTag("advert", i);
888 Artvert a;
889 a.model_file = data.getValue( "model_filename", "model.bmp" );
890 a.advert = data.getValue( "advert", "unknown advert" );
891 int num_artverts = data.getNumTags( "artvert" );
892 printf(" -ml: got advert, model file '%s', advert '%s', %i artverts\n", a.model_file.c_str(), a.advert.c_str(), num_artverts );
893 for ( int j=0; j<num_artverts; j++ )
895 data.pushTag("artvert", j );
896 a.name = data.getValue( "name", "unnamed" );
897 a.artist = data.getValue( "artist", "unknown artist" );
898 if ( data.getNumTags("movie_filename") != 0 )
900 // load a movie
901 a.artvert_is_movie = true;
902 a.artvert_movie_file = data.getValue("movie_filename", "artvertmovie1.mp4" );
904 else
906 // load an image
907 a.artvert_image_file = data.getValue( "image_filename", "artvert1.png" );
909 printf(" %i: %s:%s:%s\n", j, a.name.c_str(), a.artist.c_str(),
910 a.artvert_is_movie?(a.artvert_movie_file+"( movie)").c_str() : a.artvert_image_file.c_str() );
912 artvert_list.push_back( a );
913 data.popTag();
915 data.popTag();
917 data.popTag();
919 else
921 printf(" -ml: error reading '%s': couldn't find 'artverts' tag\n", model_file_list_file );
925 // check if model file list is empty
926 if ( artvert_list.empty() )
928 // add default
929 Artvert a;
930 artvert_list.push_back( a );
933 // set up training flags
934 for ( int i=0; i<artvert_list.size(); i++ )
935 model_file_needs_training.push_back( redo_training );
937 // check for video size arg if necessary
938 if ( video_source_is_avi )
940 // try to read from video
941 CvCapture* temp_cap = cvCaptureFromAVI(avi_bg_path);
942 video_width = (int)cvGetCaptureProperty( temp_cap, CV_CAP_PROP_FRAME_WIDTH );
943 video_height = (int)cvGetCaptureProperty( temp_cap, CV_CAP_PROP_FRAME_HEIGHT );
944 int video_fps = (int)cvGetCaptureProperty( temp_cap, CV_CAP_PROP_FPS );
945 printf(" -b: read video width/height %i/%i from avi (ignoring -vs)\n", video_width, video_height );
946 if ( !got_ds )
948 detect_width = video_width;
949 detect_height = video_height;
951 if ( !got_fps )
953 desired_capture_fps = video_fps;
955 cvReleaseCapture(&temp_cap);
958 //cout << avi_bg_path << endl;
959 cache_light = !redo_lighting;
961 glutReshapeWindow( video_width, video_height );
963 multi = new MultiGrab();
965 if( multi->init(avi_bg_path, video_width, video_height, v4l_device,
966 detect_width, detect_height, desired_capture_fps ) ==0)
968 cerr <<"Initialization error.\n";
969 return false;
973 artvert_struct artvert1 = {"Arrebato, 1980", image1, "Feb, 2009", "Iván Zulueta", "Polo", "Madrid, Spain"};
974 artvert_struct artvert2 = {"name2", image2, "2008", "simon innings", "Helmut Lang", "Parlance Avenue"};
975 artvert_struct artvert3 = {"name3", image3, "2008", "simon innings", "Loreal", "Parlance Avenue"};
976 artvert_struct artvert4 = {"name4", image4, "2008", "simon innings", "Hugo Boss", "Parlance Avenue"};
977 artvert_struct artvert5 = {"name5", image5, "2008", "simon innings", "Burger King", "Parlance Avenue"};
979 artverts[0] = artvert1;
980 artverts[1] = artvert2;
981 artverts[2] = artvert3;
982 artverts[3] = artvert4;
983 artverts[4] = artvert5;
986 // load geometry
987 loadOrTrain(0);
989 // try to load geom cache + start the run loop
990 geomCalibStart(!redo_geom);
992 // start detection
993 startDetectionThread( 1 /* priority, only if running as root */ );
995 last_frame_caught_time.SetNow();
996 frame_timer.SetNow();
998 // start serial
999 startSerialThread();
1001 printf("init() finished\n");
1002 return true;
1005 /*! The keyboard callback: reacts to '+' and '-' to change the viewed cam, 'q' exits.
1006 * 'd' turns on/off the dynamic lightmap update.
1007 * 'f' goes fullscreen.
1009 static void keyboard(unsigned char c, int x, int y)
1011 char old_button_state = button_state;
1012 const char* filename;
1013 switch (c)
1015 case 'n' :
1016 if (augment == 1)
1017 augment = 0;
1018 else
1019 augment = 1;
1020 case '+' :
1021 if (current_cam < multi->cams.size()-1)
1022 current_cam++;
1023 break;
1024 case '-':
1025 if (current_cam >= 1)
1026 current_cam--;
1027 break;
1028 case 'q':
1029 case 27 /*esc*/:
1030 exit(0);
1031 break;
1032 case 'l':
1033 dynamic_light = !dynamic_light;
1034 break;
1035 case 'd':
1036 delay_video = !delay_video;
1037 break;
1038 case 'a':
1039 if (avi_play == true)
1040 avi_play = false;
1041 else
1042 avi_play = true;
1043 case 'f':
1044 glutFullScreen();
1045 break;
1046 case 'k':
1047 track_kalman = !track_kalman;
1048 break;
1049 case 'S':
1050 show_status = !show_status;
1051 break;
1052 case 'p':
1053 show_profile_results = true;
1054 break;
1055 case 'P':
1056 FProfiler::Clear();
1057 break;
1058 case 'i':
1059 if (cnt >= NUMARTVERTS-1)
1060 cnt = 0;
1061 else
1062 cnt ++;
1063 cout << "we are on image " << cnt << endl;
1064 break;
1065 case '[':
1066 button_state |= BUTTON_RED;
1067 break;
1068 case ']':
1069 button_state |= BUTTON_GREEN;
1070 break;
1071 case '\\':
1072 button_state |= BUTTON_BLUE;
1073 break;
1075 default:
1076 break;
1079 if ( multi && show_status )
1081 planar_object_recognizer &detector(multi->cams[current_cam]->detector);
1082 bool something = true;
1083 switch (c)
1085 // detector settings
1086 case '1':
1087 detector.ransac_dist_threshold_ui*=1.02f;
1088 break;
1089 case '!':
1090 detector.ransac_dist_threshold_ui/=1.02f;
1091 break;
1092 case '2':
1093 detector.max_ransac_iterations_ui+=10;
1094 break;
1095 case '@':
1096 detector.max_ransac_iterations_ui-=10;
1097 break;
1098 case '3':
1099 detector.non_linear_refine_threshold_ui*=1.02f;
1100 break;
1101 case '#':
1102 detector.non_linear_refine_threshold_ui/=1.02f;
1103 break;
1104 case '4':
1105 detector.match_score_threshold_ui*=1.02f;
1106 break;
1107 case '$':
1108 detector.match_score_threshold_ui/=1.02f;
1109 break;
1110 case '5':
1111 detector.best_support_thresh_ui++;
1112 break;
1113 case '%':
1114 detector.best_support_thresh_ui--;
1115 break;
1116 case '6':
1117 detector.point_detector_tau_ui++;
1118 break;
1119 case '^':
1120 detector.point_detector_tau_ui--;
1121 break;
1122 case '7':
1123 matrix_tracker.increasePositionSmoothing();
1124 break;
1125 case '&':
1126 matrix_tracker.decreasePositionSmoothing();
1127 break;
1128 case '8':
1129 matrix_tracker.increasePositionZSmoothing();
1130 break;
1131 case '*':
1132 matrix_tracker.decreasePositionZSmoothing();
1133 break;
1134 case '9':
1135 matrix_tracker.increaseFramesBackRaw();
1136 break;
1137 case '(':
1138 matrix_tracker.decreaseFramesBackRaw();
1139 break;
1140 case '0':
1141 matrix_tracker.increaseFramesBackReturned();
1142 break;
1143 case ')':
1144 matrix_tracker.decreaseFramesBackReturned();
1145 break;
1148 default:
1149 something = false;
1150 break;
1152 if ( something )
1154 printf("%s\n", getSettingsString().c_str());
1159 glutPostRedisplay();
1161 if ( old_button_state != button_state )
1162 button_state_changed = true;
1165 static void keyboardReleased(unsigned char c, int x, int y)
1167 char old_button_state = button_state;
1168 switch ( c )
1170 case '[':
1171 button_state &= (BUTTON_GREEN | BUTTON_BLUE);
1172 break;
1173 case ']':
1174 button_state &= (BUTTON_RED | BUTTON_BLUE);
1175 break;
1176 case '\\':
1177 button_state &= (BUTTON_GREEN | BUTTON_RED);
1178 break;
1179 default:
1180 break;
1182 if ( old_button_state != button_state )
1183 button_state_changed = true;
1185 static void emptyWindow()
1187 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1190 int main(int argc, char *argv[])
1192 glutInit(&argc, argv);
1193 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
1194 //glutInitWindowSize(video_width,video_height); // hard set the init window size
1195 //glutInitWindowSize(800,450); // hard set the init window size
1198 glutDisplayFunc(emptyWindow);
1200 #ifdef __APPLE__
1201 glutReshapeFunc(reshape);
1202 glutCreateWindow("The Artvertiser 0.4");
1203 #endif
1204 glutMouseFunc(mouse);
1205 glutEntryFunc(entry);
1207 #ifndef __APPLE__
1208 glutGameModeString("1024x768:16@60");
1209 glutEnterGameMode();
1210 glutSetCursor(GLUT_CURSOR_NONE);
1211 #endif
1213 if (!init(argc,argv)) return -1;
1216 //ftglFont = new FTBufferFont("/usr/share/fonts/truetype/freefont/FreeMono.ttf");
1217 ftglFont = new FTBufferFont("fonts/FreeSans.ttf");
1218 ftglFont->FaceSize(12);
1219 ftglFont->CharMap(ft_encoding_unicode);
1221 //cvDestroyAllWindows();
1222 //cvWaitKey(0);
1224 glutKeyboardFunc(keyboard);
1225 glutKeyboardUpFunc(keyboardReleased);
1226 glutMainLoop();
1227 glutLeaveGameMode();
1228 return 0; /* ANSI C requires main to return int. */
1231 //!\brief Draw a frame contained in an IplTexture object on an OpenGL viewport.
1232 static bool drawBackground(IplTexture *tex)
1234 //printf("draw background\n");
1235 if (!tex || !tex->getIm()) return false;
1236 //printf("drawBackground: drawing frame with timestamp %f\n", raw_frame_timestamp.ToSeconds() );
1238 IplImage *im = tex->getIm();
1239 int w = im->width-1;
1240 int h = im->height-1;
1242 glMatrixMode(GL_PROJECTION);
1243 glLoadIdentity();
1244 glMatrixMode(GL_MODELVIEW);
1245 glLoadIdentity();
1247 glDisable(GL_BLEND);
1248 glDisable(GL_DEPTH_TEST);
1250 tex->loadTexture();
1252 glBegin(GL_QUADS);
1253 glColor4f(1,1,1,1);
1255 glTexCoord2f(tex->u(0), tex->v(0));
1256 glVertex2f(-1, 1);
1257 glTexCoord2f(tex->u(w), tex->v(0));
1258 glVertex2f(1, 1);
1259 glTexCoord2f(tex->u(w), tex->v(h));
1260 glVertex2f(1, -1);
1261 glTexCoord2f(tex->u(0), tex->v(h));
1262 glVertex2f(-1, -1);
1263 glEnd();
1265 tex->disableTexture();
1267 return true;
1270 /*! \brief Draw all the points
1273 static void drawDetectedPoints(int frame_width, int frame_height)
1275 if (!multi) return;
1277 planar_object_recognizer &detector(multi->cams[current_cam]->detector);
1279 glMatrixMode(GL_PROJECTION);
1280 glLoadIdentity();
1281 glOrtho(0, frame_width-1, frame_height-1, 0, -1, 1);
1283 glMatrixMode(GL_MODELVIEW);
1284 glLoadIdentity();
1286 glDisable(GL_BLEND);
1287 glDisable(GL_LIGHTING);
1288 glDisable(GL_DEPTH_TEST);
1291 glPointSize(2);
1292 glBegin(GL_POINTS);
1293 // draw all detected points
1294 glColor4f(0,1,0,1);
1295 for ( int i=0; detector.isReady() && i<detector.detected_point_number; ++i)
1297 keypoint& kp = detector.detected_points[i];
1298 int s = kp.scale;
1299 float x =PyrImage::convCoordf(kp.u, s, 0);
1300 float y =PyrImage::convCoordf(kp.v, s, 0);
1301 glVertex2f(x,y);
1303 // draw matching points red
1304 if ( detector.object_is_detected )
1306 glColor4f( 1, 0, 0, 1 );
1307 for (int i=0; i<detector.match_number; ++i)
1309 image_object_point_match * match = detector.matches+i;
1310 if (match->inlier)
1312 int s = (int)(match->image_point->scale);
1313 float x=PyrImage::convCoordf(match->image_point->u, s, 0);
1314 float y=PyrImage::convCoordf(match->image_point->v, s, 0);
1315 glVertex2f(x,y);
1319 glEnd();
1325 /*! \brief A draw callback during camera calibration
1327 * GLUT calls that function during camera calibration when repainting the
1328 * window is required.
1329 * During geometric calibration, no 3D is known: we just plot 2d points
1330 * where some feature points have been recognized.
1332 static void geomCalibDraw(void)
1334 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1336 glDisable(GL_LIGHTING);
1337 drawBackground(raw_frame_texture);
1339 if (!multi) return;
1341 IplImage *im = multi->cams[current_cam]->getLastProcessedFrame();
1342 planar_object_recognizer &detector(multi->cams[current_cam]->detector);
1343 if (!im) return;
1345 if (!detector.isReady()) return;
1346 detector.lock();
1348 glMatrixMode(GL_PROJECTION);
1349 glLoadIdentity();
1350 glOrtho(0, im->width-1, im->height-1, 0, -1, 1);
1352 glMatrixMode(GL_MODELVIEW);
1353 glLoadIdentity();
1355 glDisable(GL_BLEND);
1356 glDisable(GL_LIGHTING);
1357 glDisable(GL_DEPTH_TEST);
1359 if (detector.object_is_detected)
1361 glPointSize(2);
1362 glBegin(GL_POINTS);
1363 glColor4f(0,1,0,1);
1364 for (int i=0; i<detector.match_number; ++i)
1366 image_object_point_match * match = detector.matches+i;
1367 if (match->inlier)
1369 int s = (int)(match->image_point->scale);
1370 float x=PyrImage::convCoordf(match->image_point->u, s, 0);
1371 float y=PyrImage::convCoordf(match->image_point->v, s, 0);
1372 glVertex2f(x,y);
1375 glEnd();
1378 detector.unlock();
1380 glutSwapBuffers();
1383 /*!\brief Called when geometric calibration ends. It makes
1384 * sure that the CamAugmentation object is ready to work.
1386 static void geomCalibEnd()
1389 if (!multi->model.augm.LoadOptimalStructureFromFile("camera_c.txt", "camera_r_t.txt"))
1391 cout << "failed to load camera calibration.\n";
1392 exit(-1);
1394 glutIdleFunc(0);
1395 //glutDisplayFunc(0);
1396 delete calib;
1397 calib=0;
1400 /*! Called by GLUT during geometric calibration when there's nothing else to do.
1401 * This function grab frames from camera(s), run the 2D detection on every image,
1402 * and keep the result in memory for calibration. When enough homographies have
1403 * been detected, it tries to actually calibrate the cameras.
1405 static void geomCalibIdle(void)
1407 // detect the calibration object in every image
1408 // (this loop could be paralelized)
1409 int nbdet=0;
1410 for (int i=0; i<multi->cams.size(); ++i)
1412 bool dummy = false;
1413 if (multi->cams[i]->detect(dummy, dummy)) nbdet++;
1417 if(!raw_frame_texture) raw_frame_texture = new IplTexture;
1418 IplImage* raw_frame = raw_frame_texture->getImage();
1419 multi->cams[current_cam]->getLastDrawFrame( &raw_frame );
1420 raw_frame_texture->setImage(raw_frame);
1421 //raw_frame_texture->setImage(multi->cams[current_cam]->frame);
1423 if (nbdet>0)
1425 for (int i=0; i<multi->cams.size(); ++i)
1427 if (multi->cams[i]->detector.object_is_detected)
1429 add_detected_homography(i, multi->cams[i]->detector, *calib);
1431 else
1433 calib->AddHomography(i);
1436 geom_calib_nb_homography++;
1439 printf("geom calib: %.2f%%\n", 100.0f*geom_calib_nb_homography/150.0f );
1441 if (geom_calib_nb_homography>=150)
1443 if (calib->Calibrate(
1444 50, // max hom
1445 (multi->cams.size() > 1 ? 1:2), // padding or random
1446 (multi->cams.size() > 1 ? 0:3),
1447 1, // padding ratio 1/2
1450 0.0078125, //alpha
1451 0.9, //beta
1452 0.001953125,//gamma
1453 10, // iter
1454 0.05, //eps
1455 3 //postfilter eps
1458 calib->PrintOptimizedResultsToFile1();
1459 geomCalibEnd();
1460 start();
1461 return;
1464 glutPostRedisplay();
1467 /*!\brief Start geometric calibration. If the calibration can be loaded from disk,
1468 * continue directly with photometric calibration.
1470 static void geomCalibStart(bool cache)
1472 if (cache && multi->model.augm.LoadOptimalStructureFromFile("camera_c.txt", "camera_r_t.txt"))
1474 start();
1475 return;
1478 // construct a CamCalibration object and register all the cameras
1479 calib = new CamCalibration();
1481 for (int i=0; i<multi->cams.size(); ++i)
1483 calib->AddCamera(multi->cams[i]->width, multi->cams[i]->height);
1486 geom_calib_nb_homography=0;
1487 glutDisplayFunc(geomCalibDraw);
1488 glutIdleFunc(geomCalibIdle);
1493 static void drawAugmentation()
1496 // we know that im is not NULL already
1497 // IplImage *im = multi->model.image;
1499 //for ( int tracked_or_raw=0; tracked_or_raw<2; tracked_or_raw++ )
1502 // Fetch object -> image, world->image and world -> object matrices
1504 CvMat *world;
1505 /*if ( tracked_or_raw == 1 )
1507 // fetch from model:
1508 world = multi->model.augm.GetObjectToWorld();
1510 else*/
1513 // or fetch interpolated position
1514 world = cvCreateMat( 3, 4, CV_64FC1 );
1516 //printf(". now we want interpolated pose for %f\n", raw_frame_timestamp.ToSeconds() );
1517 if ( track_kalman )
1518 matrix_tracker.getInterpolatedPoseKalman( world,
1519 multi->cams[0]->getFrameIndexForTime( raw_frame_timestamp ) );
1520 else
1521 matrix_tracker.getInterpolatedPose( world, raw_frame_timestamp );
1524 /*// apply a scale factor
1525 float scalef = 1.0f;
1526 for ( int i=0; i<3; i++ )
1527 cvmSet(world, i, i, scalef*cvmGet( world, i, i ));*/
1530 // instead of this:
1531 /*CvMat *proj = multi->model.augm.GetProjectionMatrix(current_cam);
1532 CvMat *old_proj = multi->model.augm.GetProjectionMatrix(current_cam);*/
1533 // we make our own project matrix:
1534 // fetch the pre-projection matrix from model.augm
1535 CvMat* proj = multi->model.augm.GetPreProjectionMatrix(current_cam);
1536 // multiply by the object-to-world matrix
1537 CamCalibration::Mat3x4Mul( proj, world, proj );
1539 Mat3x4 moveObject, rot, obj2World, movedRT_;
1540 moveObject.setTranslate( multi->model.getImageWidth()/2, multi->model.getImageHeight()/2,
1541 -120*3/4);
1542 // apply rotation
1543 rot.setRotate(Vec3(1,0,0),2*M_PI*180.0/360.0);
1544 //rot.setIdentity();
1545 moveObject.mul(rot);
1546 //moveObject.scale
1548 CvMat cvMoveObject = cvMat(3,4,CV_64FC1, moveObject.m);
1549 CvMat movedRT=cvMat(3,4,CV_64FC1,movedRT_.m);
1552 // pose only during movement
1553 //if (pixel_shift >= 200 || !have_proj)
1554 if ( true )
1556 // memcpy or vectorisation speedup?
1557 for( int i = 0; i < 3; i++ )
1559 for( int j = 0; j < 4; j++ )
1561 a_proj[i][j] = cvmGet( proj, i, j );
1562 obj2World.m[i][j] = cvmGet(world, i, j);
1566 have_proj = 1;
1567 memcpy(old_a_proj, a_proj, sizeof(a_proj));
1569 else // copy last known good projection over current
1571 memcpy(a_proj, old_a_proj, sizeof(old_a_proj));
1573 // dump the matrix
1575 printf("found matrix: %8.4f %8.4f %8.4f %8.4f\n"
1576 " %8.4f %8.4f %8.4f %8.4f\n"
1577 " %8.4f %8.4f %8.4f %8.4f\n",
1578 a_proj[0][0], a_proj[0][1], a_proj[0][2], a_proj[0][3],
1579 a_proj[1][0], a_proj[1][1], a_proj[1][2], a_proj[1][3],
1580 a_proj[2][0], a_proj[2][1], a_proj[2][2], a_proj[2][3]
1581 );*/
1584 CamCalibration::Mat3x4Mul( world, &cvMoveObject, &movedRT);
1585 // translate into OpenGL PROJECTION and MODELVIEW matrices
1586 PerspectiveCamera c;
1587 //c.loadTdir(a_proj, multi->cams[0]->frame->width, multi->cams[0]->frame->height);
1588 c.loadTdir(a_proj, multi->cams[0]->detect_width, multi->cams[0]->detect_height );
1589 c.flip();
1590 c.setPlanes(100,1000000); // near/far clip planes
1591 cvReleaseMat(&proj);
1593 // must set the model view after drawing the background.
1594 c.setGlProjection();
1595 c.setGlModelView();
1597 /*! this is the beginning of prototype code for an occlusion mask built
1598 * by comparison of the tracked plane with that of the model image
1600 // create a copy of frame texture and warp to model image perspective
1601 CvPoint2D32f *c2 = new CvPoint2D32f[4];
1602 // update corner points of ROI in pixel space
1603 c2[0].x = cvRound(multi->cams[0]->detector.detected_u_corner1);
1604 c2[0].y = cvRound(multi->cams[0]->detector.detected_v_corner1);
1605 c2[1].x = cvRound(multi->cams[0]->detector.detected_u_corner2);
1606 c2[1].y = cvRound(multi->cams[0]->detector.detected_v_corner2);
1607 c2[2].x = cvRound(multi->cams[0]->detector.detected_u_corner3);
1608 c2[2].y = cvRound(multi->cams[0]->detector.detected_v_corner3);
1609 c2[3].x = cvRound(multi->cams[0]->detector.detected_u_corner4);
1610 c2[3].y = cvRound(multi->cams[0]->detector.detected_v_corner4);
1612 CvMat* mmat = cvCreateMat(3,3, CV_32FC1);
1613 IplImage *warped = cvCreateImage(cvSize(model_image->width, model_image->height), 8, 3);
1614 mmat = cvGetPerspectiveTransform(c2, c1, mmat);
1615 cvWarpPerspective(raw_frame_texture->getIm(), warped, mmat);
1616 cvReleaseMat(&mmat);
1618 // find difference between model image and frame
1619 IplImage *i1=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
1620 IplImage *i2=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
1621 IplImage *diff=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
1622 IplImage *display=cvCreateImage(cvSize(im->width,im->height),im->depth,1);
1624 cvCvtColor(im, i1,CV_BGR2GRAY);
1625 cvCvtColor(warped, i2,CV_BGR2GRAY);
1626 cvAbsDiff(i2,i1,diff);
1627 cvThreshold(diff, display, 35, 255, CV_THRESH_BINARY);
1629 cvReleaseImage(&warped);
1630 cvSaveImage("checkdiff.png", display);
1633 /* circles at corners of ROI. useful for debugging.
1634 cvCircle(raw_frame_texture->getIm(), c1, 10, CV_RGB(255, 0, 0), 2);
1635 cvCircle(raw_frame_texture->getIm(), c2, 10, CV_RGB(255, 0, 0), 2);
1636 cvCircle(raw_frame_texture->getIm(), c3, 10, CV_RGB(255, 0, 0), 2);
1637 cvCircle(raw_frame_texture->getIm(), c4, 10, CV_RGB(255, 0, 0), 2);
1639 tex = new IplTexture;
1640 tex->setImage(raw_frame_texture->getIm());
1641 drawBackground(tex);
1646 #ifndef DEBUG_SHADER
1648 glEnable(GL_DEPTH_TEST);
1649 glEnable(GL_BLEND);
1650 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1651 glDisable(GL_CULL_FACE);
1652 // multiply texture colour by surface colour of poly
1653 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1654 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
1656 if (avi_play == true)
1658 IplImage *avi_frame = 0;
1659 IplImage *avi_image = 0;
1660 avi_frame = cvQueryFrame( avi_capture );
1661 avi_image = cvCreateImage(cvSize(video_width/2, video_height/2), 8, 3);
1662 cvResize(avi_frame, avi_image, 0);
1663 avi_image->origin = avi_frame->origin;
1664 GLenum format = IsBGR(avi_image->channelSeq) ? GL_BGR_EXT : GL_RGBA;
1666 if (!avi_play_init)
1668 glGenTextures(1, &imageID);
1669 glBindTexture(GL_TEXTURE_2D, imageID);
1670 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1671 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1672 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1673 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, avi_image->width, avi_image->height, 0, format, GL_UNSIGNED_BYTE, avi_image->imageData);
1674 avi_play_init=true;
1676 else
1678 glBindTexture(GL_TEXTURE_2D, imageID);
1679 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, avi_image->width, avi_image->height, format, GL_UNSIGNED_BYTE, avi_image->imageData);
1682 else
1684 if ( current_artvert_index >= 0 &&
1685 current_artvert_index < artvert_list.size() )
1687 glBindTexture(GL_TEXTURE_2D, imageID);
1688 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1689 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1690 IplImage* image = artvert_list.at(current_artvert_index).getArtvertImage();
1691 GLenum format = IsBGR(image->channelSeq) ? GL_BGR_EXT : GL_RGBA;
1692 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, format, GL_UNSIGNED_BYTE, image->imageData);
1696 glEnable(GL_TEXTURE_2D);
1698 glHint(GL_POLYGON_SMOOTH, GL_NICEST);
1699 glEnable(GL_POLYGON_SMOOTH);
1702 #ifndef DEBUG_SHADER
1703 // apply the object transformation matrix
1704 Mat3x4 w2e(c.getWorldToEyeMat());
1705 w2e.mul(moveObject);
1706 c.setWorldToEyeMat(w2e);
1707 c.setGlModelView();
1708 #endif
1710 if (multi->model.map.isReady())
1712 glDisable(GL_LIGHTING);
1713 #ifdef DEBUG_SHADER
1714 multi->model.map.enableShader(current_cam, world);
1715 #else
1716 multi->model.map.enableShader(current_cam, &movedRT);
1717 #endif
1721 glColor4f(1.0, 1.0, 1.0, fade);
1723 glBegin(GL_QUADS);
1724 glTexCoord2f(0, 0);
1725 glVertex3f(artvert_roi_vec[0], artvert_roi_vec[1], 0);
1726 glTexCoord2f(1, 0);
1727 glVertex3f(artvert_roi_vec[2], artvert_roi_vec[3], 0);
1728 glTexCoord2f(1, 1);
1729 glVertex3f(artvert_roi_vec[4], artvert_roi_vec[5], 0);
1730 glTexCoord2f(0, 1);
1731 glVertex3f(artvert_roi_vec[6], artvert_roi_vec[7], 0);
1732 glEnd();
1734 glDisable(GL_TEXTURE_2D);
1736 /*! 'label' is a boolean set by the right mouse button and toggles the
1737 * in-scene artvert label.
1740 if (label)
1742 glBegin(GL_LINE_LOOP);
1743 glColor3f(0.0, 1.0, 0.0);
1744 glVertex3f(roi_vec[0]-10, roi_vec[1]-10, 0);
1745 glVertex3f(roi_vec[2]+10, roi_vec[3]-10, 0);
1746 glVertex3f(roi_vec[4]+10, roi_vec[5]+10, 0);
1747 glVertex3f(roi_vec[6]-10, roi_vec[7]+10, 0);
1748 glVertex3f(roi_vec[0]-10, roi_vec[1]-10, 0);
1749 glEnd();
1751 glTranslatef(roi_vec[2]+12, roi_vec[3], 0);
1752 glRotatef(180, 1.0, 0.0, 0.0);
1753 glRotatef(-45, 0.0, 1.0, 0.0);
1754 glColor4f(0.0, 1.0, 0.0, 1);
1756 glBegin(GL_LINE_LOOP);
1757 glVertex3f(0, 10, -.2);
1758 glVertex3f(150, 10, -.2);
1759 glVertex3f(150, -60, -.2);
1760 glVertex3f(0, -60, -.2);
1761 glEnd();
1763 glColor4f(0.0, 1.0, 0.0, .5);
1765 glBegin(GL_QUADS);
1766 glVertex3f(0, 10, -.2);
1767 glVertex3f(150, 10, -.2);
1768 glVertex3f(150, -60, -.2);
1769 glVertex3f(0, -60, -.2);
1770 glEnd();
1772 // render the text in the label
1773 glColor4f(1.0, 1.0, 1.0, 1);
1774 ftglFont->Render(artverts[cnt].artvert);
1775 glTranslatef(0, -12, 0);
1776 ftglFont->Render(artverts[cnt].date);
1777 glTranslatef(0, -12, 0);
1778 ftglFont->Render(artverts[cnt].author);
1779 glTranslatef(0, -12, 0);
1780 ftglFont->Render(artverts[cnt].advert);
1781 glTranslatef(0, -12, 0);
1782 ftglFont->Render(artverts[cnt].street);
1785 #else
1786 #endif
1787 //glEnd();
1789 /*cvReleaseMat(&world);
1791 CvScalar c =cvGet2D(multi->model.image, multi->model.image->height/2, multi->model.image->width/2);
1792 glColor3d(c.val[2], c.val[1], c.val[0]);
1794 if (multi->model.map.isReady())
1795 multi->model.map.disableShader();
1796 else
1797 glDisable(GL_LIGHTING);*/
1799 if ( avi_play == true )
1801 cvReleaseImage(&avi_image);
1802 cvReleaseImage(&avi_frame);
1808 //#define DEBUG_SHADER
1809 /*! The paint callback during photometric calibration and augmentation. In this
1810 * case, we have access to 3D data. Thus, we can augment the calibration target
1811 * with cool stuff.
1813 static void draw()
1815 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1816 glDisable(GL_LIGHTING);
1818 if ( multi->model.isInteractiveTrainBinocularsRunning() )
1819 multi->model.interactiveTrainBinocularsDraw();
1820 else
1823 drawBackground(raw_frame_texture);
1825 string cnt_str;
1826 stringstream cnt_out;
1827 cnt_out << cnt;
1828 cnt_str = cnt_out.str();
1830 //IplImage *pre_mask = cvCreateImage(cvSize(WIDTH, HEIGHT), 8, 3);
1832 if (!multi)
1833 return;
1835 int now = glutGet(GLUT_ELAPSED_TIME);
1836 /* elapsed time
1837 cout << now/1000.0 << endl;
1841 // fade
1842 double elapsed = frame_timer.Update();
1843 if ( frame_ok )
1845 last_frame_caught_time.SetNow();
1846 // increase fade
1847 if ( fade < (show_status?MAX_FADE_SHOW:MAX_FADE_NORMAL) )
1849 fade += (1.0f/SECONDS_LOST_FADE)*elapsed;
1851 else
1852 fade = show_status?MAX_FADE_SHOW:MAX_FADE_NORMAL;
1853 //printf("frame_ok: fade %f\n", fade );
1855 else
1857 FTime now;
1858 now.SetNow();
1859 double elapsed_since_last_caught = (now-last_frame_caught_time).ToSeconds();
1860 if ( elapsed_since_last_caught > SECONDS_LOST_TRACK )
1862 if ( fade > 0.0f )
1863 fade -= (1.0f/SECONDS_LOST_FADE)*elapsed;
1864 else
1865 fade = 0.0f;
1867 //printf("frame_ok: fade %f, elapsed since last caught %f\n", fade, elapsed_since_last_caught );
1869 //printf("frame %s, lost_count %f -> fade pct %4.2f, fade %4.2f\n", frame_ok?"y":"n", frame_lost_count, fade_pct, fade );
1871 // draw augmentation
1872 if ( fade > 0 && augment == 1)
1874 drawAugmentation();
1877 // calculate fps
1878 draw_fps = (draw_fps*7.0+1.0/elapsed)/8.0f;
1880 glLoadIdentity();
1881 // we need to setup a new projection matrix for the title font.
1882 glMatrixMode(GL_PROJECTION);
1883 glLoadIdentity();
1884 glMatrixMode(GL_MODELVIEW);
1885 glLoadIdentity();
1886 glTranslatef(-.98, 0.9, 0.0);
1887 glScalef(.003, .003, .003);
1888 ftglFont->FaceSize(32);
1889 glColor4f(1.0, 1.0, 1.0, 1);
1890 ftglFont->Render("the artvertiser 0.4");
1891 glTranslatef(0, -(video_height+30), 0);
1892 glColor4f(1.0, 1.0, 1.0, .6);
1893 //ftglFont->FaceSize(16);
1894 //ftglFont->Render(date(now).c_str());
1895 if (frame_ok == 1 and (now/1000)%2== 0)
1897 glTranslatef(video_width-295, video_height+35, 0);
1898 glColor4f(0.0, 1.0, 0.0, .8);
1899 glBegin(GL_TRIANGLES);
1900 glVertex3f(140, 0, 0);
1901 glVertex3f(150, 10, 0);
1902 glVertex3f(140, 20, 0);
1903 glEnd();
1904 glTranslatef(70, 5, 0);
1905 ftglFont->FaceSize(16);
1906 ftglFont->Render("tracking");
1909 // reset the ftgl font size for next pass
1910 ftglFont->FaceSize(12);
1913 if ( show_status )
1915 //printf("draw status\n");
1917 drawDetectedPoints( raw_frame_texture->getIm()->width, raw_frame_texture->getIm()->height );
1919 char detect_fps_string[256];
1920 sprintf(detect_fps_string, "draw fps: %4.2f\ndetection fps: %4.2f", draw_fps, detection_fps );
1921 drawGlutString( detect_fps_string, 1.0f, 0.2f, 0.2f, 0.7, 0.94 );
1923 // now status string
1924 string draw_string = status_string;
1925 if ( multi )
1927 // show detector settings
1928 draw_string += "\n" + getSettingsString();
1930 drawGlutString( draw_string.c_str(), 1.0f, 0.2f, 0.2f, 0.01f, 0.2f );
1933 drawMenu();
1936 glutSwapBuffers();
1937 //cvReleaseImage(&image); // cleanup used image
1938 glFlush();
1943 void startSerialThread()
1945 pthread_attr_t thread_attr;
1946 pthread_attr_init(&thread_attr);
1947 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
1948 // launch the thread
1949 pthread_create( &serial_thread, &thread_attr, serialThreadFunc, NULL );
1950 serial_thread_is_running = true;
1951 pthread_attr_destroy( &thread_attr );
1954 void shutdownSerialThread()
1956 // kill the thread
1957 serial_thread_should_exit = true;
1958 void* ret;
1959 pthread_join( serial_thread, &ret );
1960 serial_thread_is_running = false;
1964 void* serialThreadFunc( void* data )
1966 // arduino vars
1967 int fd = 0;
1968 char serialport[256];
1969 int baudrate = B9600; // default
1970 char buf[256];
1971 char buf2[256];
1972 int rc,n;
1974 fd = serialport_init( "/dev/ttyUSB0", 9600 );
1975 printf("fd said %i, errno %i\n", fd, errno );
1977 while ( !serial_thread_should_exit )
1979 int read = serialport_read_until(fd, buf, '\n');
1980 //printf("read: %s then %s\n",buf2, buf);
1981 if ( (read==0) && strlen( buf ) >= 4 /*includes final \n*/ )
1983 bool button_red = (buf[0]=='1');
1984 bool button_green = (buf[1]=='1');
1985 bool button_blue = (buf[2]=='1');
1986 // printf("buttons: %s %s %s", button_red?"red":" ", button_green?"green":" ", button_blue?"blue":" ");
1987 // bitmapped, to access all 7 press combinations
1988 char new_button_state =
1989 ( button_green ? BUTTON_GREEN : 0 ) |
1990 ( button_blue ? BUTTON_BLUE : 0 ) |
1991 ( button_red ? BUTTON_RED : 0 );
1992 // deal with debounce
1993 if ( new_button_state != button_state )
1995 printf( "serialport read %s -> 0x%x (old was 0x%x)\n",
1996 buf, new_button_state, button_state );
1997 button_state_changed = true;
1998 button_state = new_button_state;
2001 usleep(3*1000);
2004 close(fd);
2007 static void startDetectionThread( int thread_priority )
2009 pthread_attr_t thread_attr;
2010 pthread_attr_init(&thread_attr);
2011 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
2012 // launch the thread
2013 pthread_create( &detection_thread, &thread_attr, detectionThreadFunc, NULL );
2014 if ( thread_priority > 0 )
2016 printf("attempting to set detection thread priority to %i\n", thread_priority );
2017 struct sched_param param;
2018 param.sched_priority = thread_priority;
2020 int res = pthread_setschedparam( detection_thread, SCHED_RR, &param );
2021 if ( res != 0 )
2023 fprintf(stderr,"pthread_setschedparam failed: %s\n",
2024 (res == ENOSYS) ? "ENOSYS" :
2025 (res == EINVAL) ? "EINVAL" :
2026 (res == ENOTSUP) ? "ENOTSUP" :
2027 (res == EPERM) ? "EPERM" :
2028 (res == ESRCH) ? "ESRCH" :
2029 "???"
2033 detection_thread_running = true;
2034 pthread_attr_destroy( &thread_attr );
2037 static void shutdownDetectionThread()
2039 // kill the thread
2040 detection_thread_should_exit = true;
2041 void* ret;
2042 pthread_join( detection_thread, &ret );
2043 detection_thread_running = false;
2046 static void* detectionThreadFunc( void* _data )
2049 FTime detection_thread_timer;
2050 detection_thread_timer.SetNow();
2052 while ( !detection_thread_should_exit )
2054 PROFILE_THIS_BLOCK("detection_thread");
2056 if ( new_artvert_requested )
2058 // no longer draw
2059 frame_ok = false;
2060 // go with the loading
2061 loadOrTrain(new_artvert_requested_index);
2062 new_artvert_requested = false;
2065 bool frame_retrieved = false;
2066 bool frame_retrieved_and_ok = multi->cams[0]->detect( frame_retrieved, frame_ok );
2067 if( frame_retrieved )
2069 double elapsed = detection_thread_timer.Update();
2070 detection_fps = (detection_fps*0.0 + (1.0/elapsed))/1.0;
2072 if ( !frame_retrieved_and_ok )
2074 PROFILE_THIS_BLOCK("sleep till next");
2075 usleep( 10000 );
2076 continue;
2079 if ( detection_thread_should_exit )
2080 break;
2082 multi->model.augm.Clear();
2083 if (multi->cams[0]->detector.object_is_detected)
2085 add_detected_homography(0, multi->cams[0]->detector, multi->model.augm);
2087 else
2089 multi->model.augm.AddHomography();
2092 frame_ok = multi->model.augm.Accomodate(4, 1e-4);
2094 if (frame_ok)
2096 // fetch surface normal in world coordinates
2097 CvMat *mat = multi->model.augm.GetObjectToWorld();
2098 float normal[3];
2099 for (int j=0; j<3; j++)
2100 normal[j] = cvGet2D(mat, j, 2).val[0];
2102 // continue to track
2103 if ( track_kalman )
2104 matrix_tracker.addPoseKalman( mat, multi->cams[0]->getFrameIndexForTime(
2105 multi->cams[0]->getLastProcessedFrameTimestamp() ) );
2106 else
2107 matrix_tracker.addPose( mat, multi->cams[0]->getLastProcessedFrameTimestamp() );
2109 cvReleaseMat(&mat);
2114 printf("detection thread exiting\n");
2116 pthread_exit(0);
2120 /*! GLUT calls this during photometric calibration or augmentation phase when
2121 * there's nothing else to do. This function does the 2D detection and bundle
2122 * adjusts the 3D pose of the calibration pattern.
2124 static void idle()
2126 if ( running_on_binoculars )
2128 static int fullscreen_timer = 30;
2129 if ( fullscreen_timer > 0 )
2131 fullscreen_timer --;
2132 if( fullscreen_timer <= 0 )
2133 glutFullScreen();
2137 // detect the calibration object in every image
2138 // (this loop could be paralelized)
2139 int nbdet=1;
2141 if(!raw_frame_texture) raw_frame_texture = new IplTexture;
2143 PROFILE_SECTION_PUSH("getting last frame");
2145 if ( delay_video )
2147 IplImage* captured_frame;
2148 multi->cams[current_cam]->getLastDrawFrame( &captured_frame, &raw_frame_timestamp );
2150 static list< pair<IplImage*, FTime> > frameRingBuffer;
2151 while ( frameRingBuffer.size()<VIDEO_DELAY_FRAMES )
2153 IplImage* first_frame = cvCreateImage( cvGetSize( captured_frame ), captured_frame->depth, captured_frame->nChannels );
2154 cvCopy( captured_frame, first_frame );
2155 frameRingBuffer.push_back( make_pair( first_frame, raw_frame_timestamp ) );
2158 IplImage* ringbuffered = frameRingBuffer.front().first;
2159 cvCopy( captured_frame, ringbuffered );
2160 frameRingBuffer.push_back( make_pair( ringbuffered, raw_frame_timestamp ) );
2162 frameRingBuffer.pop_front();
2164 IplImage* raw_frame = frameRingBuffer.front().first;
2165 raw_frame_timestamp = frameRingBuffer.front().second;
2167 raw_frame_texture->setImage(raw_frame);
2169 else
2171 IplImage* raw_frame = raw_frame_texture->getImage();
2172 multi->cams[current_cam]->getLastDrawFrame( &raw_frame, &raw_frame_timestamp );
2173 raw_frame_texture->setImage(raw_frame);
2176 PROFILE_SECTION_POP();
2179 if ( multi->model.isInteractiveTrainBinocularsRunning() )
2181 bool button_red = button_state & BUTTON_RED;
2182 bool button_green = button_state & BUTTON_GREEN;
2183 bool button_blue = button_state & BUTTON_BLUE;
2184 multi->model.interactiveTrainBinocularsUpdate( raw_frame_texture->getImage(),
2185 button_red, button_green, button_blue );
2187 else
2189 updateMenu();
2190 //doDetection();
2192 glutPostRedisplay();
2194 PROFILE_SECTION_POP();
2196 if ( show_profile_results )
2198 // show profiler output
2199 printf("showing results\n");
2200 FProfiler::Display( FProfiler::SORT_TIME/*SORT_EXECUTION*/ );
2201 show_profile_results = false;
2205 //! Starts photometric calibration.
2206 static void start()
2208 glutIdleFunc(idle);
2209 glutDisplayFunc(draw);
2215 // menu
2216 bool menu_show = false;
2217 FTime menu_timer;
2218 bool menu_is_showing = false;
2219 bool menu_up = false;
2220 bool menu_down = false;
2221 void updateMenu();
2222 void drawMenu();
2224 int menu_index = 0;
2226 void updateMenu()
2228 // update timer
2229 if ( menu_is_showing )
2231 FTime now;
2232 now.SetNow();
2233 if ( (now-menu_timer).ToSeconds() > MENU_HIDE_TIME )
2235 menu_is_showing = false;
2239 if ( !button_state_changed )
2240 return;
2242 printf("menu sees new button state: %s %s %s\n",
2243 button_state&BUTTON_RED?"red":" ",
2244 button_state&BUTTON_GREEN?"green":" ",
2245 button_state&BUTTON_BLUE?"blue":" ");
2247 // clear changed flag
2248 button_state_changed = false;
2250 if ( ( button_state == BUTTON_GREEN ) && !menu_is_showing )
2252 menu_is_showing = true;
2253 menu_timer.SetNow();
2254 if ( menu_index >= artvert_list.size() )
2255 menu_index = artvert_list.size()-1;
2256 // done
2257 return;
2260 // only process rest of buttons if menu is showing
2261 if ( !menu_is_showing )
2262 return;
2264 // navigation
2265 if( button_state == BUTTON_BLUE )
2267 menu_index++;
2268 if ( menu_index >= artvert_list.size() )
2269 menu_index = 0;
2270 menu_timer.SetNow();
2272 if ( button_state == BUTTON_RED )
2274 menu_index--;
2275 if ( menu_index < 0 )
2276 menu_index = artvert_list.size()-1;
2277 menu_timer.SetNow();
2280 // accept?
2281 if ( button_state == BUTTON_GREEN )
2284 new_artvert_requested_index = menu_index;
2285 new_artvert_requested = true;
2287 menu_is_showing = false;
2297 void drawMenu()
2299 if ( !menu_is_showing )
2301 // draw switching text?
2302 if ( new_artvert_switching_in_progress )
2304 glMatrixMode(GL_PROJECTION);
2305 glLoadIdentity();
2306 glMatrixMode(GL_MODELVIEW);
2307 glLoadIdentity();
2308 glTranslatef(-.8, 0.65, 0.0);
2309 glScalef(.003, .003, .003);
2310 ftglFont->FaceSize(24);
2311 glColor4f(0.0, 1.0, 0.0, 1);
2313 ftglFont->Render("changing artvert...");
2318 return;
2321 // draw menu header
2323 // draw loop
2324 glMatrixMode(GL_PROJECTION);
2325 glLoadIdentity();
2326 glMatrixMode(GL_MODELVIEW);
2327 glLoadIdentity();
2328 glEnable(GL_BLEND);
2329 glTranslatef(-.85, 0.65, 0.0);
2330 glScalef(.003, .003, .003);
2331 ftglFont->FaceSize(24);
2332 glColor4f(.25, 1.0, 0.0, 1);
2334 ftglFont->Render("Select artvert:");
2335 glTranslatef( 0, -26, 0 );
2337 for ( int i=0; i<artvert_list.size(); i++ )
2339 string advert = artvert_list[i].advert;
2340 string name = artvert_list[i].name;
2341 string artist = artvert_list[i].artist;
2342 string line = string(" ") + advert + " : '" + name + "' by " + artist;
2344 if ( i == menu_index )
2346 glColor4f( 1,0.37,0,1 );
2348 else
2350 glColor4f( .25f, 1.f, 0.0f, 1 );
2352 ftglFont->Render(line.c_str());
2353 glTranslatef(0, -26, 0 );
2362 //EOF