Fawkes API Fawkes Development Version
|
00001 /*************************************************************************** 00002 * image_widget.cpp - Gtkmm widget to draw an image inside a Gtk::Window 00003 * 00004 * Created: 26.11.2008 00005 * Copyright 2008 Christof Rath <christof.rath@gmail.com> 00006 * 00007 ****************************************************************************/ 00008 00009 /* This program is free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU Library General Public License for more details. 00018 * 00019 * Read the full text in the LICENSE.GPL file in the doc directory. 00020 */ 00021 00022 00023 #include "image_widget.h" 00024 00025 #include <core/exceptions/software.h> 00026 #include <core/threading/mutex.h> 00027 #include <fvutils/color/conversions.h> 00028 #include <fvutils/color/yuv.h> 00029 #include <fvutils/scalers/lossy.h> 00030 #include <cams/camera.h> 00031 00032 #include <iomanip> 00033 00034 00035 namespace firevision { 00036 #if 0 /* just to make Emacs auto-indent happy */ 00037 } 00038 #endif 00039 00040 /** @class ImageWidget <fvwidgets/image_widget.h> 00041 * This class is an image container to display fawkes cameras (or image 00042 * buffers) inside a Gtk::Container 00043 * 00044 * @author Christof Rath 00045 */ 00046 00047 /** 00048 * Creates a new ImageWidget with predefined width and height 00049 * @param width of the widget 00050 * @param height of the widget 00051 */ 00052 ImageWidget::ImageWidget(unsigned int width, unsigned int height) 00053 { 00054 __cam = NULL; 00055 __cam_enabled = false; 00056 __cam_mutex = new fawkes::Mutex; 00057 __refresh_thread = NULL; 00058 00059 set_size(width, height); 00060 } 00061 00062 /** 00063 * Creates a new ImageWidget with a Camera as image source 00064 * @param cam the image source 00065 * @param refresh_delay if greater 0 a thread gets created that refreshes 00066 * the Image every refresh_delay milliseconds 00067 * @param width of the widget (if not equal to the camera width the image 00068 * gets scaled) 00069 * @param height of the widget (if not equal to the camera height the 00070 * image gets scaled) 00071 */ 00072 ImageWidget::ImageWidget(Camera *cam, unsigned int refresh_delay, unsigned int width, unsigned int height) 00073 { 00074 if (!cam) throw fawkes::NullPointerException("Parameter cam may not be NULL"); 00075 00076 __cam = cam; 00077 __cam_enabled = true; 00078 __cam_mutex = new fawkes::Mutex; 00079 __cam_has_buffer = false; 00080 00081 set_size(width, height); 00082 00083 try { 00084 fawkes::Time *time = __cam->capture_time(); 00085 delete time; 00086 __cam_has_timestamp = true; 00087 } 00088 catch (fawkes::Exception &e) { 00089 __cam_has_timestamp = false; 00090 } 00091 00092 __refresh_thread = new RefThread(this, refresh_delay); 00093 __refresh_thread->start(); 00094 __refresh_thread->refresh_cam(); 00095 } 00096 00097 #ifdef HAVE_GLADEMM 00098 /** 00099 * Constructor that can be used to instantiate an ImageWidget as a 00100 * derived widget from a Glade file. 00101 * 00102 * Note: The ImageWidget (and its internal buffer) is set to the size 00103 * as in the glade file, in case no camera is set afterwards. Use @see 00104 * ImageWidget::set_size() to resize the ImageWidget afterwards. 00105 * 00106 * @param cobject pointer to the base object 00107 * @param refxml the Glade XML file 00108 */ 00109 ImageWidget::ImageWidget(BaseObjectType* cobject, Glib::RefPtr<Gnome::Glade::Xml> refxml) 00110 : Gtk::Image( cobject ) 00111 { 00112 __cam = NULL; 00113 __cam_enabled = false; 00114 __cam_mutex = new fawkes::Mutex; 00115 __refresh_thread = NULL; 00116 00117 // set_size(Gtk::Image::get_width(), Gtk::Image::get_height()); 00118 } 00119 #endif 00120 00121 /** 00122 * Destructor 00123 */ 00124 ImageWidget::~ImageWidget() 00125 { 00126 if (__refresh_thread) __refresh_thread->stop(); 00127 delete __cam_mutex; 00128 } 00129 00130 /** Set the camera from which the ImageWidget obtains the images. 00131 * 00132 * Note: The size of the ImageWidget remains untouched and the cameras 00133 * image gets scaled appropriately. Use ImageWidget::set_size(0, 0) to 00134 * set the widget to the size of the camera. 00135 * 00136 * @param cam the camera 00137 * @param refresh_delay the delay between two refreshs in milliseconds 00138 */ 00139 void 00140 ImageWidget::set_camera(Camera *cam, unsigned int refresh_delay) 00141 { 00142 __cam = cam; 00143 __cam_enabled = true; 00144 __cam_has_buffer = false; 00145 00146 set_size(__cam->pixel_width(), __cam->pixel_height()); 00147 00148 try { 00149 fawkes::Time *time = __cam->capture_time(); 00150 delete time; 00151 __cam_has_timestamp = true; 00152 } 00153 catch (fawkes::Exception &e) { 00154 __cam_has_timestamp = false; 00155 } 00156 00157 if ( __refresh_thread ) { 00158 __refresh_thread->set_delay(refresh_delay); 00159 } else { 00160 __refresh_thread = new RefThread(this, refresh_delay); 00161 __refresh_thread->start(); 00162 } 00163 00164 __refresh_thread->refresh_cam(); 00165 } 00166 00167 /** 00168 * En-/disable the camera. 00169 * @param enable if true the camera is enabled and the refresh thread 00170 * is start, if false the refresh thread is stopped and the camera is 00171 * disabled 00172 */ 00173 void 00174 ImageWidget::enable_camera(bool enable) 00175 { 00176 if ( !enable && __cam_enabled ) { 00177 __refresh_thread->stop(); 00178 } else if ( __refresh_thread && enable && !__cam_enabled ) { 00179 __refresh_thread->start(); 00180 } 00181 00182 __cam_enabled = enable; 00183 } 00184 00185 /** Sets the size of the ImageWidget. 00186 * Updates the internal buffer and the size request for the ImageWidget. 00187 * If width and/or height are set to 0 (and a Camera is set) the 00188 * ImageWidget will be set to the camera dimensions. 00189 * 00190 * Note: The ImageWidget must be refreshed after changing its size! 00191 * 00192 * @param width The new width 00193 * @param height The new height 00194 */ 00195 void 00196 ImageWidget::set_size(unsigned int width, unsigned int height) 00197 { 00198 if (!width || ! height) { 00199 if (__cam) { 00200 width = __cam->pixel_width(); 00201 height = __cam->pixel_height(); 00202 } 00203 else { 00204 throw fawkes::IllegalArgumentException("ImageWidget::set_size(): width and/or height may not be 0 if no Camera is set"); 00205 } 00206 } 00207 00208 if (!__pixbuf || __width != width || __height != height) { 00209 __width = width; 00210 __height = height; 00211 00212 #if GLIBMM_MAJOR_VERSION > 2 || ( GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION >= 14 ) 00213 __pixbuf.reset(); 00214 #else 00215 __pixbuf.clear(); 00216 #endif 00217 00218 __pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, __width, __height); 00219 00220 set_size_request(__width, __height); 00221 } 00222 } 00223 /** 00224 * Returns the image buffer width 00225 * @return width of the contained image 00226 */ 00227 unsigned int 00228 ImageWidget::get_width() const 00229 { 00230 return __width; 00231 } 00232 00233 /** 00234 * Returns the image buffer height 00235 * @return height of the contained image 00236 */ 00237 unsigned int 00238 ImageWidget::get_height() const 00239 { 00240 return __height; 00241 } 00242 00243 /** 00244 * Returns the widgets pixel buffer (RGB!) 00245 * @return the RGB pixel buffer 00246 */ 00247 Glib::RefPtr<Gdk::Pixbuf> 00248 ImageWidget::get_buffer() const 00249 { 00250 return __pixbuf; 00251 } 00252 00253 /** 00254 * Sets a pixel to the given RGB colors 00255 * 00256 * @param x position of the pixel 00257 * @param y position of the pixel 00258 * @param r component of the color 00259 * @param g component of the color 00260 * @param b component of the color 00261 */ 00262 void 00263 ImageWidget::set_rgb(unsigned int x, unsigned int y, unsigned char r, unsigned char g, unsigned char b) 00264 { 00265 set_rgb (x, y, (RGB_t){r, g, b}); 00266 } 00267 00268 /** 00269 * Sets a pixel to the given RGB colors 00270 * 00271 * @param x position of the pixel 00272 * @param y position of the pixel 00273 * @param rgb the color 00274 */ 00275 void 00276 ImageWidget::set_rgb(unsigned int x, unsigned int y, RGB_t rgb) 00277 { 00278 if (x >= __width) throw fawkes::OutOfBoundsException("x-Coordinate exeeds image width", x, 0, __width); 00279 if (y >= __height) throw fawkes::OutOfBoundsException("y-Coordinate exeeds image height", x, 0, __height); 00280 00281 RGB_t * target = RGB_PIXEL_AT(__pixbuf->get_pixels(), __width, x, y); 00282 *target = rgb; 00283 } 00284 00285 /** 00286 * Show image from given colorspace. 00287 * Warning: If width and/or height not set, it is assumed, that the given 00288 * buffer has the same dimension as the widget. 00289 * 00290 * @param colorspace colorspace of the supplied buffer 00291 * @param buffer image buffer 00292 * @param width Width of the provided buffer (may be scaled to ImageWidget 00293 * dimensions) 00294 * @param height Height of the provided buffer (may be scaled to 00295 * ImageWidget dimensions) 00296 * @return TRUE if the buffer chould have been shown 00297 */ 00298 bool 00299 ImageWidget::show(colorspace_t colorspace, unsigned char *buffer, unsigned int width, unsigned int height) 00300 { 00301 try { 00302 if (!width || !height || (width == __width && height == __height)) { 00303 convert(colorspace, RGB, buffer, __pixbuf->get_pixels(), __width, __height); 00304 } 00305 else { 00306 unsigned char *scaled_buffer = (unsigned char *)malloc(colorspace_buffer_size(colorspace, __width, __height)); 00307 00308 if (scaled_buffer) { 00309 LossyScaler scaler; 00310 scaler.set_original_buffer(buffer); 00311 scaler.set_original_dimensions(width, height); 00312 scaler.set_scaled_buffer(scaled_buffer); 00313 scaler.set_scaled_dimensions(__width, __height); 00314 scaler.scale(); 00315 00316 convert(colorspace, RGB, scaled_buffer, __pixbuf->get_pixels(), __width, __height); 00317 00318 free(scaled_buffer); 00319 } 00320 } 00321 } 00322 catch (fawkes::Exception &e) { 00323 printf("ImageWidget::show(): %s\n", e.what()); 00324 return false; 00325 } 00326 00327 try { 00328 set(__pixbuf); 00329 __signal_show.emit(colorspace, buffer, width, height); 00330 return true; 00331 } 00332 catch (fawkes::Exception &e) { 00333 printf("ImageWidget::show(): Could not set the new image (%s)\n", e.what()); 00334 } 00335 00336 return false; 00337 } 00338 00339 00340 /** Signal emits after a new buffer gets successfully shown 00341 * (see @see ImageWidget::show()). 00342 * 00343 * The buffer's validity can not be guaranteed beyond the called functions 00344 * scope! In case the source of the widget is a Camera, the buffer gets 00345 * disposed after calling ImageWidget::show. 00346 * 00347 * @return The signal_show signal 00348 */ 00349 sigc::signal<void, colorspace_t, unsigned char *, unsigned int, unsigned int> & 00350 ImageWidget::signal_show() 00351 { 00352 return __signal_show; 00353 } 00354 00355 00356 /** 00357 * Sets the refresh delay for automatic camera refreshes 00358 * 00359 * @param refresh_delay im [ms] 00360 */ 00361 void 00362 ImageWidget::set_refresh_delay(unsigned int refresh_delay) 00363 { 00364 __refresh_thread->set_delay(refresh_delay); 00365 } 00366 00367 00368 /** 00369 * Performs a refresh during the next loop of the refresh thread 00370 */ 00371 void 00372 ImageWidget::refresh_cam() 00373 { 00374 if ( __cam_enabled ) { 00375 __refresh_thread->refresh_cam(); 00376 } 00377 } 00378 00379 /** 00380 * Sets the widgets pixbuf after (i.e. non blocking) retrieving the image 00381 * over the network. 00382 */ 00383 void 00384 ImageWidget::set_cam() 00385 { 00386 if ( !__cam_enabled ) { return; } 00387 00388 __cam_mutex->lock(); 00389 00390 if (__cam_has_buffer) { 00391 show(__cam->colorspace(), __cam->buffer(), __cam->pixel_width(), __cam->pixel_height()); 00392 __cam->flush(); 00393 __cam_has_buffer = false; 00394 } 00395 00396 __cam_mutex->unlock(); 00397 } 00398 00399 /** 00400 * Saves the current content of the Image 00401 * @param filename of the output 00402 * @param type of the output (By default, "jpeg", "png", "ico" and "bmp" 00403 * are possible file formats to save in, but more formats may be 00404 * installed. The list of all writable formats can be determined 00405 * by using Gdk::Pixbuf::get_formats() with 00406 * Gdk::PixbufFormat::is_writable().) 00407 * @return true on success, false otherwise 00408 */ 00409 bool 00410 ImageWidget::save_image(std::string filename, Glib::ustring type) const throw() 00411 { 00412 __cam_mutex->lock(); 00413 00414 try { 00415 #ifdef GLIBMM_EXCEPTIONS_ENABLED 00416 __pixbuf->save(filename, type); 00417 #else 00418 std::auto_ptr<Glib::Error> error; 00419 __pixbuf->save(filename, type, error); 00420 #endif 00421 __cam_mutex->unlock(); 00422 return true; 00423 } 00424 catch (Glib::Exception &e) { 00425 __cam_mutex->unlock(); 00426 printf("save failed: %s\n", e.what().c_str()); 00427 return false; 00428 } 00429 } 00430 00431 /** 00432 * Saves the content of the image on every refresh 00433 * 00434 * @param enable enables or disables the feature 00435 * @param path to save the images at 00436 * @param type file type (@see ImageWidget::save_image) 00437 * @param img_num of which to start the numbering (actually the first 00438 * image is numbered img_num + 1) 00439 */ 00440 void 00441 ImageWidget::save_on_refresh_cam(bool enable, std::string path, Glib::ustring type, unsigned int img_num) 00442 { 00443 __refresh_thread->save_on_refresh(enable, path, type, img_num); 00444 } 00445 00446 /** 00447 * Returns the latest image number 00448 * @return the latest image number 00449 */ 00450 unsigned int 00451 ImageWidget::get_image_num() 00452 { 00453 return __refresh_thread->get_img_num(); 00454 } 00455 00456 /** 00457 * Creates a new refresh thread 00458 * 00459 * @param widget to be refreshed 00460 * @param refresh_delay time between two refreshes (in [ms]) 00461 */ 00462 ImageWidget::RefThread::RefThread(ImageWidget *widget, unsigned int refresh_delay) 00463 : Thread("ImageWidget refresh thread") 00464 { 00465 set_delete_on_exit(true); 00466 00467 __widget = widget; 00468 __stop = false; 00469 __do_refresh = false; 00470 00471 __save_imgs = false; 00472 __save_num = 0; 00473 00474 __dispatcher.connect( sigc::mem_fun( *widget , &ImageWidget::set_cam ) ); 00475 00476 set_delay(refresh_delay); 00477 } 00478 00479 /** 00480 * Sets the refresh delay for automatic camera refreshes 00481 * 00482 * @param refresh_delay im [ms] 00483 */ 00484 void 00485 ImageWidget::RefThread::set_delay(unsigned int refresh_delay) 00486 { 00487 __refresh_delay = refresh_delay; 00488 __loop_cnt = 0; 00489 } 00490 00491 /** 00492 * Refreshes the camera during the next loop 00493 */ 00494 void 00495 ImageWidget::RefThread::refresh_cam() 00496 { 00497 __do_refresh = true; 00498 } 00499 00500 /** 00501 * Refreshes the Image (getting a new frame from the camera) 00502 */ 00503 void 00504 ImageWidget::RefThread::perform_refresh() 00505 { 00506 if (!__widget->__cam) { 00507 throw fawkes::NullPointerException("Camera hasn't been given during creation"); 00508 } 00509 00510 try { 00511 if (__widget->__cam_mutex->try_lock()) { 00512 __widget->__cam->dispose_buffer(); 00513 __widget->__cam->capture(); 00514 if (!__stop) { 00515 __widget->__cam_has_buffer = true; 00516 __widget->__cam_mutex->unlock(); 00517 00518 if (__widget->__cam->ready()) { 00519 __dispatcher(); 00520 00521 if (__save_imgs) { 00522 char *ctmp; 00523 if (__widget->__cam_has_timestamp) { 00524 try { 00525 fawkes::Time *ts = __widget->__cam->capture_time(); 00526 if (asprintf(&ctmp, "%s/%06u.%ld.%s", __save_path.c_str(), ++__save_num, ts->in_msec(), __save_type.c_str()) != -1) { 00527 Glib::ustring fn = ctmp; 00528 __widget->save_image(fn, __save_type); 00529 free(ctmp); 00530 } else { 00531 printf("Cannot save image, asprintf() ran out of memory\n"); 00532 } 00533 delete ts; 00534 } 00535 catch (fawkes::Exception &e) { 00536 printf("Cannot save image (%s)\n", e.what()); 00537 } 00538 } 00539 else { 00540 if (asprintf(&ctmp, "%s/%06u.%s", __save_path.c_str(), ++__save_num, __save_type.c_str()) != -1) { 00541 Glib::ustring fn = ctmp; 00542 __widget->save_image(fn, __save_type); 00543 free(ctmp); 00544 } else { 00545 printf("Cannot save image, asprintf() ran out of memory\n"); 00546 } 00547 } 00548 } 00549 } 00550 } 00551 } 00552 } 00553 catch (fawkes::Exception &e) { 00554 printf("Could not capture the image (%s)\n", e.what()); 00555 } 00556 } 00557 00558 00559 void 00560 ImageWidget::RefThread::loop() 00561 { 00562 if (!__stop) { 00563 ++__loop_cnt; 00564 00565 if (__refresh_delay && !(__loop_cnt % __refresh_delay)) { 00566 perform_refresh(); 00567 __do_refresh = false; 00568 __loop_cnt = 0; 00569 } 00570 00571 if (__do_refresh) { 00572 perform_refresh(); 00573 __do_refresh = false; 00574 __loop_cnt = 0; 00575 } 00576 } 00577 else exit(); 00578 00579 Glib::usleep(1000); 00580 } 00581 00582 /** 00583 * Stops (and destroys) the thread as soon as possible (at the next loop) 00584 */ 00585 void 00586 ImageWidget::RefThread::stop() 00587 { 00588 __stop = true; 00589 } 00590 00591 00592 void 00593 ImageWidget::RefThread::save_on_refresh(bool enabled, std::string path, Glib::ustring type, unsigned int img_num) 00594 { 00595 __save_imgs = enabled; 00596 00597 if (__save_imgs) { 00598 __save_path = path; 00599 __save_type = type; 00600 __save_num = img_num; 00601 } 00602 } 00603 00604 unsigned int 00605 ImageWidget::RefThread::get_img_num() 00606 { 00607 return __save_num; 00608 } 00609 00610 } // end namespace firevision