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