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