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 <fvcams/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 /** Constructor for Gtk::Builder. 00098 * Constructor that can be used to instantiate an ImageWidget as a 00099 * derived widget from a Gtk builder file. 00100 * 00101 * Note: The ImageWidget (and its internal buffer) is set to the size 00102 * as in the UI file, in case no camera is set afterwards. Use @see 00103 * ImageWidget::set_size() to resize the ImageWidget afterwards. 00104 * 00105 * @param cobject pointer to the base object 00106 * @param builder Builder 00107 */ 00108 ImageWidget::ImageWidget(BaseObjectType* cobject, Glib::RefPtr<Gtk::Builder> builder) 00109 : Gtk::Image(cobject) 00110 { 00111 __cam = NULL; 00112 __cam_enabled = false; 00113 __cam_mutex = new fawkes::Mutex; 00114 __refresh_thread = NULL; 00115 // set_size(Gtk::Image::get_width(), Gtk::Image::get_height()); 00116 } 00117 00118 #ifdef HAVE_GLADEMM 00119 /** Constructor for Glade. 00120 * Constructor that can be used to instantiate an ImageWidget as a 00121 * derived widget from a Glade file. 00122 * 00123 * Note: The ImageWidget (and its internal buffer) is set to the size 00124 * as in the glade file, in case no camera is set afterwards. Use @see 00125 * ImageWidget::set_size() to resize the ImageWidget afterwards. 00126 * 00127 * @param cobject pointer to the base object 00128 * @param refxml the Glade XML file 00129 */ 00130 ImageWidget::ImageWidget(BaseObjectType* cobject, Glib::RefPtr<Gnome::Glade::Xml> refxml) 00131 : Gtk::Image( cobject ) 00132 { 00133 __cam = NULL; 00134 __cam_enabled = false; 00135 __cam_mutex = new fawkes::Mutex; 00136 __refresh_thread = NULL; 00137 00138 // set_size(Gtk::Image::get_width(), Gtk::Image::get_height()); 00139 } 00140 #endif 00141 00142 /** 00143 * Destructor 00144 */ 00145 ImageWidget::~ImageWidget() 00146 { 00147 if (__refresh_thread) __refresh_thread->stop(); 00148 delete __cam_mutex; 00149 } 00150 00151 /** Set the camera from which the ImageWidget obtains the images. 00152 * 00153 * Note: The size of the ImageWidget remains untouched and the cameras 00154 * image gets scaled appropriately. Use ImageWidget::set_size(0, 0) to 00155 * set the widget to the size of the camera. 00156 * 00157 * @param cam the camera 00158 * @param refresh_delay the delay between two refreshs in milliseconds 00159 */ 00160 void 00161 ImageWidget::set_camera(Camera *cam, unsigned int refresh_delay) 00162 { 00163 __cam = cam; 00164 __cam_enabled = true; 00165 __cam_has_buffer = false; 00166 00167 set_size(__cam->pixel_width(), __cam->pixel_height()); 00168 00169 try { 00170 fawkes::Time *time = __cam->capture_time(); 00171 delete time; 00172 __cam_has_timestamp = true; 00173 } 00174 catch (fawkes::Exception &e) { 00175 __cam_has_timestamp = false; 00176 } 00177 00178 if ( __refresh_thread ) { 00179 __refresh_thread->set_delay(refresh_delay); 00180 } else { 00181 __refresh_thread = new RefThread(this, refresh_delay); 00182 __refresh_thread->start(); 00183 } 00184 00185 __refresh_thread->refresh_cam(); 00186 } 00187 00188 /** 00189 * En-/disable the camera. 00190 * @param enable if true the camera is enabled and the refresh thread 00191 * is start, if false the refresh thread is stopped and the camera is 00192 * disabled 00193 */ 00194 void 00195 ImageWidget::enable_camera(bool enable) 00196 { 00197 if ( !enable && __cam_enabled ) { 00198 __refresh_thread->stop(); 00199 } else if ( __refresh_thread && enable && !__cam_enabled ) { 00200 __refresh_thread->start(); 00201 } 00202 00203 __cam_enabled = enable; 00204 } 00205 00206 /** Sets the size of the ImageWidget. 00207 * Updates the internal buffer and the size request for the ImageWidget. 00208 * If width and/or height are set to 0 (and a Camera is set) the 00209 * ImageWidget will be set to the camera dimensions. 00210 * 00211 * Note: The ImageWidget must be refreshed after changing its size! 00212 * 00213 * @param width The new width 00214 * @param height The new height 00215 */ 00216 void 00217 ImageWidget::set_size(unsigned int width, unsigned int height) 00218 { 00219 if (!width || ! height) { 00220 if (__cam) { 00221 width = __cam->pixel_width(); 00222 height = __cam->pixel_height(); 00223 } 00224 else { 00225 throw fawkes::IllegalArgumentException("ImageWidget::set_size(): width and/or height may not be 0 if no Camera is set"); 00226 } 00227 } 00228 00229 if (!__pixbuf || __width != width || __height != height) { 00230 __width = width; 00231 __height = height; 00232 00233 #if GLIBMM_MAJOR_VERSION > 2 || ( GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION >= 14 ) 00234 __pixbuf.reset(); 00235 #else 00236 __pixbuf.clear(); 00237 #endif 00238 00239 __pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, __width, __height); 00240 00241 set_size_request(__width, __height); 00242 } 00243 } 00244 /** 00245 * Returns the image buffer width 00246 * @return width of the contained image 00247 */ 00248 unsigned int 00249 ImageWidget::get_width() const 00250 { 00251 return __width; 00252 } 00253 00254 /** 00255 * Returns the image buffer height 00256 * @return height of the contained image 00257 */ 00258 unsigned int 00259 ImageWidget::get_height() const 00260 { 00261 return __height; 00262 } 00263 00264 /** 00265 * Returns the widgets pixel buffer (RGB!) 00266 * @return the RGB pixel buffer 00267 */ 00268 Glib::RefPtr<Gdk::Pixbuf> 00269 ImageWidget::get_buffer() const 00270 { 00271 return __pixbuf; 00272 } 00273 00274 /** 00275 * Sets a pixel to the given RGB colors 00276 * 00277 * @param x position of the pixel 00278 * @param y position of the pixel 00279 * @param r component of the color 00280 * @param g component of the color 00281 * @param b component of the color 00282 */ 00283 void 00284 ImageWidget::set_rgb(unsigned int x, unsigned int y, unsigned char r, unsigned char g, unsigned char b) 00285 { 00286 set_rgb (x, y, (RGB_t){r, g, b}); 00287 } 00288 00289 /** 00290 * Sets a pixel to the given RGB colors 00291 * 00292 * @param x position of the pixel 00293 * @param y position of the pixel 00294 * @param rgb the color 00295 */ 00296 void 00297 ImageWidget::set_rgb(unsigned int x, unsigned int y, RGB_t rgb) 00298 { 00299 if (x >= __width) throw fawkes::OutOfBoundsException("x-Coordinate exeeds image width", x, 0, __width); 00300 if (y >= __height) throw fawkes::OutOfBoundsException("y-Coordinate exeeds image height", x, 0, __height); 00301 00302 RGB_t * target = RGB_PIXEL_AT(__pixbuf->get_pixels(), __width, x, y); 00303 *target = rgb; 00304 } 00305 00306 /** 00307 * Show image from given colorspace. 00308 * Warning: If width and/or height not set, it is assumed, that the given 00309 * buffer has the same dimension as the widget. 00310 * 00311 * @param colorspace colorspace of the supplied buffer 00312 * @param buffer image buffer 00313 * @param width Width of the provided buffer (may be scaled to ImageWidget 00314 * dimensions) 00315 * @param height Height of the provided buffer (may be scaled to 00316 * ImageWidget dimensions) 00317 * @return TRUE if the buffer chould have been shown 00318 */ 00319 bool 00320 ImageWidget::show(colorspace_t colorspace, unsigned char *buffer, unsigned int width, unsigned int height) 00321 { 00322 try { 00323 if (!width || !height || (width == __width && height == __height)) { 00324 convert(colorspace, RGB, buffer, __pixbuf->get_pixels(), __width, __height); 00325 } 00326 else { 00327 unsigned char *scaled_buffer = (unsigned char *)malloc(colorspace_buffer_size(colorspace, __width, __height)); 00328 00329 if (scaled_buffer) { 00330 LossyScaler scaler; 00331 scaler.set_original_buffer(buffer); 00332 scaler.set_original_dimensions(width, height); 00333 scaler.set_scaled_buffer(scaled_buffer); 00334 scaler.set_scaled_dimensions(__width, __height); 00335 scaler.scale(); 00336 00337 convert(colorspace, RGB, scaled_buffer, __pixbuf->get_pixels(), __width, __height); 00338 00339 free(scaled_buffer); 00340 } 00341 } 00342 } 00343 catch (fawkes::Exception &e) { 00344 printf("ImageWidget::show(): %s\n", e.what()); 00345 return false; 00346 } 00347 00348 try { 00349 set(__pixbuf); 00350 __signal_show.emit(colorspace, buffer, width, height); 00351 return true; 00352 } 00353 catch (fawkes::Exception &e) { 00354 printf("ImageWidget::show(): Could not set the new image (%s)\n", e.what()); 00355 } 00356 00357 return false; 00358 } 00359 00360 00361 /** Signal emits after a new buffer gets successfully shown 00362 * (see @see ImageWidget::show()). 00363 * 00364 * The buffer's validity can not be guaranteed beyond the called functions 00365 * scope! In case the source of the widget is a Camera, the buffer gets 00366 * disposed after calling ImageWidget::show. 00367 * 00368 * @return The signal_show signal 00369 */ 00370 sigc::signal<void, colorspace_t, unsigned char *, unsigned int, unsigned int> & 00371 ImageWidget::signal_show() 00372 { 00373 return __signal_show; 00374 } 00375 00376 00377 /** 00378 * Sets the refresh delay for automatic camera refreshes 00379 * 00380 * @param refresh_delay im [ms] 00381 */ 00382 void 00383 ImageWidget::set_refresh_delay(unsigned int refresh_delay) 00384 { 00385 __refresh_thread->set_delay(refresh_delay); 00386 } 00387 00388 00389 /** 00390 * Performs a refresh during the next loop of the refresh thread 00391 */ 00392 void 00393 ImageWidget::refresh_cam() 00394 { 00395 if ( __cam_enabled ) { 00396 __refresh_thread->refresh_cam(); 00397 } 00398 } 00399 00400 /** 00401 * Sets the widgets pixbuf after (i.e. non blocking) retrieving the image 00402 * over the network. 00403 */ 00404 void 00405 ImageWidget::set_cam() 00406 { 00407 if ( !__cam_enabled ) { return; } 00408 00409 __cam_mutex->lock(); 00410 00411 if (__cam_has_buffer) { 00412 show(__cam->colorspace(), __cam->buffer(), __cam->pixel_width(), __cam->pixel_height()); 00413 __cam->flush(); 00414 __cam_has_buffer = false; 00415 } 00416 00417 __cam_mutex->unlock(); 00418 } 00419 00420 /** 00421 * Saves the current content of the Image 00422 * @param filename of the output 00423 * @param type of the output (By default, "jpeg", "png", "ico" and "bmp" 00424 * are possible file formats to save in, but more formats may be 00425 * installed. The list of all writable formats can be determined 00426 * by using Gdk::Pixbuf::get_formats() with 00427 * Gdk::PixbufFormat::is_writable().) 00428 * @return true on success, false otherwise 00429 */ 00430 bool 00431 ImageWidget::save_image(std::string filename, Glib::ustring type) const throw() 00432 { 00433 __cam_mutex->lock(); 00434 00435 try { 00436 #ifdef GLIBMM_EXCEPTIONS_ENABLED 00437 __pixbuf->save(filename, type); 00438 #else 00439 std::auto_ptr<Glib::Error> error; 00440 __pixbuf->save(filename, type, error); 00441 #endif 00442 __cam_mutex->unlock(); 00443 return true; 00444 } 00445 catch (Glib::Exception &e) { 00446 __cam_mutex->unlock(); 00447 printf("save failed: %s\n", e.what().c_str()); 00448 return false; 00449 } 00450 } 00451 00452 /** 00453 * Saves the content of the image on every refresh 00454 * 00455 * @param enable enables or disables the feature 00456 * @param path to save the images at 00457 * @param type file type (@see ImageWidget::save_image) 00458 * @param img_num of which to start the numbering (actually the first 00459 * image is numbered img_num + 1) 00460 */ 00461 void 00462 ImageWidget::save_on_refresh_cam(bool enable, std::string path, Glib::ustring type, unsigned int img_num) 00463 { 00464 __refresh_thread->save_on_refresh(enable, path, type, img_num); 00465 } 00466 00467 /** 00468 * Returns the latest image number 00469 * @return the latest image number 00470 */ 00471 unsigned int 00472 ImageWidget::get_image_num() 00473 { 00474 return __refresh_thread->get_img_num(); 00475 } 00476 00477 /** 00478 * Creates a new refresh thread 00479 * 00480 * @param widget to be refreshed 00481 * @param refresh_delay time between two refreshes (in [ms]) 00482 */ 00483 ImageWidget::RefThread::RefThread(ImageWidget *widget, unsigned int refresh_delay) 00484 : Thread("ImageWidget refresh thread") 00485 { 00486 set_delete_on_exit(true); 00487 00488 __widget = widget; 00489 __stop = false; 00490 __do_refresh = false; 00491 00492 __save_imgs = false; 00493 __save_num = 0; 00494 00495 __dispatcher.connect( sigc::mem_fun( *widget , &ImageWidget::set_cam ) ); 00496 00497 set_delay(refresh_delay); 00498 } 00499 00500 /** 00501 * Sets the refresh delay for automatic camera refreshes 00502 * 00503 * @param refresh_delay im [ms] 00504 */ 00505 void 00506 ImageWidget::RefThread::set_delay(unsigned int refresh_delay) 00507 { 00508 __refresh_delay = refresh_delay; 00509 __loop_cnt = 0; 00510 } 00511 00512 /** 00513 * Refreshes the camera during the next loop 00514 */ 00515 void 00516 ImageWidget::RefThread::refresh_cam() 00517 { 00518 __do_refresh = true; 00519 } 00520 00521 /** 00522 * Refreshes the Image (getting a new frame from the camera) 00523 */ 00524 void 00525 ImageWidget::RefThread::perform_refresh() 00526 { 00527 if (!__widget->__cam) { 00528 throw fawkes::NullPointerException("Camera hasn't been given during creation"); 00529 } 00530 00531 try { 00532 if (__widget->__cam_mutex->try_lock()) { 00533 __widget->__cam->dispose_buffer(); 00534 __widget->__cam->capture(); 00535 if (!__stop) { 00536 __widget->__cam_has_buffer = true; 00537 __widget->__cam_mutex->unlock(); 00538 00539 if (__widget->__cam->ready()) { 00540 __dispatcher(); 00541 00542 if (__save_imgs) { 00543 char *ctmp; 00544 if (__widget->__cam_has_timestamp) { 00545 try { 00546 fawkes::Time *ts = __widget->__cam->capture_time(); 00547 if (asprintf(&ctmp, "%s/%06u.%ld.%s", __save_path.c_str(), ++__save_num, ts->in_msec(), __save_type.c_str()) != -1) { 00548 Glib::ustring fn = ctmp; 00549 __widget->save_image(fn, __save_type); 00550 free(ctmp); 00551 } else { 00552 printf("Cannot save image, asprintf() ran out of memory\n"); 00553 } 00554 delete ts; 00555 } 00556 catch (fawkes::Exception &e) { 00557 printf("Cannot save image (%s)\n", e.what()); 00558 } 00559 } 00560 else { 00561 if (asprintf(&ctmp, "%s/%06u.%s", __save_path.c_str(), ++__save_num, __save_type.c_str()) != -1) { 00562 Glib::ustring fn = ctmp; 00563 __widget->save_image(fn, __save_type); 00564 free(ctmp); 00565 } else { 00566 printf("Cannot save image, asprintf() ran out of memory\n"); 00567 } 00568 } 00569 } 00570 } 00571 } 00572 } 00573 } 00574 catch (fawkes::Exception &e) { 00575 printf("Could not capture the image (%s)\n", e.what()); 00576 } 00577 } 00578 00579 00580 void 00581 ImageWidget::RefThread::loop() 00582 { 00583 if (!__stop) { 00584 ++__loop_cnt; 00585 00586 if (__refresh_delay && !(__loop_cnt % __refresh_delay)) { 00587 perform_refresh(); 00588 __do_refresh = false; 00589 __loop_cnt = 0; 00590 } 00591 00592 if (__do_refresh) { 00593 perform_refresh(); 00594 __do_refresh = false; 00595 __loop_cnt = 0; 00596 } 00597 } 00598 else exit(); 00599 00600 Glib::usleep(1000); 00601 } 00602 00603 /** 00604 * Stops (and destroys) the thread as soon as possible (at the next loop) 00605 */ 00606 void 00607 ImageWidget::RefThread::stop() 00608 { 00609 __stop = true; 00610 } 00611 00612 00613 /** Set save on refresh. 00614 * @param enabled true to enable, false to disable 00615 * @param path save path 00616 * @param type save type 00617 * @param img_num image number to save 00618 */ 00619 void 00620 ImageWidget::RefThread::save_on_refresh(bool enabled, std::string path, Glib::ustring type, unsigned int img_num) 00621 { 00622 __save_imgs = enabled; 00623 00624 if (__save_imgs) { 00625 __save_path = path; 00626 __save_type = type; 00627 __save_num = img_num; 00628 } 00629 } 00630 00631 /** Get image number. 00632 * @return image number 00633 */ 00634 unsigned int 00635 ImageWidget::RefThread::get_img_num() 00636 { 00637 return __save_num; 00638 } 00639 00640 } // end namespace firevision