image_widget.cpp

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