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