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 <cams/factory.h>
00028 #ifdef HAVE_SHMEM_CAM
00029 #include <cams/shmem.h>
00030 #endif
00031 #ifdef HAVE_NETWORK_CAM
00032 #include <cams/net.h>
00033 #endif
00034 #ifdef HAVE_FILELOADER_CAM
00035 #include <cams/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 <filters/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               Gtk::FileFilter filter_rectinfo;
00307               filter_rectinfo.set_name("Rectification Info");
00308               filter_rectinfo.add_pattern("*.rectinfo");
00309               fcd.add_filter(filter_rectinfo);
00310 
00311               Gtk::FileFilter filter_any;
00312               filter_any.set_name("Any File");
00313               filter_any.add_pattern("*");
00314               fcd.add_filter(filter_any);
00315 
00316               int result = fcd.run();
00317 
00318               fcd.hide();
00319               process_gtk_events();
00320 
00321               if ( result == Gtk::RESPONSE_OK) {
00322                 // Nice, we got a file
00323                 try {
00324                   rectfile->read(fcd.get_filename().c_str());
00325                   if ( rectfile->num_blocks() == 0 ) {
00326                     throw Exception("Rectification info file does not contain any info blocks");
00327                   }
00328                   Gtk::HBox hbox;
00329                   Gtk::Label label("Camera: ");
00330                   Gtk::ComboBoxText cboxt;
00331                   hbox.add(label);
00332                   hbox.add(cboxt);
00333                   label.show();
00334                   cboxt.show();
00335 
00336                   RectificationInfoFile::RectInfoBlockVector *blocks = rectfile->rectinfo_blocks();
00337                   for (RectificationInfoFile::RectInfoBlockVector::iterator b = blocks->begin(); b != blocks->end(); ++b) {
00338                     Glib::ustring us = rectinfo_camera_strings[(*b)->camera()];
00339                     us += Glib::ustring(" (") + rectinfo_type_strings[(*b)->type()] + ")";
00340                   cboxt.append_text(us);
00341                   }
00342                   cboxt.set_active(0);
00343 
00344                   Gtk::Dialog dialog("Choose Camera", false, true);
00345                   dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
00346                   dialog.get_vbox()->add(hbox);
00347                   hbox.show();
00348                   dialog.run();
00349                   dialog.hide();
00350                   process_gtk_events();
00351                 
00352                   RectificationInfoBlock *chosen_block = (*blocks)[cboxt.get_active_row_number()];
00353                   RectificationInfoFile::RectInfoBlockVector::iterator bi = blocks->begin();
00354                   for(int i = 1; i < cboxt.get_active_row_number(); ++i) {
00355                     ++bi;
00356                   }
00357                   blocks->erase(bi); // needs to be erased because otherwise it would be deleted by following delete
00358                   delete blocks;
00359 
00360                   delete rectify_filter;
00361                   rectify_filter = new FilterRectify(chosen_block);
00362                 } catch (Exception &e) {
00363                   Gtk::MessageDialog md(e.what(),
00364                                         /* use markup */ false,
00365                                         Gtk::MESSAGE_ERROR);
00366                   md.set_title("Reading Rectification Info failed");
00367                   md.run();
00368                   md.hide();
00369                   
00370                   process_gtk_events();
00371                 }
00372               }
00373             }
00374             rectifying =  (rectify_filter != NULL);
00375           }
00376           SDL_PushEvent(&redraw_event);
00377 #  else
00378         printf("Rectification support not available at compile time\n");
00379 #  endif
00380         }
00381 #else
00382         printf("Rectification support requires gtkmm(-devel) to be installed "
00383                " at compile time.\n");
00384 #endif
00385         break;
00386       default:
00387         break;
00388       }
00389     }
00390   }
00391 
00392 #ifdef HAVE_RECTINFO
00393   delete rectfile;
00394   delete rectify_filter;
00395   free(filtered_buffer);
00396   free(unfiltered_buffer);
00397 #endif
00398 
00399   cam->close();
00400   delete cam;
00401   delete display;
00402 
00403   return 0;
00404 }
00405 
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends