2 * compile on Unix using:
3 gcc `pkg-config --cflags --libs opencv` \
4 `fltk-config --cxxflags --ldflags --libs` -DFLTK \
5 mainfltk.cpp main.cpp -o stereodemofltk
17 //********************** Calibration parameters **************************
18 extern CvCalibFilter CalibFilter;
19 extern double EtalonParams[3];
21 //************************************************************************
40 Parameter BirchfieldParams[] = { {"maxdisp", TYPE_RANGE, 1, 150, 1, &max_disp},
44 char* methods_names[] = {"Modified Birchfield", 0 };
45 Parameter* methods_params[] = {BirchfieldParams, 0};
49 Fl_Window *root_window=(Fl_Window *)0;
50 VideoWindow* video_window_left = 0;
51 VideoWindow* video_window_right = 0;
52 VideoWindow* video_window_disp = 0;
54 CvCapture* capture_left = 0;
55 CvCapture* capture_right = 0;
57 CvVideoWriter* writer_left = 0;
58 CvVideoWriter* writer_right = 0;
59 CvVideoWriter* writer_disp = 0;
61 static int started = 0;
62 double timeout = 0.005;
63 IplImage* video_image_left = 0;
64 IplImage* video_image_right = 0;
65 IplImage* video_image_disp = 0;
66 IplImage* disp_image = 0;
71 Fl_Value_Slider* video_pos = 0;
72 Fl_Check_Button* do_stereo_button = 0;
73 Fl_Check_Button* rectify_button = 0;
74 Fl_Check_Button* half_size_button = 0;
80 double fps_alpha = 0.1;
83 double prev_frame_stamp = 0;
85 int total_frames0 = 0;
91 Fl_Button* stop_button = 0;
92 Fl_Button* play_button = 0;
93 Fl_Button* video_button = 0;
94 Fl_Button* cam_button = 0;
95 Fl_Button* record_button = 0;
98 Fl_Button* calib_button = 0;
99 Fl_Button* savecam_button = 0;
100 Fl_Button* loadcam_button = 0;
101 Fl_Button* swapcam_button = 0;
103 Fl_Value_Slider* max_disp_slider = 0;
107 class VideoWindow : public Fl_Box
110 VideoWindow( int x, int y, int w, int h, const char* t = 0 );
115 VideoWindow::VideoWindow( int x, int y, int w, int h, const char* t ):
116 Fl_Box( x, y, w, h, t )
120 VideoWindow::~VideoWindow()
124 void VideoWindow::draw()
128 static void end_capture()
131 cvReleaseCapture( &capture_left );
132 cvReleaseCapture( &capture_right );
135 static void cb_Stop( Fl_Widget*, void* );
136 static void cb_StartStopRecord( Fl_Widget*, void* );
137 static void cb_Exit( Fl_Widget*, void* );
139 static double get_time_accurate(void)
141 return (double)cvGetTickCount()*1e-3/(cpu_freq+1e-10);
144 IplImage* stereo( IplImage* left, IplImage* right )
146 if( lg && (left->width != lg->width || left->height != lg->height) )
148 cvReleaseImage(&lg); lg = NULL;
150 if( rg && (right->width != rg->width || right->height != rg->height) )
152 cvReleaseImage(&rg); rg = NULL;
154 if( disp_image && (left->width != disp_image->width || left->height != disp_image->height ))
156 cvReleaseImage(&disp_image); disp_image = NULL;
160 disp_image = cvCreateImage( cvSize(left->width, left->height),
163 lg = cvCreateImage( cvSize(left->width, left->height),
166 rg = cvCreateImage( cvSize(right->width, right->height),
171 case 0: //Moodified Birchfield
172 cvCvtColor( left,lg, CV_BGR2GRAY );
173 cvCvtColor( right,rg, CV_BGR2GRAY );
174 cvSet(disp_image, cvScalar(0,0,0,0) );
176 if( half_size_button->value() )
178 IplImage* hl = cvCreateImage(cvSize(lg->width/2, lg->height/2),
179 lg->depth, lg->nChannels );
180 IplImage* hr = cvCreateImage(cvSize(rg->width/2, rg->height/2),
181 rg->depth, rg->nChannels );
182 IplImage* hd = cvCreateImage(cvSize(disp_image->width/2, disp_image->height/2),
183 disp_image->depth, disp_image->nChannels );
187 cvSet(hd, cvScalar(0,0,0,0) );
189 cvFindStereoCorrespondence(
191 CV_DISPARITY_BIRCHFIELD,
195 cvConvertScale( hd, hd, 255./max_disp );
197 cvResize( hd, disp_image );
205 cvFindStereoCorrespondence(
207 CV_DISPARITY_BIRCHFIELD,
211 cvConvertScale(disp_image, disp_image, 255./max_disp);
226 cvGetRawData( video_image_left, &data, &step, &size );
228 video_window_left->redraw();
229 fl_draw_image( (uchar*)data, video_window_left->x(), video_window_left->y(),
230 size.width, size.height, 3, step );
232 cvGetRawData( video_image_right, &data, &step, &size );
234 video_window_right->redraw();
235 fl_draw_image( (uchar*)data, video_window_right->x(), video_window_right->y(),
236 size.width, size.height, 3, step );
239 static void get_next_frame(void*)
241 static int repositioning = 0;
242 IplImage* frame_left = 0;
243 IplImage* frame_right = 0;
245 double new_pos = video_pos->value();
246 if( (new_pos-old_pos >= 1e-10 || new_pos-old_pos <= -1e-10) && !repositioning)
248 video_window_left->redraw();
249 video_window_right->redraw();
251 cvSetCaptureProperty( capture_left, CV_CAP_PROP_POS_AVI_RATIO, new_pos );
252 cvSetCaptureProperty( capture_right, CV_CAP_PROP_POS_AVI_RATIO, new_pos );
253 new_pos = cvGetCaptureProperty( capture_left, CV_CAP_PROP_POS_AVI_RATIO );
254 printf("Repositioning\n");
259 new_pos = cvGetCaptureProperty( capture_left, CV_CAP_PROP_POS_AVI_RATIO );
260 video_pos->value(new_pos);
264 frame_left = cvQueryFrame( capture_left );
265 frame_right = cvQueryFrame( capture_right );
267 if( (frame_left == 0 || frame_right == 0) && is_avi )
273 if( video_window_left && frame_left &&
274 video_window_right && frame_right )
276 //resize video window if it is less than required
277 if( video_window_left->w() < frame_left->width || video_window_left->h() < frame_left->height ||
278 video_window_right->w() < frame_right->width || video_window_right->h() < frame_right->height )
279 root_window->size( (short)(frame_left->width + frame_right->width + 30), (short)(MAX(frame_left->height,frame_right->height) + 150));
282 CvRect rect = { video_window_left->x(), video_window_left->y(),
283 frame_left->width, frame_left->height };
284 CvRect rect2 = { video_window_right->x(), video_window_right->y(),
285 frame_right->width, frame_right->height };
287 if( !video_image_left || video_image_left->width < rect.width ||
288 video_image_left->height < rect.height )
290 cvReleaseImage( &video_image_left );
291 video_image_left = cvCreateImage( cvSize( rect.width, rect.height ), 8, 3 );
294 cvSetImageROI( video_image_left, cvRect(0,0,rect.width, rect.height));
295 cvFlip( frame_left, video_image_left, 0 );
297 if( writer_left && is_recorded )
299 cvWriteFrame( writer_left, video_image_left );
301 cvCvtColor( video_image_left, video_image_left, CV_RGB2BGR );
303 if( !video_image_right || video_image_right->width < rect2.width ||
304 video_image_right->height < rect2.height )
306 cvReleaseImage( &video_image_right );
307 video_image_right = cvCreateImage( cvSize( rect2.width, rect2.height ), 8, 3 );
310 cvSetImageROI( video_image_right, cvRect(0,0,rect2.width, rect2.height));
311 cvFlip( frame_right, video_image_right, 0 );
313 if( writer_right && is_recorded )
315 cvWriteFrame( writer_right, video_image_right );
317 cvCvtColor( video_image_right, video_image_right, CV_RGB2BGR );
319 if( calib_button->value() )
321 IplImage* images[2] = { video_image_left, video_image_right };
322 bool found = CalibFilter.FindEtalon( images );
324 CalibFilter.DrawPoints( images );
328 int cur_time = clock();
329 if( cur_time >= prev_time + 1000 )
331 prev_time = cur_time;
333 cvXorS( images[0], cvScalarAll(255), images[0] );
334 cvXorS( images[1], cvScalarAll(255), images[1] );
338 if( CalibFilter.IsCalibrated() )
340 calib_button->clear();
341 rectify_button->activate();
342 savecam_button->activate();
349 if( CalibFilter.IsCalibrated() && rectify_button->value() )
351 IplImage* images[2] = { video_image_left, video_image_right };
352 CalibFilter.Rectify( images, images );
355 //Running stereo algorithm
356 //draw disparity window
357 if( do_stereo_button->value() && video_window_disp )
360 disp = stereo(video_image_left, video_image_right);
364 CvRect rect = { video_window_disp->x(), video_window_disp->y(),
365 disp->width, disp->height };
367 if( !video_image_disp || video_image_disp->width < rect.width ||
368 video_image_disp->height < rect.height )
370 cvReleaseImage( &video_image_disp );
371 video_image_disp = cvCreateImage( cvSize( rect.width, rect.height ), 8, 3 );
374 if( disp->nChannels != 3 )
376 cvCvtColor( disp, video_image_disp, CV_GRAY2BGR );
380 cvCopy( disp, video_image_disp );
383 if( writer_disp && is_recorded )
385 cvWriteFrame( writer_disp, video_image_disp );
392 cvGetRawData( video_image_disp, &data, &step, &size );
394 video_window_disp->redraw();
395 fl_draw_image( (uchar*)data, video_window_disp->x(), video_window_disp->y(),
396 size.width, size.height, 3, step );
405 double cur_frame_stamp = get_time_accurate();
408 fps = 1000/(cur_frame_stamp - prev_frame_stamp);
410 fps = (1-fps_alpha)*fps + fps_alpha*1000/(cur_frame_stamp - prev_frame_stamp);
411 prev_frame_stamp = cur_frame_stamp;
412 sprintf( fps_buffer, "FPS: %5.1f", fps );
413 fps_box->label( fps_buffer );
415 if( total_frames > 0 )
417 if( --total_frames == 0 )
422 total_frames = total_frames0;
423 cvSetCaptureProperty( capture_left, CV_CAP_PROP_POS_FRAMES, start_pos );
424 cvSetCaptureProperty( capture_right, CV_CAP_PROP_POS_FRAMES, start_pos );
427 Fl::add_timeout( timeout, get_next_frame, 0 );
431 static void cb_PauseResume( Fl_Widget*, void* )
433 video_button->deactivate();
434 cam_button->deactivate();
435 stop_button->activate();
440 play_button->label("@||");
441 play_button->activate();
442 play_button->redraw();
443 stop_button->activate();
444 stop_button->redraw();
445 record_button->activate();
447 video_window_left->redraw();
448 video_window_right->redraw();
450 prev_frame_stamp = get_time_accurate();
451 Fl::add_timeout( timeout, get_next_frame, 0 );
456 play_button->label("@>");
457 play_button->redraw();
462 static void cb_Stop( Fl_Widget*, void* )
467 cb_StartStopRecord(0,0);
468 cvSetCaptureProperty( capture_left, CV_CAP_PROP_POS_AVI_RATIO, 0 );
469 cvSetCaptureProperty( capture_right, CV_CAP_PROP_POS_AVI_RATIO, 0 );
470 video_button->activate();
471 cam_button->activate();
472 stop_button->deactivate();
473 record_button->deactivate();
476 static void cb_StartAVI( const char* newfile_left, const char* newfile_right,
477 int start_pos, int was_started )
479 if( newfile_left != 0 && newfile_right != 0)
482 capture_left = cvCaptureFromAVI( newfile_left );
483 capture_right = cvCaptureFromAVI( newfile_right );
485 if( capture_left && capture_right )
490 cvSetCaptureProperty( capture_left, CV_CAP_PROP_POS_FRAMES, start_pos );
491 cvSetCaptureProperty( capture_right, CV_CAP_PROP_POS_FRAMES, start_pos );
501 static void cb_LoadImages( const char* filename1, const char* filename2 )
503 if( video_image_left )
504 cvReleaseImage( &video_image_left );
505 if( video_image_right )
506 cvReleaseImage( &video_image_right );
507 if( video_image_disp )
508 cvReleaseImage( &video_image_disp );
510 video_image_left = cvLoadImage( filename1, 1 );
511 video_image_right = cvLoadImage( filename2, 1 );
514 static void cb_Open( Fl_Widget*, void* )
516 char filename1[1000];
518 char filename2[1000];
526 char *newfile = fl_file_chooser("Open Left File", "*", NULL);
529 strcpy( filename1, newfile );
530 newfile = fl_file_chooser("Open Right File", "*", NULL);
533 strcpy( filename2, newfile );
535 //TODO: add bitmap files processing
536 char* point = strrchr( filename1, '.' );
537 if( !strcmp( point, ".avi" ) )
538 cb_StartAVI( filename1, filename2, 0, was_started );
539 else if( !strcmp( point, ".bmp" ) )
540 cb_LoadImages( filename1, filename2 );
545 void cb_LoadCamera( Fl_Widget*, void* )
551 char *newfile = fl_file_chooser("Load Camera Parameters", "*", filename);
555 CalibFilter.LoadCameraParams( newfile );
556 rectify_button->activate();
561 void cb_SaveCamera( Fl_Widget*, void* )
567 char *newfile = fl_file_chooser("Save Camera Parameters", "*", filename);
569 if( newfile ) CalibFilter.SaveCameraParams( newfile );
576 void cb_StartCam( Fl_Widget*, void* )
585 capture_left = cvCaptureFromCAM( /*100*/-1 );
586 capture_right = cvCaptureFromCAM( /*101*/-1 );
591 static void cb_StartStopRecord( Fl_Widget*, void* )
598 if( video_image_left )
600 char *newfile = fl_file_chooser("Left File", "*", NULL);
604 writer_left = cvCreateVideoWriter( newfile, -1, 15, cvGetSize( video_image_left ));
608 record_button->box( FL_DOWN_BOX );
612 if( video_image_right )
614 char *newfile = fl_file_chooser("Right File", "*", NULL);
615 writer_right = cvCreateVideoWriter( newfile, -1, 15, cvGetSize( video_image_left ));
618 record_button->box( FL_DOWN_BOX );
622 if( video_image_disp )
624 char *newfile = fl_file_chooser("Disparity File", "*", NULL);
625 writer_disp = cvCreateVideoWriter( newfile, -1, 15, cvGetSize( video_image_disp ));
628 record_button->box( FL_DOWN_BOX );
636 record_button->box( FL_UP_BOX );
637 cvReleaseVideoWriter( &writer_left );
638 cvReleaseVideoWriter( &writer_right );
639 cvReleaseVideoWriter( &writer_disp );
645 static void cb_AutoRun( void* _aviname )
647 char* aviname_left = (char*)_aviname;
648 char* aviname_right = (char*)_aviname;
651 /* char* ext_pos = strrchr( aviname, '.' );
654 char* colon_pos = strchr( ext_pos, ':' );
658 start_pos = strtoul( colon_pos+1, &colon_pos, 10 );
659 if( colon_pos && *colon_pos == ':' )
660 total_frames0 = total_frames = strtoul( colon_pos+1, &colon_pos, 10 );
661 if( colon_pos && *colon_pos == ':' )
666 cb_StartAVI( aviname_left, aviname_right, start_pos, 0 );
670 static void cb_Exit( Fl_Widget*, void* )
672 cvReleaseCapture( &capture_left );
673 cvReleaseCapture( &capture_right );
674 cvReleaseVideoWriter( &writer_left );
675 cvReleaseVideoWriter( &writer_right );
676 cvReleaseVideoWriter( &writer_disp );
681 static void cb_OnSlider( Fl_Widget* slider, void* value)
683 *((int*)value) = ((Fl_Slider*)slider)->value();
685 //recompute stereo on current frame
686 if(!started && video_image_left && video_image_right)
688 IplImage* disp = stereo(video_image_left, video_image_right);
692 CvRect rect = { video_window_disp->x(), video_window_disp->y(),
693 disp->width, disp->height };
695 if( !video_image_disp || video_image_disp->width < rect.width ||
696 video_image_disp->height < rect.height )
698 cvReleaseImage( &video_image_disp );
699 video_image_disp = cvCreateImage( cvSize( rect.width, rect.height ), 8, 3 );
702 if( disp->nChannels != 3 )
704 cvCvtColor( disp, video_image_disp, CV_GRAY2BGR );
708 cvCopy( disp, video_image_disp );
714 cvGetRawData( video_image_disp, &data, &step, &size );
716 video_window_disp->redraw();
717 fl_draw_image( (uchar*)data, video_window_disp->x(), video_window_disp->y(),
718 size.width, size.height, 3, step );
726 static void cb_OnMethod( Fl_Widget* tab, void* )
728 Fl_Group* v= (Fl_Group*)((Fl_Tabs*)tab)->value();
729 //Compare tab label with method name
730 const char* tab_name = v->label();
733 for( int i = 0; methods_names[i]; i++)
735 if(!strcmp( tab_name, methods_names[i]) )
742 static void cb_Calibrate( Fl_Widget* button, void* )
744 Fl_Button* v= ((Fl_Button*)button);
745 v->value(!v->value());
748 static void cb_Swap( Fl_Widget* , void* )
750 CvCapture* tmp = capture_left;
751 capture_left = capture_right;
755 const int root_w = 320*3+30,
756 root_h = 240 /*image*/ + 100/*play controls*/+ 150/*method tabs*/ + 30;
758 int main( int argc, char **argv )
760 char* aviname_left = 0;
761 char* aviname_right = 0;
766 aviname_left = argv[1];
767 aviname_right = argv[2];
770 cpu_freq = cvGetTickFrequency();
771 printf("Processor frequency: %g\n", cpu_freq );
773 CalibFilter.SetEtalon( CV_CALIB_ETALON_CHESSBOARD, EtalonParams );
774 CalibFilter.SetCameraCount(2);
775 CalibFilter.SetFrames(15);
780 Fl_Window* o = root_window = new Fl_Window( root_w, root_h );
783 Fl_Tabs* o = new Fl_Tabs( 10, 10, root_w - 20, root_h - 250 );
786 Fl_Group* o = new Fl_Group( 10, 30, root_w - 20, root_h - 250, "Stereo" );
789 VideoWindow* o = new VideoWindow( 15, 35, (root_w - 30) / 3, root_h - 120 );
790 video_window_left = o;
791 o->box( FL_BORDER_BOX );
795 o = new VideoWindow( 15 + (root_w - 30)/3, 35, (root_w - 30)/3, root_h - 120 );
796 video_window_right = o;
797 o->box( FL_BORDER_BOX );
801 o = new VideoWindow( 15 + (root_w - 30)/3*2, 35, (root_w - 30)/3, root_h - 120 );
802 video_window_disp = o;
803 o->box( FL_BORDER_BOX );
809 Fl_Group::current()->resizable(o);
811 //tabs with different stereo methods
813 Fl_Tabs* o = new Fl_Tabs( 10, root_h - 230, root_w - 20, 150 );
815 o->callback(cb_OnMethod);
817 for( int i = 0; methods_params[i]; i++ )
819 Fl_Group* o = new Fl_Group( 10, root_h - 210, root_w - 20, 150, methods_names[i] );
821 //create controls for current method
822 for( int j = 0; methods_params[i][j].name; j++ )
824 switch( methods_params[i][j].type )
827 Fl_Value_Slider* o = new Fl_Value_Slider( 15,root_h - 200 + j * 40, 200, 20, methods_params[i][j].name );
828 o->type( FL_HOR_NICE_SLIDER );
829 o->minimum(methods_params[i][j].minimum);
830 o->maximum(methods_params[i][j].maximum);
832 o->callback(cb_OnSlider, methods_params[i][j].value );
833 *((int*)(methods_params[i][j].value)) = methods_params[i][j].minimum;
845 //camera calibration tab
846 Fl_Group* o = new Fl_Group( 10, root_h - 210, root_w - 20, 150, "Camera" );
848 const int bwidth = 30, bheight = 30;
849 calib_button = new Fl_Button( 10 + bwidth, root_h - 200, bwidth, bheight, "C" );
850 calib_button->callback( cb_Calibrate );
851 savecam_button = new Fl_Button( 10 + bwidth*2, root_h - 200, 2*bwidth, bheight, "Save" );
852 loadcam_button = new Fl_Button( 10 + bwidth*4, root_h - 200, 2*bwidth, bheight, "Load" );
853 savecam_button->callback(cb_SaveCamera);
854 savecam_button->deactivate();
855 loadcam_button->callback(cb_LoadCamera);
856 swapcam_button = new Fl_Button( 10 + bwidth*6, root_h - 200, bwidth, bheight, "@<->" );
857 swapcam_button->callback(cb_Swap);
867 const int bwidth = 30, bheight = 30;
868 play_button = new Fl_Button( 10, root_h - 35, bwidth, bheight, "@>" );
869 play_button->callback((Fl_Callback*)cb_PauseResume);
870 play_button->deactivate();
871 stop_button = new Fl_Button( 10 + bwidth, root_h - 35, bwidth, bheight, "@square" );
872 stop_button->callback((Fl_Callback*)cb_Stop);
873 stop_button->deactivate();
874 video_button = new Fl_Button( 10 + bwidth*2, root_h - 35, bwidth, bheight, "..." );
875 video_button->callback((Fl_Callback*)cb_Open);
876 cam_button = new Fl_Button( 10 + bwidth*3, root_h - 35, bwidth, bheight, "[o]" );
877 cam_button->callback((Fl_Callback*)cb_StartCam);
878 video_pos = new Fl_Value_Slider( 10 + bwidth*4 + 10, root_h - 35, 200, 20, "Position" );
879 video_pos->type( FL_HOR_NICE_SLIDER );
880 record_button = new Fl_Button( 10 + bwidth*4 + 230, root_h - 35, bwidth, bheight, "@circle" );
881 record_button->labelcolor(FL_RED);
882 record_button->callback((Fl_Callback*)cb_StartStopRecord );
883 record_button->deactivate();
884 fps_box = new Fl_Box( 10, root_h - 75, bwidth*4, bheight, "<No data>" );
885 fps_box->box( FL_DOWN_BOX );
887 do_stereo_button = new Fl_Check_Button( 10 + bwidth*4 + 270, root_h - 35, 4*bwidth, bheight, "CalcStereo" );
888 rectify_button = new Fl_Check_Button( 10 + bwidth*4 + 370, root_h - 35, 4*bwidth, bheight, "Rectify" );
889 rectify_button->deactivate();
890 half_size_button = new Fl_Check_Button( 10 + bwidth*4 + 470, root_h - 35, 4*bwidth, bheight, "Use half size" );
897 Fl::add_timeout( 0.1, cb_AutoRun, aviname_left );