Fawkes API  Fawkes Development Version
viewer.cpp
00001 
00002 /***************************************************************************
00003  *  viewer.cpp - Generic viewer tool
00004  *
00005  *  Created: Tue Nov 06 15:02:51 2007
00006  *  Copyright  2005-2007  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 
00024 #include <core/exceptions/software.h>
00025 #include <utils/system/argparser.h>
00026 
00027 #include <fvcams/factory.h>
00028 #ifdef HAVE_SHMEM_CAM
00029 #include <fvcams/shmem.h>
00030 #endif
00031 #ifdef HAVE_NETWORK_CAM
00032 #include <fvcams/net.h>
00033 #endif
00034 #ifdef HAVE_FILELOADER_CAM
00035 #include <fvcams/fileloader.h>
00036 #endif
00037 
00038 #include <fvwidgets/image_display.h>
00039 #ifdef HAVE_RECTINFO
00040 #include <fvutils/rectification/rectfile.h>
00041 #include <fvutils/rectification/rectinfo_block.h>
00042 #include <fvfilters/rectify.h>
00043 #endif
00044 
00045 #include <cstring>
00046 #include <cstdio>
00047 #include <stdint.h>
00048 
00049 #include <SDL.h>
00050 #ifdef HAVE_GTKMM
00051 #include <gtkmm.h>
00052 #endif
00053 
00054 #include <fvutils/color/conversions.h>
00055 
00056 using namespace fawkes;
00057 using namespace firevision;
00058 
00059 void
00060 print_usage(const char *program_name)
00061 {
00062   printf("Usage: %s [-c] [-s shmem_id] [-n host[:port]/image_id] [-f file] [-o shmem_id] [-v] \\\n"
00063          "          [-d delay] [cam arg string]\n\n"
00064          "  -c             Start in continuous update mode\n"
00065          "  -s shmem_id    Open shared memory image with given ID\n"
00066          "  -n net_string  Open network camera, the camera string is of the form\n"
00067          "                 host[:port]/image_id. You have to specify at least the host\n"
00068          "                 and the image_id, the port is optional and defaults to 5000\n"
00069          "  -j             Receive JPEG images, only valid with -n\n"
00070          "  -d delay       Delay in ms before a new image is capture.\n"
00071          "  -f file        Open file loader camera with given file (image, colormap)\n"
00072          "  -o shmem_id    Output the image to a shared memory segment with given ID\n"
00073          "  -v             Verbose output on console\n"
00074          "  cam arg string Can be an arbitrary camera argument string that is understood\n"
00075          "                 by CameraFactory and the desired camera.\n",
00076          program_name);
00077 }
00078 
00079 void
00080 print_keys()
00081 {
00082   printf("Keys:\n"
00083          "  c        Toggle continuous mode (automatic image updating as fast as possible)\n"
00084          "  r        rectify image, will query for rectification info file and possibly\n"
00085          "           for camera if there is more than one block.\n"
00086          "  +        Increase delay by 5 ms\n"
00087          "  -        Decrease delay by 5 ms\n"
00088          "  Shift-R  rectify image, use already loaded lut info file, do not query for\n"
00089          "           new file\n"
00090          "  Space    Refresh image\n"
00091          "  q/Esc    Quit viewer\n");
00092 }
00093 
00094 
00095 /** Process all outstanding Gtk events. */
00096 void
00097 process_gtk_events()
00098 {
00099   while ( Gtk::Main::events_pending() ) {
00100     Gtk::Main::iteration();
00101   }
00102 }
00103 
00104 
00105 int
00106 main(int argc, char **argv)
00107 {
00108   ArgumentParser argp(argc, argv, "hs:f:n:vjcd:o:");
00109   std::string title = "";
00110 
00111 #ifdef HAVE_GTKMM
00112   Gtk::Main gtk_main(argc, argv);
00113 #endif
00114 
00115   Camera *cam;
00116   SharedMemoryImageBuffer *buf = NULL;
00117   bool verbose = argp.has_arg("v");
00118   int delay = 0;
00119 
00120   if ( argp.has_arg("d") ) {
00121     delay = atoi(argp.arg("d"));
00122     if ( delay < 0 )  delay = 0;
00123   }
00124 
00125   if ( argp.has_arg("h") ) {
00126     print_usage(argp.program_name());
00127     exit(0);
00128   } else if ( argp.has_arg("s") ) {
00129 #ifdef HAVE_SHMEM_CAM
00130                 title = std::string(argp.arg("s"));
00131     cam = new SharedMemoryCamera(argp.arg("s"));
00132 #else
00133     throw Exception("SharedMemoryCamera not available at compile time");
00134 #endif
00135   } else if ( argp.has_arg("f") ) {
00136 #ifdef HAVE_FILELOADER_CAM
00137     title = std::string("File: ").append(argp.arg("f"));
00138     cam = new FileLoader(argp.arg("f"));
00139 #else
00140     throw Exception("FileLoader not available at compile time");
00141 #endif
00142   } else if ( argp.has_arg("n") ) {
00143 #ifdef HAVE_NETWORK_CAM
00144     title = std::string("Net cam: ").append(argp.arg("n"));
00145     char *net_string = strdup(argp.arg("n"));
00146     char *image_id = NULL, *host = NULL, *port = NULL, *save_ptr = NULL;
00147     int port_num = 2208;
00148     char *hostport;
00149 
00150     hostport = strtok_r(net_string, "/", &save_ptr);
00151     image_id = strtok_r(NULL, "", &save_ptr);
00152 
00153     if ( strchr(hostport, ':') != NULL ) {
00154       host = strtok_r(hostport, ":", &save_ptr);
00155       port = strtok_r(NULL, "", &save_ptr);
00156     } else {
00157       host = hostport;
00158     }
00159 
00160     if ( port != NULL ) {
00161       port_num = atoi(port);
00162       if ( (port_num < 0) || (port_num > 0xFFFF) ) {
00163         throw OutOfBoundsException("Invalid port", port_num, 0, 0xFFFF);
00164       }
00165     }
00166 
00167     if( image_id == NULL ) {
00168       throw IllegalArgumentException("Image ID must be specified");
00169     }
00170 
00171     cam = new NetworkCamera(host, port_num, image_id, argp.has_arg("j"));
00172     free(net_string);
00173 #else
00174     throw Exception("NetworkCamera not available at compile time");
00175 #endif
00176   } else {
00177     if ( argp.num_items() == 0 ) {
00178       print_usage(argp.program_name());
00179       printf("\n\nNeither camera option nor camera string given. Aborting.\n\n");
00180       exit(-3);
00181     }
00182     cam = CameraFactory::instance(argp.items()[0]);
00183   }
00184 
00185   if ( cam == NULL ) {
00186     throw Exception("Failed to initialize camera for unknown reason");
00187   }
00188 
00189   try {
00190     cam->open();
00191     cam->start();
00192   } catch (Exception &e) {
00193     printf("Failed to open camera\n");
00194     e.print_trace();
00195     delete cam;
00196     exit(-2);
00197   }
00198 
00199   if ( argp.has_arg("o") )
00200   {
00201     buf = new SharedMemoryImageBuffer(argp.arg("o"), cam->colorspace(), cam->pixel_width(), cam->pixel_height());
00202   }
00203 
00204   print_keys();
00205 
00206   if ( verbose ) {
00207     printf("Camera opened, settings:\n"
00208            "  Colorspace:  %u (%s)\n"
00209            "  Dimensions:  %u x %u\n"
00210            "  Buffer size: %zu\n"
00211            "  Delay:       %i ms\n",
00212            cam->colorspace(), colorspace_to_string(cam->colorspace()),
00213            cam->pixel_width(), cam->pixel_height(),
00214            colorspace_buffer_size(cam->colorspace(), cam->pixel_width(), cam->pixel_height()),
00215            delay);
00216   }
00217 
00218   ImageDisplay *display = new ImageDisplay(cam->pixel_width(), cam->pixel_height(), title.c_str());
00219 
00220 #ifdef HAVE_RECTINFO
00221   RectificationInfoFile *rectfile = new RectificationInfoFile();
00222   FilterRectify *rectify_filter = NULL;
00223   unsigned char *filtered_buffer = malloc_buffer(YUV422_PLANAR,
00224                                                  cam->pixel_width(), cam->pixel_height());
00225   unsigned char *unfiltered_buffer = malloc_buffer(YUV422_PLANAR,
00226                                                    cam->pixel_width(), cam->pixel_height());
00227   bool rectifying = false;
00228 #endif
00229   bool continuous = argp.has_arg("c");
00230 
00231   SDL_Event redraw_event;
00232   redraw_event.type = SDL_KEYUP;
00233   redraw_event.key.keysym.sym = SDLK_SPACE;
00234 
00235   SDL_PushEvent(&redraw_event);
00236 
00237   bool quit = false;
00238   while (! quit) {
00239     SDL_Event event;
00240     if ( SDL_WaitEvent(&event) ) {
00241       switch (event.type) {
00242       case SDL_QUIT:
00243         quit = true;
00244         break;
00245       case SDL_KEYUP:
00246         if ( event.key.keysym.sym == SDLK_SPACE ) {
00247           cam->capture();
00248           if (cam->buffer() != NULL ) {
00249             if ( buf ) memcpy(buf->buffer(), cam->buffer(), cam->buffer_size());
00250 #ifdef HAVE_RECTINFO
00251             if ( rectifying ) {
00252               convert(cam->colorspace(), YUV422_PLANAR, cam->buffer(), unfiltered_buffer,
00253                       cam->pixel_width(), cam->pixel_height());
00254               ROI *fir = ROI::full_image(cam->pixel_width(), cam->pixel_height());
00255               rectify_filter->set_src_buffer(unfiltered_buffer, fir);
00256               rectify_filter->set_dst_buffer(filtered_buffer, fir);
00257               rectify_filter->apply();
00258               display->show(YUV422_PLANAR, filtered_buffer);
00259             } else {
00260 #endif
00261               display->show(cam->colorspace(), cam->buffer());
00262 #ifdef HAVE_RECTINFO
00263             }
00264 #endif
00265 
00266             cam->dispose_buffer();
00267           } else {
00268             printf("No valid frame received\n");
00269           }
00270           if ( continuous ) {
00271             usleep(delay * 1000);
00272             SDL_PushEvent(&redraw_event);
00273           }
00274         } else if ( event.key.keysym.sym == SDLK_ESCAPE ) {
00275           quit = true;
00276         } else if ( event.key.keysym.sym == SDLK_q ) {
00277           quit = true;
00278         } else if ( event.key.keysym.sym == SDLK_c ) {
00279           continuous = ! continuous;
00280           SDL_PushEvent(&redraw_event);
00281         } else if ( event.key.keysym.sym == SDLK_PLUS ) {
00282           delay += 5;
00283           printf("New delay: %i ms\n", delay);
00284         } else if ( event.key.keysym.sym == SDLK_MINUS ) {
00285           if ( delay > 5 ) {
00286             delay -= 5;
00287           } else {
00288             delay = 0;
00289           }
00290           printf("New delay: %i ms\n", delay);
00291         } else if ( event.key.keysym.sym == SDLK_r ) {
00292 #ifdef HAVE_GTKMM
00293 #  ifdef HAVE_RECTINFO
00294           if ( rectifying ) {
00295             rectifying = false;
00296           } else {
00297             if ( (! (SDL_GetModState() & KMOD_LSHIFT) &&
00298                   ! (SDL_GetModState() & KMOD_RSHIFT)) ||
00299                  ! rectify_filter ) {
00300               Gtk::FileChooserDialog fcd("Open Rectification Info File");
00301 
00302               // Add response buttons the the dialog
00303               fcd.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
00304               fcd.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
00305 
00306 #if GTK_VERSION_GE(3,0)
00307               Glib::RefPtr<Gtk::FileFilter> filter_rectinfo =
00308                 Gtk::FileFilter::create();
00309               filter_rectinfo->set_name("Rectification Info");
00310               filter_rectinfo->add_pattern("*.rectinfo");
00311 #else
00312               Gtk::FileFilter filter_rectinfo;
00313               filter_rectinfo.set_name("Rectification Info");
00314               filter_rectinfo.add_pattern("*.rectinfo");
00315 #endif        
00316               fcd.add_filter(filter_rectinfo);
00317 
00318 #if GTK_VERSION_GE(3,0)
00319               Glib::RefPtr<Gtk::FileFilter> filter_any =
00320                 Gtk::FileFilter::create();
00321               filter_any->set_name("Any File");
00322               filter_any->add_pattern("*");
00323 #else
00324               Gtk::FileFilter filter_any;
00325               filter_any.set_name("Any File");
00326               filter_any.add_pattern("*");
00327 #endif
00328               fcd.add_filter(filter_any);
00329 
00330               int result = fcd.run();
00331 
00332               fcd.hide();
00333               process_gtk_events();
00334 
00335               if ( result == Gtk::RESPONSE_OK) {
00336                 // Nice, we got a file
00337                 try {
00338                   rectfile->read(fcd.get_filename().c_str());
00339                   if ( rectfile->num_blocks() == 0 ) {
00340                     throw Exception("Rectification info file does not contain any info blocks");
00341                   }
00342                   Gtk::HBox hbox;
00343                   Gtk::Label label("Camera: ");
00344                   Gtk::ComboBoxText cboxt;
00345                   hbox.add(label);
00346                   hbox.add(cboxt);
00347                   label.show();
00348                   cboxt.show();
00349 
00350                   RectificationInfoFile::RectInfoBlockVector *blocks = rectfile->rectinfo_blocks();
00351                   for (RectificationInfoFile::RectInfoBlockVector::iterator b = blocks->begin(); b != blocks->end(); ++b) {
00352                     Glib::ustring us = rectinfo_camera_strings[(*b)->camera()];
00353                     us += Glib::ustring(" (") + rectinfo_type_strings[(*b)->type()] + ")";
00354 #if GTK_VERSION_GE(3,0)
00355                     cboxt.append(us);
00356 #else
00357                     cboxt.append_text(us);
00358 #endif
00359                   }
00360                   cboxt.set_active(0);
00361 
00362                   Gtk::Dialog dialog("Choose Camera", true);
00363                   dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
00364                   dialog.get_vbox()->add(hbox);
00365                   hbox.show();
00366                   dialog.run();
00367                   dialog.hide();
00368                   process_gtk_events();
00369                 
00370                   RectificationInfoBlock *chosen_block = (*blocks)[cboxt.get_active_row_number()];
00371                   RectificationInfoFile::RectInfoBlockVector::iterator bi = blocks->begin();
00372                   for(int i = 1; i < cboxt.get_active_row_number(); ++i) {
00373                     ++bi;
00374                   }
00375                   blocks->erase(bi); // needs to be erased because otherwise it would be deleted by following delete
00376                   delete blocks;
00377 
00378                   delete rectify_filter;
00379                   rectify_filter = new FilterRectify(chosen_block);
00380                 } catch (Exception &e) {
00381                   Gtk::MessageDialog md(e.what(),
00382                                         /* use markup */ false,
00383                                         Gtk::MESSAGE_ERROR);
00384                   md.set_title("Reading Rectification Info failed");
00385                   md.run();
00386                   md.hide();
00387                   
00388                   process_gtk_events();
00389                 }
00390               }
00391             }
00392             rectifying =  (rectify_filter != NULL);
00393           }
00394           SDL_PushEvent(&redraw_event);
00395 #  else
00396         printf("Rectification support not available at compile time\n");
00397 #  endif
00398         }
00399 #else
00400         printf("Rectification support requires gtkmm(-devel) to be installed "
00401                " at compile time.\n");
00402 #endif
00403         break;
00404       default:
00405         break;
00406       }
00407     }
00408   }
00409 
00410 #ifdef HAVE_RECTINFO
00411   delete rectfile;
00412   delete rectify_filter;
00413   free(filtered_buffer);
00414   free(unfiltered_buffer);
00415 #endif
00416 
00417   cam->close();
00418   delete cam;
00419   delete display;
00420 
00421   return 0;
00422 }
00423