1 // This is oxl/xcv/xcv.cxx
2 
3 //:
4 // \file
5 // \author  K.Y.McGaul
6 // \brief Main function for xcv.
7 //
8 //  This contains the main function for xcv, a program designed to demonstrate
9 //  the functionality of vxl.
10 //
11 // \verbatim
12 //  Modifications:
13 //   K.Y.McGaul     27-JAN-2000    Initial version.
14 //   Feb.2002 - Peter Vanroose - brief doxygen comment placed on single line
15 // \endverbatim
16 
17 #include <iostream>
18 #include <vector>
19 #ifdef _MSC_VER
20 #  include "vcl_msvc_warnings.h"
21 #endif
22 #include <cassert>
23 
24 #include "vul/vul_arg.h"
25 
26 #include "vil1/vil1_image.h"
27 #include "vil1/vil1_load.h"
28 
29 #include "vgui/vgui.h"
30 #include "vgui/vgui_window.h"
31 #include "vgui/vgui_adaptor.h"
32 #include "vgui/vgui_tableau.h"
33 #include "vgui/vgui_menu.h"
34 #include "vgui/vgui_dialog.h"
35 #include "vgui/vgui_macro.h"
36 #include "vgui/vgui_find.h"
37 #include "vgui/vgui_easy2D_tableau.h"
38 #include "vgui/vgui_rubberband_tableau.h"
39 #include "vgui/vgui_viewer2D_tableau.h"
40 #include "vgui/vgui_grid_tableau.h"
41 #include "vgui/vgui_shell_tableau.h"
42 
43 #include "xcv_file.h"
44 #include "xcv_display.h"
45 #include "xcv_geometry.h"
46 #include "xcv_processing.h"
47 #include "xcv_segmentation.h"
48 #include "xcv_multiview.h"
49 #include "xcv_image_tableau.h"
50 #include "xcv_picker_tableau.h"
51 
52 #define MENUBAR_HEIGHT  70
53 
54 //-----------------------------------------------------------------------------
55 class xcv_tableau : public vgui_grid_tableau
56 {
57  public:
58   //xcv_tableau(int nb_images) : vgui_grid_tableau(nb_images, 1) {}
xcv_tableau(int rows,int cols)59   xcv_tableau(int rows, int cols) : vgui_grid_tableau(cols, rows) {}
~xcv_tableau()60  ~xcv_tableau() {}
handle(const vgui_event & e)61   bool handle(const vgui_event& e) { return vgui_grid_tableau::handle(e); }
62 };
63 
64 xcv_tableau* xcv_tab;
65 
66 //-----------------------------------------------------------------------------
67 //: Get file last used for saving data.
68 //-----------------------------------------------------------------------------
get_savefile()69 std::string* get_savefile()
70 {
71   static std::string savefile = "";
72   return &savefile;
73 }
74 
75 //-----------------------------------------------------------------------------
76 //: Get file last used for loading data.
77 //-----------------------------------------------------------------------------
get_loadfile()78 std::string* get_loadfile()
79 {
80   static std::string loadfile = "";
81   return &loadfile;
82 }
83 
84 //-----------------------------------------------------------------------------
85 //: Displays given message on the status bar.
86 //-----------------------------------------------------------------------------
post_to_status_bar(const char * msg)87 void post_to_status_bar(const char* msg)
88 {
89   vgui::out << msg << std::endl;
90 }
91 
get_top(unsigned col,unsigned row)92 vgui_tableau_sptr get_top(unsigned col,unsigned row)
93 {
94   return xcv_tab->get_tableau_at(col,row);
95 }
96 
97 //-----------------------------------------------------------------------------
98 //: Gets the list of all image tableaux in xcv.
99 //-----------------------------------------------------------------------------
get_image_list()100 std::vector<xcv_image_tableau_sptr> get_image_list()
101 {
102   std::vector<xcv_image_tableau_sptr> img_tabs;
103   std::vector<vgui_tableau_sptr> all_tabs = xcv_tab->get_tableau_list();
104   for (unsigned i=0; i<all_tabs.size(); i++)
105   {
106     xcv_image_tableau_sptr img
107       = (xcv_image_tableau*)vgui_find_below_by_type_name(all_tabs[i],
108       std::string("xcv_image_tableau")).operator->();
109     img_tabs.push_back(img);
110   }
111   return img_tabs;
112 }
113 
114 //-----------------------------------------------------------------------------
115 //: Gets the list of all easy2D tableaux in xcv.
116 //-----------------------------------------------------------------------------
get_easy2D_list()117 std::vector<vgui_easy2D_tableau_sptr> get_easy2D_list()
118 {
119   std::vector<vgui_easy2D_tableau_sptr> easy_tabs;
120   std::vector<vgui_tableau_sptr> all_tabs = xcv_tab->get_tableau_list();
121   for (unsigned i=0; i<all_tabs.size(); i++)
122   {
123     vgui_easy2D_tableau_sptr easy =
124       (vgui_easy2D_tableau*)vgui_find_below_by_type_name(
125         all_tabs[i], std::string("vgui_easy2D_tableau")).operator->();
126     easy_tabs.push_back(easy);
127   }
128   return easy_tabs;
129 }
130 
131 //-----------------------------------------------------------------------------
132 //: Gets the last selected row and column position.
133 //-----------------------------------------------------------------------------
get_current(unsigned * col,unsigned * row)134 void get_current(unsigned* col, unsigned* row)
135 {
136   xcv_tab->get_last_selected_position(col, row);
137 }
138 
139 //-----------------------------------------------------------------------------
140 //: Returns true if there are exactly two selected views.
141 //-----------------------------------------------------------------------------
get_twoviews(std::vector<int> * col_pos,std::vector<int> * row_pos)142 bool get_twoviews(std::vector<int>* col_pos, std::vector<int>* row_pos)
143 {
144   std::vector<int> cols, rows, times;
145   int nb_views = xcv_tab->get_selected_positions(&cols, &rows, &times);
146   // if not selected,  pick top left pair.
147   if (nb_views != 2) {
148     for (unsigned int i = 0; i < xcv_tab->rows(); ++i)
149       for (unsigned int j = 0; j < xcv_tab->cols(); ++j)
150         xcv_tab->set_selected(i,j, false);
151     cols.clear();
152     rows.clear();
153     times.clear();
154     xcv_tab->set_selected(0,0, true);
155     xcv_tab->set_selected(0,1, true);
156     nb_views = xcv_tab->get_selected_positions(&cols, &rows, &times);
157   }
158 
159   // If still not selected, may be a funny layout or summat.
160   if (nb_views != 2)
161   {
162     vgui_dialog two_dl("Error");
163     two_dl.message("");
164     two_dl.message("You must select exactly two views.");
165     two_dl.message("");
166     two_dl.ask();
167     vgui_macro_warning
168       << "You must select exactly two views, not the current "
169       << nb_views << std::endl;
170     return false;
171   }
172 
173   // Sort the views into time order:
174   if (times[0] < times[1])
175   {
176     col_pos->push_back(cols[0]); col_pos->push_back(cols[1]);
177     row_pos->push_back(rows[0]); row_pos->push_back(rows[1]);
178   }
179   else
180   {
181     col_pos->push_back(cols[1]); col_pos->push_back(cols[0]);
182     row_pos->push_back(rows[1]); row_pos->push_back(rows[0]);
183   }
184   return true;
185 }
186 
187 //-----------------------------------------------------------------------------
188 //: Returns true if there are exactly three selected views.
189 //-----------------------------------------------------------------------------
get_threeviews(std::vector<int> * col_pos,std::vector<int> * row_pos)190 bool get_threeviews(std::vector<int>* col_pos, std::vector<int>* row_pos)
191 {
192   std::vector<int> cols, rows, times;
193   int nb_views = xcv_tab->get_selected_positions(&cols, &rows, &times);
194   if (nb_views != 3)
195   {
196     vgui_dialog dl("Error");
197     dl.message("");
198     dl.message("You need to select exactly three views.");
199     dl.message("");
200     dl.ask();
201     vgui_macro_warning
202       << "You need to selected exactly three views. Number selected = "
203       << nb_views << std::endl;
204     return false;
205   }
206   // Sort the view into time order:
207   int first, second, third;
208   if (times[0] <= times[1] && times[1] <= times[2])
209   { first = 0; second = 1; third = 2; }
210   else if (times[0] <= times[2] && times[2] <= times[1])
211   { first = 0; second = 2; third = 1; }
212   else if (times[1] <= times[0] && times[0] <= times[2])
213   { first = 1; second = 0; third = 2; }
214   else if (times[1] <= times[2] && times[2] <= times[0])
215   { first = 1; second = 2; third = 0; }
216   else if (times[2] <= times[0] && times[0] <= times[1])
217   { first = 2; second = 0; third = 1; }
218   else
219   { first = 2; second = 1; third = 0; }
220 
221   col_pos->push_back(cols[first]); row_pos->push_back(rows[first]);
222   col_pos->push_back(cols[second]); row_pos->push_back(rows[second]);
223   col_pos->push_back(cols[third]); row_pos->push_back(rows[third]);
224   return true;
225 }
226 
227 //-----------------------------------------------------------------------------
228 //: Return the underlying rubberbander from the tableau at the given position.
229 //  This function returns NULL if it fails.
230 //-----------------------------------------------------------------------------
get_rubberbander_at(unsigned col,unsigned row)231 vgui_rubberband_tableau_sptr get_rubberbander_at(unsigned col, unsigned row)
232 {
233   vgui_tableau_sptr top_tab = xcv_tab->get_tableau_at(col, row);
234   if (top_tab)
235   {
236     std::string type_name("vgui_rubberband_tableau");
237     vgui_rubberband_tableau_sptr tab;
238     tab.vertical_cast(vgui_find_below_by_type_name(top_tab, type_name));
239     if (tab)
240       return tab;
241   }
242   vgui_macro_warning << "Unable to get rubberbander tableau at (" << col
243                      <<", "<<row<<")\n";
244   return vgui_rubberband_tableau_sptr();
245 }
246 
247 //-----------------------------------------------------------------------------
248 //: Return the underlying easy2D from the tableau at the given position.
249 //  This function returns NULL if it fails.
250 //-----------------------------------------------------------------------------
get_easy2D_at(unsigned col,unsigned row)251 vgui_easy2D_tableau_sptr get_easy2D_at(unsigned col, unsigned row)
252 {
253   vgui_tableau_sptr top_tab = xcv_tab->get_tableau_at(col, row);
254   if (top_tab)
255   {
256     std::string type_name("vgui_easy2D_tableau");
257     vgui_easy2D_tableau_sptr tab;
258     tab.vertical_cast(vgui_find_below_by_type_name(top_tab, type_name));
259     if (tab)
260       return tab;
261   }
262   vgui_macro_warning << "Unable to get easy2D at ("<< col<< ", "<< row << ")\n";
263   return vgui_easy2D_tableau_sptr();
264 }
265 
266 //-----------------------------------------------------------------------------
267 //: Return the underlying easy2D from the tableau at the given position.
268 //  This function returns NULL if it fails.
269 //-----------------------------------------------------------------------------
get_composite_at(unsigned col,unsigned row)270 vgui_composite_tableau_sptr get_composite_at(unsigned col, unsigned row)
271 {
272   vgui_tableau_sptr top_tab = xcv_tab->get_tableau_at(col, row);
273   if (top_tab)
274   {
275     std::string type_name("vgui_composite_tableau");
276     vgui_composite_tableau_sptr tab;
277     tab.vertical_cast(vgui_find_below_by_type_name(top_tab, type_name));
278     if (tab)
279       return tab;
280   }
281   vgui_macro_warning << "Unable to get composite at ("<<col<<", "<<row<< ")\n";
282   return vgui_composite_tableau_sptr();
283 }
284 
285 //-----------------------------------------------------------------------------
286 //: Return the viewer2D at the given position.
287 //  This function returns NULL if it fails.
288 //-----------------------------------------------------------------------------
get_viewer2D_at(unsigned col,unsigned row)289 vgui_viewer2D_tableau_sptr get_viewer2D_at(unsigned col, unsigned row)
290 {
291   vgui_tableau_sptr top_tab = xcv_tab->get_tableau_at(col, row);
292   if (top_tab)
293   {
294     vgui_viewer2D_tableau_sptr view;
295     view.vertical_cast(vgui_find_below_by_type_name(top_tab,
296                                                     std::string("vgui_viewer2D_tableau")));
297     if (view)
298       return view;
299   }
300   vgui_macro_warning << "Unable to get viewer2D tableau at (" << col
301                      << ", " << row << ")\n";
302   return vgui_viewer2D_tableau_sptr();
303 }
304 
305 //-----------------------------------------------------------------------------
306 //: Return currently active easy2d
307 //-----------------------------------------------------------------------------
get_current_easy2D()308 vgui_easy2D_tableau_sptr get_current_easy2D()
309 {
310   unsigned i,j;
311   get_current(&i,&j);
312   return get_easy2D_at(i,j);
313 }
314 
315 //-----------------------------------------------------------------------------
316 //:  Gets the image tableau at the given position.
317 //   This function returns a new image tableau if it fails.
318 //-----------------------------------------------------------------------------
get_image_tableau_at(unsigned col,unsigned row)319 xcv_image_tableau_sptr get_image_tableau_at(unsigned col, unsigned row)
320 {
321   vgui_easy2D_tableau_sptr tab = get_easy2D_at(col, row);
322   if (tab)
323   {
324     xcv_image_tableau_sptr tt;
325     tt.vertical_cast(vgui_find_below_by_type_name(tab,
326       std::string("xcv_image_tableau")));
327     if (tt)
328       return tt;
329   }
330   vgui_macro_warning << "Unable to get xcv_image_tableau at (" << col << ", "
331                      << row << ")\n";
332   return xcv_image_tableau_sptr();
333 }
334 
335 //-----------------------------------------------------------------------------
336 //: Gets the picker tableau at the given position.
337 //  Returns a new picker_tableau if it fails.
338 //-----------------------------------------------------------------------------
get_picker_tableau_at(unsigned col,unsigned row)339 xcv_picker_tableau_sptr get_picker_tableau_at(unsigned col, unsigned row)
340 {
341   vgui_tableau_sptr top_tab = xcv_tab->get_tableau_at(col, row);
342   if (top_tab)
343   {
344     xcv_picker_tableau_sptr tt;
345     tt.vertical_cast(vgui_find_below_by_type_name(top_tab,
346       std::string("xcv_picker_tableau")));
347     if (tt)
348       return tt;
349   }
350   vgui_macro_warning << "Unable to get xcv_picker_tableau at (" << col << ", "
351                      << row << ")\n";
352   return xcv_picker_tableau_sptr();
353 }
354 
355 //-----------------------------------------------------------------------------
356 //: Gets the underlying image from the tableau at the given position and returns it in the given image pointer.
357 //  Returns true if the image is OK, otherwise returns false.
358 //-----------------------------------------------------------------------------
get_image_at(vil1_image * img,unsigned col,unsigned row)359 bool get_image_at(vil1_image* img, unsigned col, unsigned row)
360 {
361   xcv_image_tableau_sptr img_tab = get_image_tableau_at(col, row);
362   if (img_tab)
363   {
364     *img = img_tab->get_image();
365     return true;
366   }
367   vgui_macro_warning << "Unable to get image at ("<< col <<", "<< row << ")\n";
368   return false;
369 }
370 
371 //-----------------------------------------------------------------------------
372 //: Given an image, returns a tableau suitable to display in xcv.
373 //-----------------------------------------------------------------------------
create_tableau(vil1_image img)374 vgui_tableau_sptr create_tableau(vil1_image img)
375 {
376   xcv_image_tableau_new       image (img);
377   vgui_easy2D_tableau_new     easy  (image);
378   vgui_rubberband_tableau_new rubber(new vgui_rubberband_easy2D_client(easy));
379   vgui_composite_tableau_new  comp(easy,rubber);
380   xcv_picker_tableau_new      picker(comp);
381   vgui_viewer2D_tableau_new   view  (picker);
382   return view;
383 }
384 
385 //-----------------------------------------------------------------------------
386 //: Displays the given image on XCV at the given position.
387 //-----------------------------------------------------------------------------
add_image_at(std::string image_filename,unsigned col,unsigned row)388 void add_image_at(std::string image_filename, unsigned col, unsigned row)
389 {
390   vil1_image img = vil1_load(image_filename.c_str());
391   vgui_tableau_sptr tab = create_tableau(img);
392   xcv_tab->add_at(tab, col, row);
393 }
394 
395 
396 //-----------------------------------------------------------------------------
397 //: Add image to next available slot
398 //-----------------------------------------------------------------------------
add_image(vil1_image & img)399 void add_image(vil1_image& img)
400 {
401   vgui_tableau_sptr tab = create_tableau(img);
402   xcv_tab->add_next(tab);
403 }
404 
405 //-----------------------------------------------------------------------------
406 //: Removes the image at the given position from the display.
407 //-----------------------------------------------------------------------------
remove_image_at(unsigned col,unsigned row)408 void remove_image_at(unsigned col, unsigned row)
409 {
410   xcv_tab->remove_at(col, row);
411 }
412 
413 //-----------------------------------------------------------------------------
414 //: Create the menubar.
415 //-----------------------------------------------------------------------------
416 vgui_menu xcv_menubar;
417 
create_menubar()418 vgui_menu create_menubar()
419 {
420   xcv_menubar.add("File", xcv_file::create_file_menu());
421   xcv_menubar.add("Display", xcv_display::create_display_menu());
422   xcv_menubar.add("Image-processing", xcv_processing::create_processing_menu());
423   xcv_menubar.add("Geometry", xcv_geometry::create_geometry_menu());
424   xcv_menubar.add("Segmentation", xcv_segmentation::create_segmentation_menu());
425   xcv_menubar.add("Multiview", xcv_multiview::create_multiview_menu());
426 
427   return xcv_menubar;
428 }
429 
430 //-----------------------------------------------------------------------------
431 //: Choosing a window size
432 // This function chooses a window size by adding up the total image size
433 // along each row (column) and choosing the row (column) sum which is
434 // greatest as the window width (height).
435 //-----------------------------------------------------------------------------
xcv_window_size_traditional(int rows,int cols,std::vector<vil1_image> const & images,unsigned * window_w,unsigned * window_h,double * viewer_scale)436 void xcv_window_size_traditional(int rows, int cols,
437                                  std::vector<vil1_image> const &images,
438                                  unsigned *window_w, unsigned *window_h,
439                                  double *viewer_scale)
440 {
441   assert(rows > 0 && cols > 0);
442 
443   assert(window_w && window_h && viewer_scale);
444   *window_w = 0;
445   *window_h = 0;
446   *viewer_scale = 1;
447 
448   // set width of window
449   for (int i=0; i<rows; ++i)
450   {
451     unsigned int winnie = 0;
452     for (int j=0; j<cols; ++j)
453     {
454       unsigned int d = i*cols + j;
455       if (d < images.size())
456         winnie += images[d].width();
457     }
458     if (winnie > *window_w)
459       *window_w = winnie;
460   }
461 
462   // set height of window
463   for (int j=0; j<cols; ++j)
464   {
465     unsigned int winnie = 0;
466     for (int i=0; i<rows; ++i)
467     {
468       unsigned int d = i*cols + j;
469       if (d < images.size())
470         winnie += images[d].height();
471     }
472     if (winnie > *window_h)
473       *window_h = winnie;
474   }
475   // Add a bit to the height for the menu and status bars:
476   *window_h += MENUBAR_HEIGHT;
477 }
478 
479 //-----------------------------------------------------------------------------
480 //: This tries to resize the window to fill some proportion of the screen.
481 //  Useful for very small or very large images.
482 //-----------------------------------------------------------------------------
xcv_window_size_adaptive(int rows,int cols,std::vector<vil1_image> const & images,unsigned * window_w,unsigned * window_h,double * viewer_scale)483 void xcv_window_size_adaptive(int rows, int cols,
484                               std::vector<vil1_image> const &images,
485                               unsigned *window_w, unsigned *window_h,
486                               double *viewer_scale)
487 {
488   xcv_window_size_traditional(rows, cols, images, window_w, window_h,
489                               viewer_scale);
490 
491   // resize the window to occupy roughly 64% of a 1024x1280 display by area.
492   double mw = 0.80 * 1280;
493   double mh = 0.80 * 1024;
494 
495   double dw = *window_w;
496   double dh = *window_h;
497   double factor = 1.1;
498   // first make it nice and big
499   while (factor*dw < mw && factor*dh < mh) {
500     dw *= factor;
501     dh *= factor;
502     *viewer_scale *= factor;
503   }
504   // then shrink it to fit onto the screen
505   while (factor*dw > mw || factor*dh > mh) {
506     dw /= factor;
507     dh /= factor;
508     *viewer_scale /= factor;
509   }
510   *window_w = unsigned(dw);
511   *window_h = unsigned(dh);
512 }
513 
514 //-----------------------------------------------------------------------------
515 // main.
516 //-----------------------------------------------------------------------------
main(int argc,char ** argv)517 int main(int argc, char** argv)
518 {
519   // Select the toolkit: command line or environment variable
520   // can override, but the default is 'gtk' or 'mfc'.
521   if (! vgui::select(argc, argv))
522   {
523     if      (vgui::exists("gtk"))
524       vgui::select("gtk");
525     else if (vgui::exists("mfc"))
526       vgui::select("mfc");
527     else if (vgui::exists("qt"))
528       vgui::select("qt");
529     else
530     {
531       // ??
532     }
533   }
534   // Initialize the chosen toolkit.
535   vgui::init(argc, argv);
536 
537   // Let the vbl arg parser loose on those command line
538   // arguments not used by vgui::init();
539   vul_arg<bool> a_adaptive("-adaptive", "resize window adaptively");
540   vul_arg<int>  a_rows("-rows", "desired number of rows in array of images", 0);
541   vul_arg<int>  a_cols("-cols", "desired number of col(umn)s in array of images", 0);
542   vul_arg_parse(argc, argv);
543 
544   int rows, cols;
545   if (a_rows() && a_cols())
546   {
547     rows = a_rows();
548     cols = a_cols();
549   }
550   else
551   {
552     rows = 1;
553     if (argc-1 > 0)
554       cols = argc-1;
555     else
556       cols = 1;
557   }
558   xcv_tab = new xcv_tableau(rows, cols);
559 
560   unsigned window_width  = 0;
561   unsigned window_height = 0;
562 
563   {
564     std::vector<vgui_tableau_sptr> viewers;
565     std::vector<vil1_image> images;
566 
567     xcv_tab->set_grid_size_changeable(false);
568     for (int argcount=1; argcount<argc && std::strcmp(argv[argcount], "-d");
569          ++argcount)
570     {
571       vil1_image img = vil1_load(argv[argcount]);
572       vgui_tableau_sptr tab = create_tableau(img);
573       xcv_tab->add_next(tab);
574 
575       images.push_back(img);
576       viewers.push_back(tab);
577     }
578     xcv_tab->set_grid_size_changeable(true);
579 
580     double viewer_scale;
581     if (a_adaptive())
582       xcv_window_size_adaptive(rows, cols, images, &window_width,
583                                &window_height, &viewer_scale);
584     else
585       xcv_window_size_traditional(rows, cols, images, &window_width,
586                                   &window_height, &viewer_scale);
587 
588     for (unsigned int i=0; i<viewers.size(); ++i)
589     {
590       vgui_viewer2D_tableau_sptr v; v.vertical_cast(viewers[i]);
591       v->token.scaleX *= (float)viewer_scale;
592       v->token.scaleY *= (float)viewer_scale;
593     }
594   }
595 
596 
597   // safety net, in case something silly happened.
598   if (window_width > 1024)
599     window_width = 1024;
600   if (window_height > 1024)
601     window_height = 1024;
602 
603   if (window_width <= 0)
604     window_width = 512;
605   if (window_height <= MENUBAR_HEIGHT)
606     window_height = 512;
607 
608   std::cerr << "window_width  = " << window_width << std::endl
609            << "window_height = " << window_height << std::endl;
610 
611   // Create a window, add the tableau and show it on screen:
612   vgui_window *win = vgui::produce_window(window_width, window_height,
613                                           create_menubar(),
614                                           "xcv"); // title
615   win->get_adaptor()->set_tableau(vgui_shell_tableau_new(xcv_tab));
616   win->set_statusbar(true);
617   win->enable_vscrollbar(false);
618   win->enable_hscrollbar(false);
619   win->show();
620   return vgui::run();
621 }
622