Point Cloud Library (PCL)
1.7.0
|
00001 /* 00002 * Software License Agreement (BSD License) 00003 * 00004 * Point Cloud Library (PCL) - www.pointclouds.org 00005 * Copyright (c) 2010-2012, Willow Garage, Inc. 00006 * Copyright (c) 2012-, Open Perception, Inc. 00007 * 00008 * All rights reserved. 00009 * 00010 * Redistribution and use in source and binary forms, with or without 00011 * modification, are permitted provided that the following conditions 00012 * are met: 00013 * 00014 * * Redistributions of source code must retain the above copyright 00015 * notice, this list of conditions and the following disclaimer. 00016 * * Redistributions in binary form must reproduce the above 00017 * copyright notice, this list of conditions and the following 00018 * disclaimer in the documentation and/or other materials provided 00019 * with the distribution. 00020 * * Neither the name of the copyright holder(s) nor the names of its 00021 * contributors may be used to endorse or promote products derived 00022 * from this software without specific prior written permission. 00023 * 00024 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00025 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00026 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00027 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00028 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00029 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00030 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00031 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00032 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00033 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00034 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00035 * POSSIBILITY OF SUCH DAMAGE. 00036 * 00037 */ 00038 00039 #ifndef PCL_VISUALIZATION_IMAGE_VISUALIZER_H__ 00040 #define PCL_VISUALIZATION_IMAGE_VISUALIZER_H__ 00041 00042 #include <pcl/pcl_macros.h> 00043 #include <pcl/point_types.h> 00044 #include <pcl/console/print.h> 00045 #include <pcl/visualization/interactor.h> 00046 #include <pcl/visualization/interactor_style.h> 00047 #include <pcl/visualization/vtk/pcl_image_canvas_source_2d.h> 00048 #include <pcl/visualization/vtk/pcl_context_item.h> 00049 #include <pcl/geometry/planar_polygon.h> 00050 #include <pcl/correspondence.h> 00051 00052 #include <boost/shared_array.hpp> 00053 00054 #include <vtkInteractorStyleImage.h> 00055 00056 class vtkImageSlice; 00057 class vtkContextActor; 00058 class vtkImageViewer; 00059 class vtkImageFlip; 00060 00061 namespace pcl 00062 { 00063 namespace visualization 00064 { 00065 typedef Eigen::Array<unsigned char, 3, 1> Vector3ub; 00066 static const Vector3ub green_color (0, 255, 0); 00067 static const Vector3ub red_color (255, 0, 0); 00068 static const Vector3ub blue_color (0, 0, 255); 00069 00070 /** \brief An image viewer interactor style, tailored for ImageViewer. 00071 * \author Radu B. Rusu 00072 * \ingroup visualization 00073 */ 00074 class PCL_EXPORTS ImageViewerInteractorStyle : public vtkInteractorStyleImage 00075 { 00076 public: 00077 static ImageViewerInteractorStyle *New (); 00078 ImageViewerInteractorStyle (); 00079 00080 virtual void OnMouseWheelForward () {} 00081 virtual void OnMouseWheelBackward () {} 00082 virtual void OnMiddleButtonDown () {} 00083 virtual void OnRightButtonDown () {} 00084 virtual void OnLeftButtonDown (); 00085 00086 virtual void 00087 OnChar (); 00088 00089 void 00090 adjustCamera (vtkImageData *image, vtkRenderer *ren); 00091 00092 void 00093 adjustCamera (vtkRenderer *ren); 00094 }; 00095 00096 /** \brief ImageViewer is a class for 2D image visualization. 00097 * 00098 * Features include: 00099 * - add and remove different layers with different opacity (transparency) values 00100 * - add 2D geometric shapes (circles, boxes, etc) in separate layers 00101 * - display RGB, monochrome, float, angle images 00102 * 00103 * Simple usage example: 00104 * \code 00105 * pcl::visualization::ImageViewer iv; 00106 * iv.addCircle (10, 10, 5, 1.0, 0.0, 0.0, "circles", 1.0); // add a red, fully opaque circle with radius 5 pixels at (10,10) in layer "circles" 00107 * iv.addFilledRectangle (10, 20, 10, 20, 0.0, 1.0, 0.0, "boxes", 0.5); // add a green, 50% transparent box at (10,10->20,20) in layer "boxes" 00108 * iv.addRGBImage<pcl::PointXYZRGBA> (cloud); // add a RGB image from a point cloud dataset in an "rgb_image" default layer 00109 * iv.spin (); // press 'q' to exit 00110 * iv.removeLayer ("circles"); // remove layer "circles" 00111 * iv.spin (); // press 'q' to exit 00112 * \endcode 00113 * 00114 * \author Radu B. Rusu, Suat Gedikli 00115 * \ingroup visualization 00116 */ 00117 class PCL_EXPORTS ImageViewer 00118 { 00119 public: 00120 typedef boost::shared_ptr<ImageViewer> Ptr; 00121 00122 /** \brief Constructor. 00123 * \param[in] window_title the title of the window 00124 */ 00125 ImageViewer (const std::string& window_title = ""); 00126 00127 /** \brief Destructor. */ 00128 virtual ~ImageViewer (); 00129 00130 /** \brief Show a monochrome 2D image on screen. 00131 * \param[in] data the input data representing the image 00132 * \param[in] width the width of the image 00133 * \param[in] height the height of the image 00134 * \param[in] layer_id the name of the layer (default: "image") 00135 * \param[in] opacity the opacity of the layer (default: 1.0) 00136 */ 00137 void 00138 showMonoImage (const unsigned char* data, unsigned width, unsigned height, 00139 const std::string &layer_id = "mono_image", double opacity = 1.0); 00140 00141 /** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update). 00142 * \param[in] data the input data representing the image 00143 * \param[in] width the width of the image 00144 * \param[in] height the height of the image 00145 * \param[in] layer_id the name of the layer (default: "image") 00146 * \param[in] opacity the opacity of the layer (default: 1.0) 00147 */ 00148 void 00149 addMonoImage (const unsigned char* data, unsigned width, unsigned height, 00150 const std::string &layer_id = "mono_image", double opacity = 1.0); 00151 00152 /** \brief Show a monochrome 2D image on screen. 00153 * \param[in] cloud the input data representing the grayscale point cloud 00154 * \param[in] layer_id the name of the layer (default: "image") 00155 * \param[in] opacity the opacity of the layer (default: 1.0) 00156 */ 00157 inline void 00158 showMonoImage (const pcl::PointCloud<pcl::Intensity>::ConstPtr &cloud, 00159 const std::string &layer_id = "mono_image", double opacity = 1.0) 00160 { 00161 return (showMonoImage (*cloud, layer_id, opacity)); 00162 } 00163 00164 /** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update). 00165 * \param[in] cloud the input data representing the grayscale point cloud 00166 * \param[in] layer_id the name of the layer (default: "image") 00167 * \param[in] opacity the opacity of the layer (default: 1.0) 00168 */ 00169 inline void 00170 addMonoImage (const pcl::PointCloud<pcl::Intensity>::ConstPtr &cloud, 00171 const std::string &layer_id = "mono_image", double opacity = 1.0) 00172 { 00173 return (addMonoImage (*cloud, layer_id, opacity)); 00174 } 00175 00176 /** \brief Show a monochrome 2D image on screen. 00177 * \param[in] cloud the input data representing the grayscale point cloud 00178 * \param[in] layer_id the name of the layer (default: "image") 00179 * \param[in] opacity the opacity of the layer (default: 1.0) 00180 */ 00181 void 00182 showMonoImage (const pcl::PointCloud<pcl::Intensity> &cloud, 00183 const std::string &layer_id = "mono_image", double opacity = 1.0); 00184 00185 /** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update). 00186 * \param[in] cloud the input data representing the RGB point cloud 00187 * \param[in] layer_id the name of the layer (default: "image") 00188 * \param[in] opacity the opacity of the layer (default: 1.0) 00189 */ 00190 void 00191 addMonoImage (const pcl::PointCloud<pcl::Intensity> &cloud, 00192 const std::string &layer_id = "mono_image", double opacity = 1.0); 00193 00194 /** \brief Show a monochrome 2D image on screen. 00195 * \param[in] cloud the input data representing the grayscale point cloud 00196 * \param[in] layer_id the name of the layer (default: "image") 00197 * \param[in] opacity the opacity of the layer (default: 1.0) 00198 */ 00199 inline void 00200 showMonoImage (const pcl::PointCloud<pcl::Intensity8u>::ConstPtr &cloud, 00201 const std::string &layer_id = "mono_image", double opacity = 1.0) 00202 { 00203 return (showMonoImage (*cloud, layer_id, opacity)); 00204 } 00205 00206 /** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update). 00207 * \param[in] cloud the input data representing the grayscale point cloud 00208 * \param[in] layer_id the name of the layer (default: "image") 00209 * \param[in] opacity the opacity of the layer (default: 1.0) 00210 */ 00211 inline void 00212 addMonoImage (const pcl::PointCloud<pcl::Intensity8u>::ConstPtr &cloud, 00213 const std::string &layer_id = "mono_image", double opacity = 1.0) 00214 { 00215 return (addMonoImage (*cloud, layer_id, opacity)); 00216 } 00217 00218 /** \brief Show a monochrome 2D image on screen. 00219 * \param[in] cloud the input data representing the grayscale point cloud 00220 * \param[in] layer_id the name of the layer (default: "image") 00221 * \param[in] opacity the opacity of the layer (default: 1.0) 00222 */ 00223 void 00224 showMonoImage (const pcl::PointCloud<pcl::Intensity8u> &cloud, 00225 const std::string &layer_id = "mono_image", double opacity = 1.0); 00226 00227 /** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update). 00228 * \param[in] cloud the input data representing the RGB point cloud 00229 * \param[in] layer_id the name of the layer (default: "image") 00230 * \param[in] opacity the opacity of the layer (default: 1.0) 00231 */ 00232 void 00233 addMonoImage (const pcl::PointCloud<pcl::Intensity8u> &cloud, 00234 const std::string &layer_id = "mono_image", double opacity = 1.0); 00235 00236 /** \brief Show a 2D RGB image on screen. 00237 * \param[in] data the input data representing the image 00238 * \param[in] width the width of the image 00239 * \param[in] height the height of the image 00240 * \param[in] layer_id the name of the layer (default: "image") 00241 * \param[in] opacity the opacity of the layer (default: 1.0) 00242 */ 00243 void 00244 showRGBImage (const unsigned char* data, unsigned width, unsigned height, 00245 const std::string &layer_id = "rgb_image", double opacity = 1.0); 00246 00247 /** \brief Add an RGB 2D image layer, but do not render it (use spin/spinOnce to update). 00248 * \param[in] data the input data representing the image 00249 * \param[in] width the width of the image 00250 * \param[in] height the height of the image 00251 * \param[in] layer_id the name of the layer (default: "image") 00252 * \param[in] opacity the opacity of the layer (default: 1.0) 00253 */ 00254 void 00255 addRGBImage (const unsigned char* data, unsigned width, unsigned height, 00256 const std::string &layer_id = "rgb_image", double opacity = 1.0); 00257 00258 /** \brief Show a 2D image on screen, obtained from the RGB channel of a point cloud. 00259 * \param[in] cloud the input data representing the RGB point cloud 00260 * \param[in] layer_id the name of the layer (default: "image") 00261 * \param[in] opacity the opacity of the layer (default: 1.0) 00262 */ 00263 template <typename T> inline void 00264 showRGBImage (const typename pcl::PointCloud<T>::ConstPtr &cloud, 00265 const std::string &layer_id = "rgb_image", double opacity = 1.0) 00266 { 00267 return (showRGBImage<T> (*cloud, layer_id, opacity)); 00268 } 00269 00270 /** \brief Add an RGB 2D image layer, but do not render it (use spin/spinOnce to update). 00271 * \param[in] cloud the input data representing the RGB point cloud 00272 * \param[in] layer_id the name of the layer (default: "image") 00273 * \param[in] opacity the opacity of the layer (default: 1.0) 00274 */ 00275 template <typename T> inline void 00276 addRGBImage (const typename pcl::PointCloud<T>::ConstPtr &cloud, 00277 const std::string &layer_id = "rgb_image", double opacity = 1.0) 00278 { 00279 return (addRGBImage<T> (*cloud, layer_id, opacity)); 00280 } 00281 00282 /** \brief Show a 2D image on screen, obtained from the RGB channel of a point cloud. 00283 * \param[in] cloud the input data representing the RGB point cloud 00284 * \param[in] layer_id the name of the layer (default: "image") 00285 * \param[in] opacity the opacity of the layer (default: 1.0) 00286 */ 00287 template <typename T> void 00288 showRGBImage (const pcl::PointCloud<T> &cloud, 00289 const std::string &layer_id = "rgb_image", double opacity = 1.0); 00290 00291 /** \brief Add an RGB 2D image layer, but do not render it (use spin/spinOnce to update). 00292 * \param[in] cloud the input data representing the RGB point cloud 00293 * \param[in] layer_id the name of the layer (default: "image") 00294 * \param[in] opacity the opacity of the layer (default: 1.0) 00295 */ 00296 template <typename T> void 00297 addRGBImage (const pcl::PointCloud<T> &cloud, 00298 const std::string &layer_id = "rgb_image", double opacity = 1.0); 00299 00300 /** \brief Show a 2D image (float) on screen. 00301 * \param[in] data the input data representing the image in float format 00302 * \param[in] width the width of the image 00303 * \param[in] height the height of the image 00304 * \param[in] min_value filter all values in the image to be larger than this minimum value 00305 * \param[in] max_value filter all values in the image to be smaller than this maximum value 00306 * \param[in] grayscale show data as grayscale (true) or not (false). Default: false 00307 * \param[in] layer_id the name of the layer (default: "image") 00308 * \param[in] opacity the opacity of the layer (default: 1.0) 00309 */ 00310 void 00311 showFloatImage (const float* data, unsigned int width, unsigned int height, 00312 float min_value = std::numeric_limits<float>::min (), 00313 float max_value = std::numeric_limits<float>::max (), bool grayscale = false, 00314 const std::string &layer_id = "float_image", double opacity = 1.0); 00315 00316 /** \brief Add a float 2D image layer, but do not render it (use spin/spinOnce to update). 00317 * \param[in] data the input data representing the image in float format 00318 * \param[in] width the width of the image 00319 * \param[in] height the height of the image 00320 * \param[in] min_value filter all values in the image to be larger than this minimum value 00321 * \param[in] max_value filter all values in the image to be smaller than this maximum value 00322 * \param[in] grayscale show data as grayscale (true) or not (false). Default: false 00323 * \param[in] layer_id the name of the layer (default: "image") 00324 * \param[in] opacity the opacity of the layer (default: 1.0) 00325 */ 00326 void 00327 addFloatImage (const float* data, unsigned int width, unsigned int height, 00328 float min_value = std::numeric_limits<float>::min (), 00329 float max_value = std::numeric_limits<float>::max (), bool grayscale = false, 00330 const std::string &layer_id = "float_image", double opacity = 1.0); 00331 00332 /** \brief Show a 2D image (unsigned short) on screen. 00333 * \param[in] short_image the input data representing the image in unsigned short format 00334 * \param[in] width the width of the image 00335 * \param[in] height the height of the image 00336 * \param[in] min_value filter all values in the image to be larger than this minimum value 00337 * \param[in] max_value filter all values in the image to be smaller than this maximum value 00338 * \param[in] grayscale show data as grayscale (true) or not (false). Default: false 00339 * \param[in] layer_id the name of the layer (default: "image") 00340 * \param[in] opacity the opacity of the layer (default: 1.0) 00341 */ 00342 void 00343 showShortImage (const unsigned short* short_image, unsigned int width, unsigned int height, 00344 unsigned short min_value = std::numeric_limits<unsigned short>::min (), 00345 unsigned short max_value = std::numeric_limits<unsigned short>::max (), bool grayscale = false, 00346 const std::string &layer_id = "short_image", double opacity = 1.0); 00347 00348 /** \brief Add a short 2D image layer, but do not render it (use spin/spinOnce to update). 00349 * \param[in] short_image the input data representing the image in unsigned short format 00350 * \param[in] width the width of the image 00351 * \param[in] height the height of the image 00352 * \param[in] min_value filter all values in the image to be larger than this minimum value 00353 * \param[in] max_value filter all values in the image to be smaller than this maximum value 00354 * \param[in] grayscale show data as grayscale (true) or not (false). Default: false 00355 * \param[in] layer_id the name of the layer (default: "image") 00356 * \param[in] opacity the opacity of the layer (default: 1.0) 00357 */ 00358 void 00359 addShortImage (const unsigned short* short_image, unsigned int width, unsigned int height, 00360 unsigned short min_value = std::numeric_limits<unsigned short>::min (), 00361 unsigned short max_value = std::numeric_limits<unsigned short>::max (), bool grayscale = false, 00362 const std::string &layer_id = "short_image", double opacity = 1.0); 00363 00364 /** \brief Show a 2D image on screen representing angle data. 00365 * \param[in] data the input data representing the image 00366 * \param[in] width the width of the image 00367 * \param[in] height the height of the image 00368 * \param[in] layer_id the name of the layer (default: "image") 00369 * \param[in] opacity the opacity of the layer (default: 1.0) 00370 */ 00371 void 00372 showAngleImage (const float* data, unsigned width, unsigned height, 00373 const std::string &layer_id = "angle_image", double opacity = 1.0); 00374 00375 /** \brief Add an angle 2D image layer, but do not render it (use spin/spinOnce to update). 00376 * \param[in] data the input data representing the image 00377 * \param[in] width the width of the image 00378 * \param[in] height the height of the image 00379 * \param[in] layer_id the name of the layer (default: "image") 00380 * \param[in] opacity the opacity of the layer (default: 1.0) 00381 */ 00382 void 00383 addAngleImage (const float* data, unsigned width, unsigned height, 00384 const std::string &layer_id = "angle_image", double opacity = 1.0); 00385 00386 /** \brief Show a 2D image on screen representing half angle data. 00387 * \param[in] data the input data representing the image 00388 * \param[in] width the width of the image 00389 * \param[in] height the height of the image 00390 * \param[in] layer_id the name of the layer (default: "image") 00391 * \param[in] opacity the opacity of the layer (default: 1.0) 00392 */ 00393 void 00394 showHalfAngleImage (const float* data, unsigned width, unsigned height, 00395 const std::string &layer_id = "half_angle_image", double opacity = 1.0); 00396 00397 /** \brief Add a half angle 2D image layer, but do not render it (use spin/spinOnce to update). 00398 * \param[in] data the input data representing the image 00399 * \param[in] width the width of the image 00400 * \param[in] height the height of the image 00401 * \param[in] layer_id the name of the layer (default: "image") 00402 * \param[in] opacity the opacity of the layer (default: 1.0) 00403 */ 00404 void 00405 addHalfAngleImage (const float* data, unsigned width, unsigned height, 00406 const std::string &layer_id = "half_angle_image", double opacity = 1.0); 00407 00408 /** \brief Sets the pixel at coordinates(u,v) to color while setting the neighborhood to another 00409 * \param[in] u the u/x coordinate of the pixel 00410 * \param[in] v the v/y coordinate of the pixel 00411 * \param[in] fg_color the pixel color 00412 * \param[in] bg_color the neighborhood color 00413 * \param[in] radius the circle radius around the pixel 00414 * \param[in] layer_id the name of the layer (default: "points") 00415 * \param[in] opacity the opacity of the layer (default: 1.0) 00416 */ 00417 void 00418 markPoint (size_t u, size_t v, Vector3ub fg_color, Vector3ub bg_color = red_color, double radius = 3.0, 00419 const std::string &layer_id = "points", double opacity = 1.0); 00420 00421 /** \brief Set the window title name 00422 * \param[in] name the window title 00423 */ 00424 void 00425 setWindowTitle (const std::string& name); 00426 00427 /** \brief Spin method. Calls the interactor and runs an internal loop. */ 00428 void 00429 spin (); 00430 00431 /** \brief Spin once method. Calls the interactor and updates the screen once. 00432 * \param[in] time - How long (in ms) should the visualization loop be allowed to run. 00433 * \param[in] force_redraw - if false it might return without doing anything if the 00434 * interactor's framerate does not require a redraw yet. 00435 */ 00436 void 00437 spinOnce (int time = 1, bool force_redraw = true); 00438 00439 /** \brief Register a callback function for keyboard events 00440 * \param[in] callback the function that will be registered as a callback for a keyboard event 00441 * \param[in] cookie user data that is passed to the callback 00442 * \return a connection object that allows to disconnect the callback function. 00443 */ 00444 boost::signals2::connection 00445 registerKeyboardCallback (void (*callback) (const pcl::visualization::KeyboardEvent&, void*), 00446 void* cookie = NULL) 00447 { 00448 return (registerKeyboardCallback (boost::bind (callback, _1, cookie))); 00449 } 00450 00451 /** \brief Register a callback function for keyboard events 00452 * \param[in] callback the member function that will be registered as a callback for a keyboard event 00453 * \param[in] instance instance to the class that implements the callback function 00454 * \param[in] cookie user data that is passed to the callback 00455 * \return a connection object that allows to disconnect the callback function. 00456 */ 00457 template<typename T> boost::signals2::connection 00458 registerKeyboardCallback (void (T::*callback) (const pcl::visualization::KeyboardEvent&, void*), 00459 T& instance, void* cookie = NULL) 00460 { 00461 return (registerKeyboardCallback (boost::bind (callback, boost::ref (instance), _1, cookie))); 00462 } 00463 00464 /** \brief Register a callback boost::function for keyboard events 00465 * \param[in] cb the boost function that will be registered as a callback for a keyboard event 00466 * \return a connection object that allows to disconnect the callback function. 00467 */ 00468 boost::signals2::connection 00469 registerKeyboardCallback (boost::function<void (const pcl::visualization::KeyboardEvent&)> cb); 00470 00471 /** \brief Register a callback boost::function for mouse events 00472 * \param[in] callback the function that will be registered as a callback for a mouse event 00473 * \param[in] cookie user data that is passed to the callback 00474 * \return a connection object that allows to disconnect the callback function. 00475 */ 00476 boost::signals2::connection 00477 registerMouseCallback (void (*callback) (const pcl::visualization::MouseEvent&, void*), 00478 void* cookie = NULL) 00479 { 00480 return (registerMouseCallback (boost::bind (callback, _1, cookie))); 00481 } 00482 00483 /** \brief Register a callback function for mouse events 00484 * \param[in] callback the member function that will be registered as a callback for a mouse event 00485 * \param[in] instance instance to the class that implements the callback function 00486 * \param[in] cookie user data that is passed to the callback 00487 * \return a connection object that allows to disconnect the callback function. 00488 */ 00489 template<typename T> boost::signals2::connection 00490 registerMouseCallback (void (T::*callback) (const pcl::visualization::MouseEvent&, void*), 00491 T& instance, void* cookie = NULL) 00492 { 00493 return (registerMouseCallback (boost::bind (callback, boost::ref (instance), _1, cookie))); 00494 } 00495 00496 /** \brief Register a callback function for mouse events 00497 * \param[in] cb the boost function that will be registered as a callback for a mouse event 00498 * \return a connection object that allows to disconnect the callback function. 00499 */ 00500 boost::signals2::connection 00501 registerMouseCallback (boost::function<void (const pcl::visualization::MouseEvent&)> cb); 00502 00503 /** \brief Set the position in screen coordinates. 00504 * \param[in] x where to move the window to (X) 00505 * \param[in] y where to move the window to (Y) 00506 */ 00507 void 00508 setPosition (int x, int y); 00509 00510 /** \brief Set the window size in screen coordinates. 00511 * \param[in] xw window size in horizontal (pixels) 00512 * \param[in] yw window size in vertical (pixels) 00513 */ 00514 void 00515 setSize (int xw, int yw); 00516 00517 /** \brief Return the window size in pixels. */ 00518 int* 00519 getSize (); 00520 00521 /** \brief Returns true when the user tried to close the window */ 00522 bool 00523 wasStopped () const { return (stopped_); } 00524 00525 /** \brief Stop the interaction and close the visualizaton window. */ 00526 void 00527 close () 00528 { 00529 stopped_ = true; 00530 // This tends to close the window... 00531 #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) 00532 interactor_->stopLoop (); 00533 #else 00534 interactor_->TerminateApp (); 00535 #endif 00536 } 00537 00538 /** \brief Add a circle shape from a point and a radius 00539 * \param[in] x the x coordinate of the circle center 00540 * \param[in] y the y coordinate of the circle center 00541 * \param[in] radius the radius of the circle 00542 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00543 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00544 */ 00545 bool 00546 addCircle (unsigned int x, unsigned int y, double radius, 00547 const std::string &layer_id = "circles", double opacity = 1.0); 00548 00549 /** \brief Add a circle shape from a point and a radius 00550 * \param[in] x the x coordinate of the circle center 00551 * \param[in] y the y coordinate of the circle center 00552 * \param[in] radius the radius of the circle 00553 * \param[in] r the red channel of the color that the sphere should be rendered with (0.0 -> 1.0) 00554 * \param[in] g the green channel of the color that the sphere should be rendered with (0.0 -> 1.0) 00555 * \param[in] b the blue channel of the color that the sphere should be rendered with (0.0 -> 1.0) 00556 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00557 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00558 */ 00559 bool 00560 addCircle (unsigned int x, unsigned int y, double radius, 00561 double r, double g, double b, 00562 const std::string &layer_id = "circles", double opacity = 1.0); 00563 00564 /** \brief Add a 2D box and color its edges with a given color 00565 * \param[in] min_pt the X,Y min coordinate 00566 * \param[in] max_pt the X,Y max coordinate 00567 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00568 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00569 */ 00570 bool 00571 addRectangle (const pcl::PointXY &min_pt, const pcl::PointXY &max_pt, 00572 const std::string &layer_id = "rectangles", double opacity = 1.0); 00573 00574 /** \brief Add a 2D box and color its edges with a given color 00575 * \param[in] min_pt the X,Y min coordinate 00576 * \param[in] max_pt the X,Y max coordinate 00577 * \param[in] r the red channel of the color that the box should be rendered with (0.0 -> 1.0) 00578 * \param[in] g the green channel of the color that the box should be rendered with (0.0 -> 1.0) 00579 * \param[in] b the blue channel of the color that the box should be rendered with (0.0 -> 1.0) 00580 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00581 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00582 */ 00583 bool 00584 addRectangle (const pcl::PointXY &min_pt, const pcl::PointXY &max_pt, 00585 double r, double g, double b, 00586 const std::string &layer_id = "rectangles", double opacity = 1.0); 00587 00588 /** \brief Add a 2D box and color its edges with a given color 00589 * \param[in] x_min the X min coordinate 00590 * \param[in] x_max the X max coordinate 00591 * \param[in] y_min the Y min coordinate 00592 * \param[in] y_max the Y max coordinate 00593 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00594 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00595 */ 00596 bool 00597 addRectangle (unsigned int x_min, unsigned int x_max, unsigned int y_min, unsigned int y_max, 00598 const std::string &layer_id = "rectangles", double opacity = 1.0); 00599 00600 /** \brief Add a 2D box and color its edges with a given color 00601 * \param[in] x_min the X min coordinate 00602 * \param[in] x_max the X max coordinate 00603 * \param[in] y_min the Y min coordinate 00604 * \param[in] y_max the Y max coordinate 00605 * \param[in] r the red channel of the color that the box should be rendered with (0.0 -> 1.0) 00606 * \param[in] g the green channel of the color that the box should be rendered with (0.0 -> 1.0) 00607 * \param[in] b the blue channel of the color that the box should be rendered with (0.0 -> 1.0) 00608 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00609 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00610 */ 00611 bool 00612 addRectangle (unsigned int x_min, unsigned int x_max, unsigned int y_min, unsigned int y_max, 00613 double r, double g, double b, 00614 const std::string &layer_id = "rectangles", double opacity = 1.0); 00615 00616 /** \brief Add a 2D box and color its edges with a given color 00617 * \param[in] image the organized point cloud dataset containing the image data 00618 * \param[in] min_pt the X,Y min coordinate 00619 * \param[in] max_pt the X,Y max coordinate 00620 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00621 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00622 */ 00623 template <typename T> bool 00624 addRectangle (const typename pcl::PointCloud<T>::ConstPtr &image, 00625 const T &min_pt, const T &max_pt, 00626 const std::string &layer_id = "rectangles", double opacity = 1.0); 00627 00628 /** \brief Add a 2D box and color its edges with a given color 00629 * \param[in] image the organized point cloud dataset containing the image data 00630 * \param[in] min_pt the X,Y min coordinate 00631 * \param[in] max_pt the X,Y max coordinate 00632 * \param[in] r the red channel of the color that the box should be rendered with (0.0 -> 1.0) 00633 * \param[in] g the green channel of the color that the box should be rendered with (0.0 -> 1.0) 00634 * \param[in] b the blue channel of the color that the box should be rendered with (0.0 -> 1.0) 00635 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00636 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00637 */ 00638 template <typename T> bool 00639 addRectangle (const typename pcl::PointCloud<T>::ConstPtr &image, 00640 const T &min_pt, const T &max_pt, 00641 double r, double g, double b, 00642 const std::string &layer_id = "rectangles", double opacity = 1.0); 00643 00644 /** \brief Add a 2D box that contains a given image mask and color its edges 00645 * \param[in] image the organized point cloud dataset containing the image data 00646 * \param[in] mask the point data representing the mask that we want to draw 00647 * \param[in] r the red channel of the color that the mask should be rendered with 00648 * \param[in] g the green channel of the color that the mask should be rendered with 00649 * \param[in] b the blue channel of the color that the mask should be rendered with 00650 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00651 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00652 */ 00653 template <typename T> bool 00654 addRectangle (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PointCloud<T> &mask, 00655 double r, double g, double b, 00656 const std::string &layer_id = "rectangles", double opacity = 1.0); 00657 00658 /** \brief Add a 2D box that contains a given image mask and color its edges in red 00659 * \param[in] image the organized point cloud dataset containing the image data 00660 * \param[in] mask the point data representing the mask that we want to draw 00661 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00662 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00663 */ 00664 template <typename T> bool 00665 addRectangle (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PointCloud<T> &mask, 00666 const std::string &layer_id = "image_mask", double opacity = 1.0); 00667 00668 /** \brief Add a 2D box and fill it in with a given color 00669 * \param[in] x_min the X min coordinate 00670 * \param[in] x_max the X max coordinate 00671 * \param[in] y_min the Y min coordinate 00672 * \param[in] y_max the Y max coordinate 00673 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00674 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5) 00675 */ 00676 bool 00677 addFilledRectangle (unsigned int x_min, unsigned int x_max, unsigned int y_min, unsigned int y_max, 00678 const std::string &layer_id = "boxes", double opacity = 0.5); 00679 00680 /** \brief Add a 2D box and fill it in with a given color 00681 * \param[in] x_min the X min coordinate 00682 * \param[in] x_max the X max coordinate 00683 * \param[in] y_min the Y min coordinate 00684 * \param[in] y_max the Y max coordinate 00685 * \param[in] r the red channel of the color that the box should be rendered with (0.0 -> 1.0) 00686 * \param[in] g the green channel of the color that the box should be rendered with (0.0 -> 1.0) 00687 * \param[in] b the blue channel of the color that the box should be rendered with (0.0 -> 1.0) 00688 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00689 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5) 00690 */ 00691 bool 00692 addFilledRectangle (unsigned int x_min, unsigned int x_max, unsigned int y_min, unsigned int y_max, 00693 double r, double g, double b, 00694 const std::string &layer_id = "boxes", double opacity = 0.5); 00695 00696 /** \brief Add a 2D line with a given color 00697 * \param[in] x_min the X min coordinate 00698 * \param[in] y_min the Y min coordinate 00699 * \param[in] x_max the X max coordinate 00700 * \param[in] y_max the Y max coordinate 00701 * \param[in] r the red channel of the color that the line should be rendered with (0.0 -> 1.0) 00702 * \param[in] g the green channel of the color that the line should be rendered with (0.0 -> 1.0) 00703 * \param[in] b the blue channel of the color that the line should be rendered with (0.0 -> 1.0) 00704 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00705 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00706 */ 00707 bool 00708 addLine (unsigned int x_min, unsigned int y_min, unsigned int x_max, unsigned int y_max, 00709 double r, double g, double b, 00710 const std::string &layer_id = "line", double opacity = 1.0); 00711 00712 /** \brief Add a 2D line with a given color 00713 * \param[in] x_min the X min coordinate 00714 * \param[in] y_min the Y min coordinate 00715 * \param[in] x_max the X max coordinate 00716 * \param[in] y_max the Y max coordinate 00717 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00718 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00719 */ 00720 bool 00721 addLine (unsigned int x_min, unsigned int y_min, unsigned int x_max, unsigned int y_max, 00722 const std::string &layer_id = "line", double opacity = 1.0); 00723 00724 00725 /** \brief Add a generic 2D mask to an image 00726 * \param[in] image the organized point cloud dataset containing the image data 00727 * \param[in] mask the point data representing the mask that we want to draw 00728 * \param[in] r the red channel of the color that the mask should be rendered with 00729 * \param[in] g the green channel of the color that the mask should be rendered with 00730 * \param[in] b the blue channel of the color that the mask should be rendered with 00731 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00732 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5) 00733 */ 00734 template <typename T> bool 00735 addMask (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PointCloud<T> &mask, 00736 double r, double g, double b, 00737 const std::string &layer_id = "image_mask", double opacity = 0.5); 00738 00739 /** \brief Add a generic 2D mask to an image (colored in red) 00740 * \param[in] image the organized point cloud dataset containing the image data 00741 * \param[in] mask the point data representing the mask that we want to draw 00742 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00743 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5) 00744 */ 00745 template <typename T> bool 00746 addMask (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PointCloud<T> &mask, 00747 const std::string &layer_id = "image_mask", double opacity = 0.5); 00748 00749 /** \brief Add a generic 2D planar polygon to an image 00750 * \param[in] image the organized point cloud dataset containing the image data 00751 * \param[in] polygon the point data representing the polygon that we want to draw. 00752 * A line will be drawn from each point to the next in the dataset. 00753 * \param[in] r the red channel of the color that the polygon should be rendered with 00754 * \param[in] g the green channel of the color that the polygon should be rendered with 00755 * \param[in] b the blue channel of the color that the polygon should be rendered with 00756 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00757 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00758 */ 00759 template <typename T> bool 00760 addPlanarPolygon (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PlanarPolygon<T> &polygon, 00761 double r, double g, double b, 00762 const std::string &layer_id = "planar_polygon", double opacity = 1.0); 00763 00764 /** \brief Add a generic 2D planar polygon to an image 00765 * \param[in] image the organized point cloud dataset containing the image data 00766 * \param[in] polygon the point data representing the polygon that we want to draw. 00767 * A line will be drawn from each point to the next in the dataset. 00768 * \param[in] layer_id the 2D layer ID where we want the extra information to be drawn. 00769 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0) 00770 */ 00771 template <typename T> bool 00772 addPlanarPolygon (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PlanarPolygon<T> &polygon, 00773 const std::string &layer_id = "planar_polygon", double opacity = 1.0); 00774 00775 /** \brief Add a new 2D rendering layer to the viewer. 00776 * \param[in] layer_id the name of the layer 00777 * \param[in] width the width of the layer 00778 * \param[in] height the height of the layer 00779 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5) 00780 */ 00781 bool 00782 addLayer (const std::string &layer_id, int width, int height, double opacity = 0.5); 00783 00784 /** \brief Remove a 2D layer given by its ID. 00785 * \param[in] layer_id the name of the layer 00786 */ 00787 void 00788 removeLayer (const std::string &layer_id); 00789 00790 /** \brief Add the specified correspondences to the display. 00791 * \param[in] source_img The source RGB image 00792 * \param[in] target_img The target RGB image 00793 * \param[in] correspondences The list of correspondences to display. 00794 * \param[in] nth display only the Nth correspondence (e.g., skip the rest) 00795 * \param[in] layer_id the layer id (default: "correspondences") 00796 */ 00797 template <typename PointT> bool 00798 showCorrespondences (const pcl::PointCloud<PointT> &source_img, 00799 const pcl::PointCloud<PointT> &target_img, 00800 const pcl::Correspondences &correspondences, 00801 int nth = 1, 00802 const std::string &layer_id = "correspondences"); 00803 00804 protected: 00805 /** \brief Trigger a render call. */ 00806 void 00807 render (); 00808 00809 /** \brief Convert the Intensity information in a PointCloud<Intensity> to an unsigned char array 00810 * \param[in] cloud the input cloud containing the grayscale intensity information 00811 * \param[out] data a boost shared array of unsigned char type 00812 * \note The method assumes that the data array has already been allocated and 00813 * contains enough space to copy all the data from cloud! 00814 */ 00815 void 00816 convertIntensityCloudToUChar (const pcl::PointCloud<pcl::Intensity> &cloud, 00817 boost::shared_array<unsigned char> data); 00818 00819 /** \brief Convert the Intensity8u information in a PointCloud<Intensity8u> to an unsigned char array 00820 * \param[in] cloud the input cloud containing the grayscale intensity information 00821 * \param[out] data a boost shared array of unsigned char type 00822 * \note The method assumes that the data array has already been allocated and 00823 * contains enough space to copy all the data from cloud! 00824 */ 00825 void 00826 convertIntensityCloud8uToUChar (const pcl::PointCloud<pcl::Intensity8u> &cloud, 00827 boost::shared_array<unsigned char> data); 00828 00829 /** \brief Convert the RGB information in a PointCloud<T> to an unsigned char array 00830 * \param[in] cloud the input cloud containing the RGB information 00831 * \param[out] data a boost shared array of unsigned char type 00832 * \note The method assumes that the data array has already been allocated and 00833 * contains enough space to copy all the data from cloud! 00834 */ 00835 template <typename T> void 00836 convertRGBCloudToUChar (const pcl::PointCloud<T> &cloud, 00837 boost::shared_array<unsigned char> &data); 00838 00839 /** \brief Set the stopped flag back to false */ 00840 void 00841 resetStoppedFlag () { stopped_ = false; } 00842 00843 /** \brief Fire up a mouse event with a specified event ID 00844 * \param[int] event_id the id of the event 00845 */ 00846 void 00847 emitMouseEvent (unsigned long event_id); 00848 00849 /** \brief Fire up a keyboard event with a specified event ID 00850 * \param[int] event_id the id of the event 00851 */ 00852 void 00853 emitKeyboardEvent (unsigned long event_id); 00854 00855 // Callbacks used to register for vtk command 00856 static void 00857 MouseCallback (vtkObject*, unsigned long eid, void* clientdata, void *calldata); 00858 static void 00859 KeyboardCallback (vtkObject*, unsigned long eid, void* clientdata, void *calldata); 00860 00861 protected: // types 00862 struct ExitMainLoopTimerCallback : public vtkCommand 00863 { 00864 ExitMainLoopTimerCallback () : right_timer_id (), window () {} 00865 00866 static ExitMainLoopTimerCallback* New () 00867 { 00868 return (new ExitMainLoopTimerCallback); 00869 } 00870 virtual void 00871 Execute (vtkObject* vtkNotUsed (caller), unsigned long event_id, void* call_data) 00872 { 00873 if (event_id != vtkCommand::TimerEvent) 00874 return; 00875 int timer_id = *static_cast<int*> (call_data); 00876 if (timer_id != right_timer_id) 00877 return; 00878 #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) 00879 window->interactor_->stopLoop (); 00880 #else 00881 window->interactor_->TerminateApp (); 00882 #endif 00883 } 00884 int right_timer_id; 00885 ImageViewer* window; 00886 }; 00887 struct ExitCallback : public vtkCommand 00888 { 00889 ExitCallback () : window () {} 00890 00891 static ExitCallback* New () 00892 { 00893 return (new ExitCallback); 00894 } 00895 virtual void 00896 Execute (vtkObject*, unsigned long event_id, void*) 00897 { 00898 if (event_id != vtkCommand::ExitEvent) 00899 return; 00900 window->stopped_ = true; 00901 #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) 00902 window->interactor_->stopLoop (); 00903 #else 00904 window->interactor_->TerminateApp (); 00905 #endif 00906 } 00907 ImageViewer* window; 00908 }; 00909 00910 private: 00911 /** \brief Internal structure describing a layer. */ 00912 struct Layer 00913 { 00914 Layer () : actor (), layer_name () {} 00915 vtkSmartPointer<vtkContextActor> actor; 00916 std::string layer_name; 00917 }; 00918 00919 typedef std::vector<Layer> LayerMap; 00920 00921 /** \brief Add a new 2D rendering layer to the viewer. 00922 * \param[in] layer_id the name of the layer 00923 * \param[in] width the width of the layer 00924 * \param[in] height the height of the layer 00925 * \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5) 00926 * \param[in] fill_box set to true to fill in the image with one black box before starting 00927 */ 00928 LayerMap::iterator 00929 createLayer (const std::string &layer_id, int width, int height, double opacity = 0.5, bool fill_box = true); 00930 00931 boost::signals2::signal<void (const pcl::visualization::MouseEvent&)> mouse_signal_; 00932 boost::signals2::signal<void (const pcl::visualization::KeyboardEvent&)> keyboard_signal_; 00933 00934 #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) 00935 vtkSmartPointer<PCLVisualizerInteractor> interactor_; 00936 #else 00937 vtkSmartPointer<vtkRenderWindowInteractor> interactor_; 00938 #endif 00939 vtkSmartPointer<vtkCallbackCommand> mouse_command_; 00940 vtkSmartPointer<vtkCallbackCommand> keyboard_command_; 00941 00942 /** \brief Callback object enabling us to leave the main loop, when a timer fires. */ 00943 vtkSmartPointer<ExitMainLoopTimerCallback> exit_main_loop_timer_callback_; 00944 vtkSmartPointer<ExitCallback> exit_callback_; 00945 00946 /** \brief The ImageViewer widget. */ 00947 vtkSmartPointer<vtkImageViewer> image_viewer_; 00948 00949 /** \brief The render window. */ 00950 vtkSmartPointer<vtkRenderWindow> win_; 00951 00952 /** \brief The renderer. */ 00953 vtkSmartPointer<vtkRenderer> ren_; 00954 00955 #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION >= 10)) 00956 /** \brief Global prop. This is the actual "actor". */ 00957 vtkSmartPointer<vtkImageSlice> slice_; 00958 #endif 00959 /** \brief The interactor style. */ 00960 vtkSmartPointer<ImageViewerInteractorStyle> interactor_style_; 00961 00962 /** \brief The data array representing the image. Used internally. */ 00963 boost::shared_array<unsigned char> data_; 00964 00965 /** \brief The data array (representing the image) size. Used internally. */ 00966 size_t data_size_; 00967 00968 /** \brief Set to false if the interaction loop is running. */ 00969 bool stopped_; 00970 00971 /** \brief Global timer ID. Used in destructor only. */ 00972 int timer_id_; 00973 00974 // /** \brief Internal blender used to overlay 2D geometry over the image. */ 00975 // vtkSmartPointer<vtkImageBlend> blend_; 00976 00977 /** \brief Internal list with different 2D layers shapes. */ 00978 LayerMap layer_map_; 00979 00980 /** \brief Image reslice, used for flipping the image. */ 00981 vtkSmartPointer<vtkImageFlip> algo_; 00982 00983 /** \brief Internal data array. Used everytime add***Image is called. 00984 * Cleared, everytime the render loop is executed. 00985 */ 00986 std::vector<unsigned char*> image_data_; 00987 00988 struct LayerComparator 00989 { 00990 LayerComparator (const std::string &str) : str_ (str) {} 00991 const std::string &str_; 00992 00993 bool 00994 operator () (const Layer &layer) 00995 { 00996 return (layer.layer_name == str_); 00997 } 00998 }; 00999 01000 public: 01001 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 01002 }; 01003 } 01004 } 01005 01006 #include <pcl/visualization/impl/image_viewer.hpp> 01007 01008 #endif /* __IMAGE_VISUALIZER_H__ */ 01009