Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
viewer.cpp
1 
2 /***************************************************************************
3  * viewer.cpp - Generic viewer tool
4  *
5  * Created: Tue Nov 06 15:02:51 2007
6  * Copyright 2005-2007 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 
24 #include <core/exceptions/software.h>
25 #include <utils/system/argparser.h>
26 
27 #include <fvcams/factory.h>
28 #ifdef HAVE_SHMEM_CAM
29 #include <fvcams/shmem.h>
30 #endif
31 #ifdef HAVE_NETWORK_CAM
32 #include <fvcams/net.h>
33 #endif
34 #ifdef HAVE_FILELOADER_CAM
35 #include <fvcams/fileloader.h>
36 #endif
37 
38 #include <fvwidgets/image_display.h>
39 #ifdef HAVE_RECTINFO
40 #include <fvutils/rectification/rectfile.h>
41 #include <fvutils/rectification/rectinfo_block.h>
42 #include <fvfilters/rectify.h>
43 #endif
44 
45 #include <cstring>
46 #include <cstdio>
47 #include <stdint.h>
48 
49 #include <SDL.h>
50 #ifdef HAVE_GTKMM
51 #include <gtkmm.h>
52 #endif
53 
54 #include <fvutils/color/conversions.h>
55 
56 using namespace fawkes;
57 using namespace firevision;
58 
59 void
60 print_usage(const char *program_name)
61 {
62  printf("Usage: %s [-c] [-s shmem_id] [-n host[:port]/image_id] [-f file] [-o shmem_id] [-v] \\\n"
63  " [-d delay] [cam arg string]\n\n"
64  " -c Start in continuous update mode\n"
65  " -s shmem_id Open shared memory image with given ID\n"
66  " -n net_string Open network camera, the camera string is of the form\n"
67  " host[:port]/image_id. You have to specify at least the host\n"
68  " and the image_id, the port is optional and defaults to 5000\n"
69  " -j Receive JPEG images, only valid with -n\n"
70  " -d delay Delay in ms before a new image is capture.\n"
71  " -f file Open file loader camera with given file (image, colormap)\n"
72  " -o shmem_id Output the image to a shared memory segment with given ID\n"
73  " -v Verbose output on console\n"
74  " cam arg string Can be an arbitrary camera argument string that is understood\n"
75  " by CameraFactory and the desired camera.\n",
76  program_name);
77 }
78 
79 void
80 print_keys()
81 {
82  printf("Keys:\n"
83  " c Toggle continuous mode (automatic image updating as fast as possible)\n"
84  " r rectify image, will query for rectification info file and possibly\n"
85  " for camera if there is more than one block.\n"
86  " + Increase delay by 5 ms\n"
87  " - Decrease delay by 5 ms\n"
88  " Shift-R rectify image, use already loaded lut info file, do not query for\n"
89  " new file\n"
90  " Space Refresh image\n"
91  " q/Esc Quit viewer\n");
92 }
93 
94 
95 /** Process all outstanding Gtk events. */
96 void
97 process_gtk_events()
98 {
99  while ( Gtk::Main::events_pending() ) {
100  Gtk::Main::iteration();
101  }
102 }
103 
104 
105 int
106 main(int argc, char **argv)
107 {
108  ArgumentParser argp(argc, argv, "hs:f:n:vjcd:o:");
109  std::string title = "";
110 
111 #ifdef HAVE_GTKMM
112  Gtk::Main gtk_main(argc, argv);
113 #endif
114 
115  Camera *cam;
116  SharedMemoryImageBuffer *buf = NULL;
117  bool verbose = argp.has_arg("v");
118  int delay = 0;
119 
120  if ( argp.has_arg("d") ) {
121  delay = atoi(argp.arg("d"));
122  if ( delay < 0 ) delay = 0;
123  }
124 
125  if ( argp.has_arg("h") ) {
126  print_usage(argp.program_name());
127  exit(0);
128  } else if ( argp.has_arg("s") ) {
129 #ifdef HAVE_SHMEM_CAM
130  title = std::string(argp.arg("s"));
131  cam = new SharedMemoryCamera(argp.arg("s"));
132 #else
133  throw Exception("SharedMemoryCamera not available at compile time");
134 #endif
135  } else if ( argp.has_arg("f") ) {
136 #ifdef HAVE_FILELOADER_CAM
137  title = std::string("File: ").append(argp.arg("f"));
138  cam = new FileLoader(argp.arg("f"));
139 #else
140  throw Exception("FileLoader not available at compile time");
141 #endif
142  } else if ( argp.has_arg("n") ) {
143 #ifdef HAVE_NETWORK_CAM
144  title = std::string("Net cam: ").append(argp.arg("n"));
145  char *net_string = strdup(argp.arg("n"));
146  char *image_id = NULL, *host = NULL, *port = NULL, *save_ptr = NULL;
147  int port_num = 2208;
148  char *hostport;
149 
150  hostport = strtok_r(net_string, "/", &save_ptr);
151  image_id = strtok_r(NULL, "", &save_ptr);
152 
153  if ( strchr(hostport, ':') != NULL ) {
154  host = strtok_r(hostport, ":", &save_ptr);
155  port = strtok_r(NULL, "", &save_ptr);
156  } else {
157  host = hostport;
158  }
159 
160  if ( port != NULL ) {
161  port_num = atoi(port);
162  if ( (port_num < 0) || (port_num > 0xFFFF) ) {
163  throw OutOfBoundsException("Invalid port", port_num, 0, 0xFFFF);
164  }
165  }
166 
167  if( image_id == NULL ) {
168  throw IllegalArgumentException("Image ID must be specified");
169  }
170 
171  cam = new NetworkCamera(host, port_num, image_id, argp.has_arg("j"));
172  free(net_string);
173 #else
174  throw Exception("NetworkCamera not available at compile time");
175 #endif
176  } else {
177  if ( argp.num_items() == 0 ) {
178  print_usage(argp.program_name());
179  printf("\n\nNeither camera option nor camera string given. Aborting.\n\n");
180  exit(-3);
181  }
182  cam = CameraFactory::instance(argp.items()[0]);
183  }
184 
185  if ( cam == NULL ) {
186  throw Exception("Failed to initialize camera for unknown reason");
187  }
188 
189  try {
190  cam->open();
191  cam->start();
192  } catch (Exception &e) {
193  printf("Failed to open camera\n");
194  e.print_trace();
195  delete cam;
196  exit(-2);
197  }
198 
199  if ( argp.has_arg("o") )
200  {
201  buf = new SharedMemoryImageBuffer(argp.arg("o"), cam->colorspace(), cam->pixel_width(), cam->pixel_height());
202  }
203 
204  print_keys();
205 
206  if ( verbose ) {
207  printf("Camera opened, settings:\n"
208  " Colorspace: %u (%s)\n"
209  " Dimensions: %u x %u\n"
210  " Buffer size: %zu\n"
211  " Delay: %i ms\n",
212  cam->colorspace(), colorspace_to_string(cam->colorspace()),
213  cam->pixel_width(), cam->pixel_height(),
214  colorspace_buffer_size(cam->colorspace(), cam->pixel_width(), cam->pixel_height()),
215  delay);
216  }
217 
218  ImageDisplay *display = new ImageDisplay(cam->pixel_width(), cam->pixel_height(), title.c_str());
219 
220 #ifdef HAVE_RECTINFO
222  FilterRectify *rectify_filter = NULL;
223  unsigned char *filtered_buffer = malloc_buffer(YUV422_PLANAR,
224  cam->pixel_width(), cam->pixel_height());
225  unsigned char *unfiltered_buffer = malloc_buffer(YUV422_PLANAR,
226  cam->pixel_width(), cam->pixel_height());
227  bool rectifying = false;
228 #endif
229  bool continuous = argp.has_arg("c");
230 
231  SDL_Event redraw_event;
232  redraw_event.type = SDL_KEYUP;
233  redraw_event.key.keysym.sym = SDLK_SPACE;
234 
235  SDL_PushEvent(&redraw_event);
236 
237  bool quit = false;
238  while (! quit) {
239  SDL_Event event;
240  if ( SDL_WaitEvent(&event) ) {
241  switch (event.type) {
242  case SDL_QUIT:
243  quit = true;
244  break;
245  case SDL_KEYUP:
246  if ( event.key.keysym.sym == SDLK_SPACE ) {
247  cam->capture();
248  if (cam->buffer() != NULL ) {
249  if ( buf ) memcpy(buf->buffer(), cam->buffer(), cam->buffer_size());
250 #ifdef HAVE_RECTINFO
251  if ( rectifying ) {
252  convert(cam->colorspace(), YUV422_PLANAR, cam->buffer(), unfiltered_buffer,
253  cam->pixel_width(), cam->pixel_height());
254  ROI *fir = ROI::full_image(cam->pixel_width(), cam->pixel_height());
255  rectify_filter->set_src_buffer(unfiltered_buffer, fir);
256  rectify_filter->set_dst_buffer(filtered_buffer, fir);
257  rectify_filter->apply();
258  display->show(YUV422_PLANAR, filtered_buffer);
259  } else {
260 #endif
261  display->show(cam->colorspace(), cam->buffer());
262 #ifdef HAVE_RECTINFO
263  }
264 #endif
265 
266  cam->dispose_buffer();
267  } else {
268  printf("No valid frame received\n");
269  }
270  if ( continuous ) {
271  usleep(delay * 1000);
272  SDL_PushEvent(&redraw_event);
273  }
274  } else if ( event.key.keysym.sym == SDLK_ESCAPE ) {
275  quit = true;
276  } else if ( event.key.keysym.sym == SDLK_q ) {
277  quit = true;
278  } else if ( event.key.keysym.sym == SDLK_c ) {
279  continuous = ! continuous;
280  SDL_PushEvent(&redraw_event);
281  } else if ( event.key.keysym.sym == SDLK_PLUS ) {
282  delay += 5;
283  printf("New delay: %i ms\n", delay);
284  } else if ( event.key.keysym.sym == SDLK_MINUS ) {
285  if ( delay > 5 ) {
286  delay -= 5;
287  } else {
288  delay = 0;
289  }
290  printf("New delay: %i ms\n", delay);
291  } else if ( event.key.keysym.sym == SDLK_r ) {
292 #ifdef HAVE_GTKMM
293 # ifdef HAVE_RECTINFO
294  if ( rectifying ) {
295  rectifying = false;
296  } else {
297  if ( (! (SDL_GetModState() & KMOD_LSHIFT) &&
298  ! (SDL_GetModState() & KMOD_RSHIFT)) ||
299  ! rectify_filter ) {
300  Gtk::FileChooserDialog fcd("Open Rectification Info File");
301 
302  // Add response buttons the the dialog
303  fcd.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
304  fcd.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
305 
306 #if GTK_VERSION_GE(3,0)
307  Glib::RefPtr<Gtk::FileFilter> filter_rectinfo =
308  Gtk::FileFilter::create();
309  filter_rectinfo->set_name("Rectification Info");
310  filter_rectinfo->add_pattern("*.rectinfo");
311 #else
312  Gtk::FileFilter filter_rectinfo;
313  filter_rectinfo.set_name("Rectification Info");
314  filter_rectinfo.add_pattern("*.rectinfo");
315 #endif
316  fcd.add_filter(filter_rectinfo);
317 
318 #if GTK_VERSION_GE(3,0)
319  Glib::RefPtr<Gtk::FileFilter> filter_any =
320  Gtk::FileFilter::create();
321  filter_any->set_name("Any File");
322  filter_any->add_pattern("*");
323 #else
324  Gtk::FileFilter filter_any;
325  filter_any.set_name("Any File");
326  filter_any.add_pattern("*");
327 #endif
328  fcd.add_filter(filter_any);
329 
330  int result = fcd.run();
331 
332  fcd.hide();
333  process_gtk_events();
334 
335  if ( result == Gtk::RESPONSE_OK) {
336  // Nice, we got a file
337  try {
338  rectfile->read(fcd.get_filename().c_str());
339  if ( rectfile->num_blocks() == 0 ) {
340  throw Exception("Rectification info file does not contain any info blocks");
341  }
342  Gtk::HBox hbox;
343  Gtk::Label label("Camera: ");
344  Gtk::ComboBoxText cboxt;
345  hbox.add(label);
346  hbox.add(cboxt);
347  label.show();
348  cboxt.show();
349 
350  RectificationInfoFile::RectInfoBlockVector *blocks = rectfile->rectinfo_blocks();
351  for (RectificationInfoFile::RectInfoBlockVector::iterator b = blocks->begin(); b != blocks->end(); ++b) {
352  Glib::ustring us = rectinfo_camera_strings[(*b)->camera()];
353  us += Glib::ustring(" (") + rectinfo_type_strings[(*b)->type()] + ")";
354 #if GTK_VERSION_GE(3,0)
355  cboxt.append(us);
356 #else
357  cboxt.append_text(us);
358 #endif
359  }
360  cboxt.set_active(0);
361 
362  Gtk::Dialog dialog("Choose Camera", true);
363  dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
364  dialog.get_vbox()->add(hbox);
365  hbox.show();
366  dialog.run();
367  dialog.hide();
368  process_gtk_events();
369 
370  RectificationInfoBlock *chosen_block = (*blocks)[cboxt.get_active_row_number()];
371  RectificationInfoFile::RectInfoBlockVector::iterator bi = blocks->begin();
372  for(int i = 1; i < cboxt.get_active_row_number(); ++i) {
373  ++bi;
374  }
375  blocks->erase(bi); // needs to be erased because otherwise it would be deleted by following delete
376  delete blocks;
377 
378  delete rectify_filter;
379  rectify_filter = new FilterRectify(chosen_block);
380  } catch (Exception &e) {
381  Gtk::MessageDialog md(e.what(),
382  /* use markup */ false,
383  Gtk::MESSAGE_ERROR);
384  md.set_title("Reading Rectification Info failed");
385  md.run();
386  md.hide();
387 
388  process_gtk_events();
389  }
390  }
391  }
392  rectifying = (rectify_filter != NULL);
393  }
394  SDL_PushEvent(&redraw_event);
395 # else
396  printf("Rectification support not available at compile time\n");
397 # endif
398  }
399 #else
400  printf("Rectification support requires gtkmm(-devel) to be installed "
401  " at compile time.\n");
402 #endif
403  break;
404  default:
405  break;
406  }
407  }
408  }
409 
410 #ifdef HAVE_RECTINFO
411  delete rectfile;
412  delete rectify_filter;
413  free(filtered_buffer);
414  free(unfiltered_buffer);
415 #endif
416 
417  cam->close();
418  delete cam;
419  delete display;
420 
421  return 0;
422 }
423 
Simple image display.
Definition: image_display.h:38
Camera interface for image aquiring devices in FireVision.
Definition: camera.h:35
virtual void set_src_buffer(unsigned char *buf, ROI *roi, orientation_t ori=ORI_HORIZONTAL, unsigned int buffer_num=0)
Set source buffer with orientation.
Definition: filter.cpp:93
Parse command line arguments.
Definition: argparser.h:66
Region of interest.
Definition: roi.h:58
Vector that is used for maintaining the rectification info blocks.
Definition: rectfile.h:49
virtual const char * what() const
Get primary string.
Definition: exception.cpp:661
Base class for exceptions in Fawkes.
Definition: exception.h:36
Rectify image.
Definition: rectify.h:36
Shared memory image buffer.
Definition: shm_image.h:135
Shared memory camera.
Definition: shmem.h:38
unsigned char * buffer() const
Get image buffer.
Definition: shm_image.cpp:234
Rectification info block.
void print_trace()
Prints trace to stderr.
Definition: exception.cpp:619
virtual void apply()
Apply the filter.
Definition: rectify.cpp:79
Network camera.
Definition: net.h:42
Index out of bounds.
Definition: software.h:88
Expected parameter is missing.
Definition: software.h:82
void show(colorspace_t colorspace, unsigned char *buffer)
Show image from given colorspace.
Rectification Info File.
Definition: rectfile.h:38
virtual void set_dst_buffer(unsigned char *buf, ROI *roi)
Set the destination buffer.
Definition: filter.cpp:134
Load images from files.
Definition: fileloader.h:39