1053 lines
54 KiB
C++

/*
* Software License Agreement (BSD License)
*
* Point Cloud Library (PCL) - www.pointclouds.org
* Copyright (c) 2010-2012, Willow Garage, Inc.
* Copyright (c) 2012-, Open Perception, Inc.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of the copyright holder(s) nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#include <pcl/memory.h>
#include <pcl/pcl_macros.h>
#include <pcl/point_types.h>
#include <pcl/console/print.h>
#include <pcl/visualization/interactor_style.h>
#include <pcl/visualization/vtk/pcl_image_canvas_source_2d.h>
#include <pcl/visualization/vtk/pcl_context_item.h>
#include <pcl/geometry/planar_polygon.h>
#include <pcl/correspondence.h>
#include <boost/shared_array.hpp>
#include <vtkVersion.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderWindowInteractor.h>
class vtkImageSlice;
class vtkContextActor;
class vtkImageViewer;
class vtkImageFlip;
namespace pcl
{
namespace visualization
{
using Vector3ub = Eigen::Array<unsigned char, 3, 1>;
static const Vector3ub green_color (0, 255, 0);
static const Vector3ub red_color (255, 0, 0);
static const Vector3ub blue_color (0, 0, 255);
/** \brief An image viewer interactor style, tailored for ImageViewer.
* \author Radu B. Rusu
* \ingroup visualization
*/
class PCL_EXPORTS ImageViewerInteractorStyle : public vtkInteractorStyleImage
{
public:
static ImageViewerInteractorStyle *New ();
ImageViewerInteractorStyle ();
void OnMouseWheelForward () override {}
void OnMouseWheelBackward () override {}
void OnMiddleButtonDown () override {}
void OnRightButtonDown () override {}
void OnLeftButtonDown () override;
void
OnChar () override;
void
adjustCamera (vtkImageData *image, vtkRenderer *ren);
void
adjustCamera (vtkRenderer *ren);
};
/** \brief ImageViewer is a class for 2D image visualization.
*
* Features include:
* - add and remove different layers with different opacity (transparency) values
* - add 2D geometric shapes (circles, boxes, etc) in separate layers
* - display RGB, monochrome, float, angle images
*
* Simple usage example:
* \code
* pcl::visualization::ImageViewer iv;
* 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"
* 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"
* iv.addRGBImage<pcl::PointXYZRGBA> (cloud); // add a RGB image from a point cloud dataset in an "rgb_image" default layer
* iv.spin (); // press 'q' to exit
* iv.removeLayer ("circles"); // remove layer "circles"
* iv.spin (); // press 'q' to exit
* \endcode
*
* \author Radu B. Rusu, Suat Gedikli
* \ingroup visualization
*/
class PCL_EXPORTS ImageViewer
{
public:
using Ptr = shared_ptr<ImageViewer>;
using ConstPtr = shared_ptr<const ImageViewer>;
/** \brief Constructor.
* \param[in] window_title the title of the window
*/
ImageViewer (const std::string& window_title = "");
/** \brief Destructor. */
virtual ~ImageViewer ();
/** \brief Set up the interactor style. By default the interactor style is set to
* vtkInteractorStyleImage you can use this to set it to another type.
* \param[in] style user set interactor style.
*/
void
setInteractorStyle (vtkInteractorObserver *style)
{
interactor_->SetInteractorStyle (style);
}
/** \brief Show a monochrome 2D image on screen.
* \param[in] data the input data representing the image
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
showMonoImage (const unsigned char* data, unsigned width, unsigned height,
const std::string &layer_id = "mono_image", double opacity = 1.0);
/** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] data the input data representing the image
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
addMonoImage (const unsigned char* data, unsigned width, unsigned height,
const std::string &layer_id = "mono_image", double opacity = 1.0);
/** \brief Show a monochrome 2D image on screen.
* \param[in] cloud the input data representing the grayscale point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
inline void
showMonoImage (const pcl::PointCloud<pcl::Intensity>::ConstPtr &cloud,
const std::string &layer_id = "mono_image", double opacity = 1.0)
{
return (showMonoImage (*cloud, layer_id, opacity));
}
/** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] cloud the input data representing the grayscale point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
inline void
addMonoImage (const pcl::PointCloud<pcl::Intensity>::ConstPtr &cloud,
const std::string &layer_id = "mono_image", double opacity = 1.0)
{
return (addMonoImage (*cloud, layer_id, opacity));
}
/** \brief Show a monochrome 2D image on screen.
* \param[in] cloud the input data representing the grayscale point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
showMonoImage (const pcl::PointCloud<pcl::Intensity> &cloud,
const std::string &layer_id = "mono_image", double opacity = 1.0);
/** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] cloud the input data representing the RGB point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
addMonoImage (const pcl::PointCloud<pcl::Intensity> &cloud,
const std::string &layer_id = "mono_image", double opacity = 1.0);
/** \brief Show a monochrome 2D image on screen.
* \param[in] cloud the input data representing the grayscale point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
inline void
showMonoImage (const pcl::PointCloud<pcl::Intensity8u>::ConstPtr &cloud,
const std::string &layer_id = "mono_image", double opacity = 1.0)
{
return (showMonoImage (*cloud, layer_id, opacity));
}
/** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] cloud the input data representing the grayscale point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
inline void
addMonoImage (const pcl::PointCloud<pcl::Intensity8u>::ConstPtr &cloud,
const std::string &layer_id = "mono_image", double opacity = 1.0)
{
return (addMonoImage (*cloud, layer_id, opacity));
}
/** \brief Show a monochrome 2D image on screen.
* \param[in] cloud the input data representing the grayscale point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
showMonoImage (const pcl::PointCloud<pcl::Intensity8u> &cloud,
const std::string &layer_id = "mono_image", double opacity = 1.0);
/** \brief Add a monochrome 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] cloud the input data representing the RGB point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
addMonoImage (const pcl::PointCloud<pcl::Intensity8u> &cloud,
const std::string &layer_id = "mono_image", double opacity = 1.0);
/** \brief Show a 2D RGB image on screen.
* \param[in] data the input data representing the image
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
showRGBImage (const unsigned char* data, unsigned width, unsigned height,
const std::string &layer_id = "rgb_image", double opacity = 1.0);
/** \brief Add an RGB 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] data the input data representing the image
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
* \param[in] autoresize flag to enable window to adapt to image size (default true)
*/
void
addRGBImage (const unsigned char* data, unsigned width, unsigned height,
const std::string &layer_id = "rgb_image", double opacity = 1.0,
bool autoresize = true);
/** \brief Show a 2D image on screen, obtained from the RGB channel of a point cloud.
* \param[in] cloud the input data representing the RGB point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
template <typename T> inline void
showRGBImage (const typename pcl::PointCloud<T>::ConstPtr &cloud,
const std::string &layer_id = "rgb_image", double opacity = 1.0)
{
return (showRGBImage<T> (*cloud, layer_id, opacity));
}
/** \brief Add an RGB 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] cloud the input data representing the RGB point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
template <typename T> inline void
addRGBImage (const typename pcl::PointCloud<T>::ConstPtr &cloud,
const std::string &layer_id = "rgb_image", double opacity = 1.0)
{
return (addRGBImage<T> (*cloud, layer_id, opacity));
}
/** \brief Show a 2D image on screen, obtained from the RGB channel of a point cloud.
* \param[in] cloud the input data representing the RGB point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
template <typename T> void
showRGBImage (const pcl::PointCloud<T> &cloud,
const std::string &layer_id = "rgb_image", double opacity = 1.0);
/** \brief Add an RGB 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] cloud the input data representing the RGB point cloud
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
template <typename T> void
addRGBImage (const pcl::PointCloud<T> &cloud,
const std::string &layer_id = "rgb_image", double opacity = 1.0);
/** \brief Show a 2D image (float) on screen.
* \param[in] data the input data representing the image in float format
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] min_value filter all values in the image to be larger than this minimum value
* \param[in] max_value filter all values in the image to be smaller than this maximum value
* \param[in] grayscale show data as grayscale (true) or not (false). Default: false
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
showFloatImage (const float* data, unsigned int width, unsigned int height,
float min_value = std::numeric_limits<float>::min (),
float max_value = std::numeric_limits<float>::max (), bool grayscale = false,
const std::string &layer_id = "float_image", double opacity = 1.0);
/** \brief Add a float 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] data the input data representing the image in float format
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] min_value filter all values in the image to be larger than this minimum value
* \param[in] max_value filter all values in the image to be smaller than this maximum value
* \param[in] grayscale show data as grayscale (true) or not (false). Default: false
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
addFloatImage (const float* data, unsigned int width, unsigned int height,
float min_value = std::numeric_limits<float>::min (),
float max_value = std::numeric_limits<float>::max (), bool grayscale = false,
const std::string &layer_id = "float_image", double opacity = 1.0);
/** \brief Show a 2D image (unsigned short) on screen.
* \param[in] short_image the input data representing the image in unsigned short format
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] min_value filter all values in the image to be larger than this minimum value
* \param[in] max_value filter all values in the image to be smaller than this maximum value
* \param[in] grayscale show data as grayscale (true) or not (false). Default: false
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
showShortImage (const unsigned short* short_image, unsigned int width, unsigned int height,
unsigned short min_value = std::numeric_limits<unsigned short>::min (),
unsigned short max_value = std::numeric_limits<unsigned short>::max (), bool grayscale = false,
const std::string &layer_id = "short_image", double opacity = 1.0);
/** \brief Add a short 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] short_image the input data representing the image in unsigned short format
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] min_value filter all values in the image to be larger than this minimum value
* \param[in] max_value filter all values in the image to be smaller than this maximum value
* \param[in] grayscale show data as grayscale (true) or not (false). Default: false
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
addShortImage (const unsigned short* short_image, unsigned int width, unsigned int height,
unsigned short min_value = std::numeric_limits<unsigned short>::min (),
unsigned short max_value = std::numeric_limits<unsigned short>::max (), bool grayscale = false,
const std::string &layer_id = "short_image", double opacity = 1.0);
/** \brief Show a 2D image on screen representing angle data.
* \param[in] data the input data representing the image
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
showAngleImage (const float* data, unsigned width, unsigned height,
const std::string &layer_id = "angle_image", double opacity = 1.0);
/** \brief Add an angle 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] data the input data representing the image
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
addAngleImage (const float* data, unsigned width, unsigned height,
const std::string &layer_id = "angle_image", double opacity = 1.0);
/** \brief Show a 2D image on screen representing half angle data.
* \param[in] data the input data representing the image
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
showHalfAngleImage (const float* data, unsigned width, unsigned height,
const std::string &layer_id = "half_angle_image", double opacity = 1.0);
/** \brief Add a half angle 2D image layer, but do not render it (use spin/spinOnce to update).
* \param[in] data the input data representing the image
* \param[in] width the width of the image
* \param[in] height the height of the image
* \param[in] layer_id the name of the layer (default: "image")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
addHalfAngleImage (const float* data, unsigned width, unsigned height,
const std::string &layer_id = "half_angle_image", double opacity = 1.0);
/** \brief Sets the pixel at coordinates(u,v) to color while setting the neighborhood to another
* \param[in] u the u/x coordinate of the pixel
* \param[in] v the v/y coordinate of the pixel
* \param[in] fg_color the pixel color
* \param[in] bg_color the neighborhood color
* \param[in] radius the circle radius around the pixel
* \param[in] layer_id the name of the layer (default: "points")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
markPoint (std::size_t u, std::size_t v, Vector3ub fg_color, Vector3ub bg_color = red_color, double radius = 3.0,
const std::string &layer_id = "points", double opacity = 1.0);
/** \brief Sets the pixel at coordinates(u,v) to color while setting the neighborhood to another
* \param[in] uv the u/x, v/y coordinate of the pixels to be marked
* \param[in] fg_color the pixel color
* \param[in] bg_color the neighborhood color
* \param[in] size edge of the square surrounding each pixel
* \param[in] layer_id the name of the layer (default: "markers")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
markPoints (const std::vector<int>& uv, Vector3ub fg_color, Vector3ub bg_color = red_color, double size = 3.0,
const std::string &layer_id = "markers", double opacity = 1.0);
/** \brief Sets the pixel at coordinates(u,v) to color while setting the neighborhood to another (float coordinates version).
* \param[in] uv the u/x, v/y coordinate of the pixels to be marked
* \param[in] fg_color the pixel color
* \param[in] bg_color the neighborhood color
* \param[in] size edge of the square surrounding each pixel
* \param[in] layer_id the name of the layer (default: "markers")
* \param[in] opacity the opacity of the layer (default: 1.0)
*/
void
markPoints (const std::vector<float>& uv, Vector3ub fg_color, Vector3ub bg_color = red_color, double size = 3.0,
const std::string &layer_id = "markers", double opacity = 1.0);
/** \brief Set the window title name
* \param[in] name the window title
*/
void
setWindowTitle (const std::string& name);
/** \brief Spin method. Calls the interactor and runs an internal loop. */
void
spin ();
/** \brief Spin once method. Calls the interactor and updates the screen once.
* \param[in] time - How long (in ms) should the visualization loop be allowed to run.
* \param[in] force_redraw - if false it might return without doing anything if the
* interactor's framerate does not require a redraw yet.
*/
void
spinOnce (int time = 1, bool force_redraw = true);
/** \brief Register a callback function for keyboard events
* \param[in] callback the function that will be registered as a callback for a keyboard event
* \param[in] cookie user data that is passed to the callback
* \return a connection object that allows to disconnect the callback function.
*/
boost::signals2::connection
registerKeyboardCallback (void (*callback) (const pcl::visualization::KeyboardEvent&, void*),
void* cookie = nullptr)
{
return (registerKeyboardCallback ([=] (const pcl::visualization::KeyboardEvent& e) { (*callback) (e, cookie); }));
}
/** \brief Register a callback function for keyboard events
* \param[in] callback the member function that will be registered as a callback for a keyboard event
* \param[in] instance instance to the class that implements the callback function
* \param[in] cookie user data that is passed to the callback
* \return a connection object that allows to disconnect the callback function.
*/
template<typename T> boost::signals2::connection
registerKeyboardCallback (void (T::*callback) (const pcl::visualization::KeyboardEvent&, void*),
T& instance, void* cookie = nullptr)
{
return (registerKeyboardCallback ([=, &instance] (const pcl::visualization::KeyboardEvent& e) { (instance.*callback) (e, cookie); }));
}
/** \brief Register a callback std::function for keyboard events
* \param[in] cb the boost function that will be registered as a callback for a keyboard event
* \return a connection object that allows to disconnect the callback function.
*/
boost::signals2::connection
registerKeyboardCallback (std::function<void (const pcl::visualization::KeyboardEvent&)> cb);
/** \brief Register a callback std::function for mouse events
* \param[in] callback the function that will be registered as a callback for a mouse event
* \param[in] cookie user data that is passed to the callback
* \return a connection object that allows to disconnect the callback function.
*/
boost::signals2::connection
registerMouseCallback (void (*callback) (const pcl::visualization::MouseEvent&, void*),
void* cookie = nullptr)
{
return (registerMouseCallback ([=] (const pcl::visualization::MouseEvent& e) { (*callback) (e, cookie); }));
}
/** \brief Register a callback function for mouse events
* \param[in] callback the member function that will be registered as a callback for a mouse event
* \param[in] instance instance to the class that implements the callback function
* \param[in] cookie user data that is passed to the callback
* \return a connection object that allows to disconnect the callback function.
*/
template<typename T> boost::signals2::connection
registerMouseCallback (void (T::*callback) (const pcl::visualization::MouseEvent&, void*),
T& instance, void* cookie = nullptr)
{
return (registerMouseCallback ([=, &instance] (const pcl::visualization::MouseEvent& e) { (instance.*callback) (e, cookie); }));
}
/** \brief Register a callback function for mouse events
* \param[in] cb the boost function that will be registered as a callback for a mouse event
* \return a connection object that allows to disconnect the callback function.
*/
boost::signals2::connection
registerMouseCallback (std::function<void (const pcl::visualization::MouseEvent&)> cb);
/** \brief Set the position in screen coordinates.
* \param[in] x where to move the window to (X)
* \param[in] y where to move the window to (Y)
*/
void
setPosition (int x, int y);
/** \brief Set the window size in screen coordinates.
* \param[in] xw window size in horizontal (pixels)
* \param[in] yw window size in vertical (pixels)
*/
void
setSize (int xw, int yw);
/** \brief Return the window size in pixels. */
int*
getSize ();
/** \brief Returns true when the user tried to close the window */
bool
wasStopped () const { return (stopped_); }
/** \brief Stop the interaction and close the visualizaton window. */
void
close ()
{
stopped_ = true;
// This tends to close the window...
interactor_->TerminateApp ();
}
/** \brief Add a circle shape from a point and a radius
* \param[in] x the x coordinate of the circle center
* \param[in] y the y coordinate of the circle center
* \param[in] radius the radius of the circle
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addCircle (unsigned int x, unsigned int y, double radius,
const std::string &layer_id = "circles", double opacity = 1.0);
/** \brief Add a circle shape from a point and a radius
* \param[in] x the x coordinate of the circle center
* \param[in] y the y coordinate of the circle center
* \param[in] radius the radius of the circle
* \param[in] r the red channel of the color that the sphere should be rendered with (0.0 -> 1.0)
* \param[in] g the green channel of the color that the sphere should be rendered with (0.0 -> 1.0)
* \param[in] b the blue channel of the color that the sphere should be rendered with (0.0 -> 1.0)
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addCircle (unsigned int x, unsigned int y, double radius,
double r, double g, double b,
const std::string &layer_id = "circles", double opacity = 1.0);
/** \brief Add a 2D box and color its edges with a given color
* \param[in] min_pt the X,Y min coordinate
* \param[in] max_pt the X,Y max coordinate
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addRectangle (const pcl::PointXY &min_pt, const pcl::PointXY &max_pt,
const std::string &layer_id = "rectangles", double opacity = 1.0);
/** \brief Add a 2D box and color its edges with a given color
* \param[in] min_pt the X,Y min coordinate
* \param[in] max_pt the X,Y max coordinate
* \param[in] r the red channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] g the green channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] b the blue channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addRectangle (const pcl::PointXY &min_pt, const pcl::PointXY &max_pt,
double r, double g, double b,
const std::string &layer_id = "rectangles", double opacity = 1.0);
/** \brief Add a 2D box and color its edges with a given color
* \param[in] x_min the X min coordinate
* \param[in] x_max the X max coordinate
* \param[in] y_min the Y min coordinate
* \param[in] y_max the Y max coordinate
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addRectangle (unsigned int x_min, unsigned int x_max, unsigned int y_min, unsigned int y_max,
const std::string &layer_id = "rectangles", double opacity = 1.0);
/** \brief Add a 2D box and color its edges with a given color
* \param[in] x_min the X min coordinate
* \param[in] x_max the X max coordinate
* \param[in] y_min the Y min coordinate
* \param[in] y_max the Y max coordinate
* \param[in] r the red channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] g the green channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] b the blue channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addRectangle (unsigned int x_min, unsigned int x_max, unsigned int y_min, unsigned int y_max,
double r, double g, double b,
const std::string &layer_id = "rectangles", double opacity = 1.0);
/** \brief Add a 2D box and color its edges with a given color
* \param[in] image the organized point cloud dataset containing the image data
* \param[in] min_pt the X,Y min coordinate
* \param[in] max_pt the X,Y max coordinate
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
template <typename T> bool
addRectangle (const typename pcl::PointCloud<T>::ConstPtr &image,
const T &min_pt, const T &max_pt,
const std::string &layer_id = "rectangles", double opacity = 1.0);
/** \brief Add a 2D box and color its edges with a given color
* \param[in] image the organized point cloud dataset containing the image data
* \param[in] min_pt the X,Y min coordinate
* \param[in] max_pt the X,Y max coordinate
* \param[in] r the red channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] g the green channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] b the blue channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
template <typename T> bool
addRectangle (const typename pcl::PointCloud<T>::ConstPtr &image,
const T &min_pt, const T &max_pt,
double r, double g, double b,
const std::string &layer_id = "rectangles", double opacity = 1.0);
/** \brief Add a 2D box that contains a given image mask and color its edges
* \param[in] image the organized point cloud dataset containing the image data
* \param[in] mask the point data representing the mask that we want to draw
* \param[in] r the red channel of the color that the mask should be rendered with
* \param[in] g the green channel of the color that the mask should be rendered with
* \param[in] b the blue channel of the color that the mask should be rendered with
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
template <typename T> bool
addRectangle (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PointCloud<T> &mask,
double r, double g, double b,
const std::string &layer_id = "rectangles", double opacity = 1.0);
/** \brief Add a 2D box that contains a given image mask and color its edges in red
* \param[in] image the organized point cloud dataset containing the image data
* \param[in] mask the point data representing the mask that we want to draw
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
template <typename T> bool
addRectangle (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PointCloud<T> &mask,
const std::string &layer_id = "image_mask", double opacity = 1.0);
/** \brief Add a 2D box and fill it in with a given color
* \param[in] x_min the X min coordinate
* \param[in] x_max the X max coordinate
* \param[in] y_min the Y min coordinate
* \param[in] y_max the Y max coordinate
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5)
*/
bool
addFilledRectangle (unsigned int x_min, unsigned int x_max, unsigned int y_min, unsigned int y_max,
const std::string &layer_id = "boxes", double opacity = 0.5);
/** \brief Add a 2D box and fill it in with a given color
* \param[in] x_min the X min coordinate
* \param[in] x_max the X max coordinate
* \param[in] y_min the Y min coordinate
* \param[in] y_max the Y max coordinate
* \param[in] r the red channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] g the green channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] b the blue channel of the color that the box should be rendered with (0.0 -> 1.0)
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5)
*/
bool
addFilledRectangle (unsigned int x_min, unsigned int x_max, unsigned int y_min, unsigned int y_max,
double r, double g, double b,
const std::string &layer_id = "boxes", double opacity = 0.5);
/** \brief Add a 2D line with a given color
* \param[in] x_min the X min coordinate
* \param[in] y_min the Y min coordinate
* \param[in] x_max the X max coordinate
* \param[in] y_max the Y max coordinate
* \param[in] r the red channel of the color that the line should be rendered with (0.0 -> 1.0)
* \param[in] g the green channel of the color that the line should be rendered with (0.0 -> 1.0)
* \param[in] b the blue channel of the color that the line should be rendered with (0.0 -> 1.0)
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addLine (unsigned int x_min, unsigned int y_min, unsigned int x_max, unsigned int y_max,
double r, double g, double b,
const std::string &layer_id = "line", double opacity = 1.0);
/** \brief Add a 2D line with a given color
* \param[in] x_min the X min coordinate
* \param[in] y_min the Y min coordinate
* \param[in] x_max the X max coordinate
* \param[in] y_max the Y max coordinate
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addLine (unsigned int x_min, unsigned int y_min, unsigned int x_max, unsigned int y_max,
const std::string &layer_id = "line", double opacity = 1.0);
/** \brief Add a 2D text with a given color
* \param[in] x the X coordinate
* \param[in] y the Y coordinate
* \param[in] text the text string to be displayed
* \param[in] r the red channel of the color that the line should be rendered with (0.0 -> 1.0)
* \param[in] g the green channel of the color that the line should be rendered with (0.0 -> 1.0)
* \param[in] b the blue channel of the color that the line should be rendered with (0.0 -> 1.0)
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addText (unsigned int x, unsigned int y, const std::string& text,
double r, double g, double b,
const std::string &layer_id = "line", double opacity = 1.0);
/** \brief Add a 2D text with a given color
* \param[in] x the X coordinate
* \param[in] y the Y coordinate
* \param[in] text the text string to be displayed
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
bool
addText (unsigned int x, unsigned int y, const std::string& text,
const std::string &layer_id = "line", double opacity = 1.0);
/** \brief Add a generic 2D mask to an image
* \param[in] image the organized point cloud dataset containing the image data
* \param[in] mask the point data representing the mask that we want to draw
* \param[in] r the red channel of the color that the mask should be rendered with
* \param[in] g the green channel of the color that the mask should be rendered with
* \param[in] b the blue channel of the color that the mask should be rendered with
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5)
*/
template <typename T> bool
addMask (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PointCloud<T> &mask,
double r, double g, double b,
const std::string &layer_id = "image_mask", double opacity = 0.5);
/** \brief Add a generic 2D mask to an image (colored in red)
* \param[in] image the organized point cloud dataset containing the image data
* \param[in] mask the point data representing the mask that we want to draw
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5)
*/
template <typename T> bool
addMask (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PointCloud<T> &mask,
const std::string &layer_id = "image_mask", double opacity = 0.5);
/** \brief Add a generic 2D planar polygon to an image
* \param[in] image the organized point cloud dataset containing the image data
* \param[in] polygon the point data representing the polygon that we want to draw.
* A line will be drawn from each point to the next in the dataset.
* \param[in] r the red channel of the color that the polygon should be rendered with
* \param[in] g the green channel of the color that the polygon should be rendered with
* \param[in] b the blue channel of the color that the polygon should be rendered with
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
template <typename T> bool
addPlanarPolygon (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PlanarPolygon<T> &polygon,
double r, double g, double b,
const std::string &layer_id = "planar_polygon", double opacity = 1.0);
/** \brief Add a generic 2D planar polygon to an image
* \param[in] image the organized point cloud dataset containing the image data
* \param[in] polygon the point data representing the polygon that we want to draw.
* A line will be drawn from each point to the next in the dataset.
* \param[in] layer_id the 2D layer ID where we want the extra information to be drawn.
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 1.0)
*/
template <typename T> bool
addPlanarPolygon (const typename pcl::PointCloud<T>::ConstPtr &image, const pcl::PlanarPolygon<T> &polygon,
const std::string &layer_id = "planar_polygon", double opacity = 1.0);
/** \brief Add a new 2D rendering layer to the viewer.
* \param[in] layer_id the name of the layer
* \param[in] width the width of the layer
* \param[in] height the height of the layer
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5)
*/
bool
addLayer (const std::string &layer_id, int width, int height, double opacity = 0.5);
/** \brief Remove a 2D layer given by its ID.
* \param[in] layer_id the name of the layer
*/
void
removeLayer (const std::string &layer_id);
/** \brief Add the specified correspondences to the display.
* \param[in] source_img The source RGB image
* \param[in] target_img The target RGB image
* \param[in] correspondences The list of correspondences to display.
* \param[in] nth display only the Nth correspondence (e.g., skip the rest)
* \param[in] layer_id the layer id (default: "correspondences")
*/
template <typename PointT> bool
showCorrespondences (const pcl::PointCloud<PointT> &source_img,
const pcl::PointCloud<PointT> &target_img,
const pcl::Correspondences &correspondences,
int nth = 1,
const std::string &layer_id = "correspondences");
protected:
/** \brief Trigger a render call. */
void
render ();
/** \brief Convert the Intensity information in a PointCloud<Intensity> to an unsigned char array
* \param[in] cloud the input cloud containing the grayscale intensity information
* \param[out] data a boost shared array of unsigned char type
* \note The method assumes that the data array has already been allocated and
* contains enough space to copy all the data from cloud!
*/
void
convertIntensityCloudToUChar (const pcl::PointCloud<pcl::Intensity> &cloud,
boost::shared_array<unsigned char> data);
/** \brief Convert the Intensity8u information in a PointCloud<Intensity8u> to an unsigned char array
* \param[in] cloud the input cloud containing the grayscale intensity information
* \param[out] data a boost shared array of unsigned char type
* \note The method assumes that the data array has already been allocated and
* contains enough space to copy all the data from cloud!
*/
void
convertIntensityCloud8uToUChar (const pcl::PointCloud<pcl::Intensity8u> &cloud,
boost::shared_array<unsigned char> data);
/** \brief Convert the RGB information in a PointCloud<T> to an unsigned char array
* \param[in] cloud the input cloud containing the RGB information
* \param[out] data a boost shared array of unsigned char type
* \note The method assumes that the data array has already been allocated and
* contains enough space to copy all the data from cloud!
*/
template <typename T> void
convertRGBCloudToUChar (const pcl::PointCloud<T> &cloud,
boost::shared_array<unsigned char> &data);
/** \brief Set the stopped flag back to false */
void
resetStoppedFlag () { stopped_ = false; }
/** \brief Fire up a mouse event with a specified event ID
* \param[in] event_id the id of the event
*/
void
emitMouseEvent (unsigned long event_id);
/** \brief Fire up a keyboard event with a specified event ID
* \param[in] event_id the id of the event
*/
void
emitKeyboardEvent (unsigned long event_id);
// Callbacks used to register for vtk command
static void
MouseCallback (vtkObject*, unsigned long eid, void* clientdata, void *calldata);
static void
KeyboardCallback (vtkObject*, unsigned long eid, void* clientdata, void *calldata);
protected: // types
struct ExitMainLoopTimerCallback : public vtkCommand
{
ExitMainLoopTimerCallback () : right_timer_id (), window () {}
static ExitMainLoopTimerCallback* New ()
{
return (new ExitMainLoopTimerCallback);
}
void
Execute (vtkObject* vtkNotUsed (caller), unsigned long event_id, void* call_data) override
{
if (event_id != vtkCommand::TimerEvent)
return;
int timer_id = *static_cast<int*> (call_data);
if (timer_id != right_timer_id)
return;
window->interactor_->TerminateApp ();
}
int right_timer_id;
ImageViewer* window;
};
struct ExitCallback : public vtkCommand
{
ExitCallback () : window () {}
static ExitCallback* New ()
{
return (new ExitCallback);
}
void
Execute (vtkObject*, unsigned long event_id, void*) override
{
if (event_id != vtkCommand::ExitEvent)
return;
window->stopped_ = true;
window->interactor_->TerminateApp ();
}
ImageViewer* window;
};
private:
/** \brief Internal structure describing a layer. */
struct Layer
{
Layer () {}
vtkSmartPointer<vtkContextActor> actor;
std::string layer_name;
};
using LayerMap = std::vector<Layer>;
/** \brief Add a new 2D rendering layer to the viewer.
* \param[in] layer_id the name of the layer
* \param[in] width the width of the layer
* \param[in] height the height of the layer
* \param[in] opacity the opacity of the layer: 0 for invisible, 1 for opaque. (default: 0.5)
* \param[in] fill_box set to true to fill in the image with one black box before starting
*/
LayerMap::iterator
createLayer (const std::string &layer_id, int width, int height, double opacity = 0.5, bool fill_box = true);
boost::signals2::signal<void (const pcl::visualization::MouseEvent&)> mouse_signal_;
boost::signals2::signal<void (const pcl::visualization::KeyboardEvent&)> keyboard_signal_;
vtkSmartPointer<vtkRenderWindowInteractor> interactor_;
vtkSmartPointer<vtkCallbackCommand> mouse_command_;
vtkSmartPointer<vtkCallbackCommand> keyboard_command_;
/** \brief Callback object enabling us to leave the main loop, when a timer fires. */
vtkSmartPointer<ExitMainLoopTimerCallback> exit_main_loop_timer_callback_;
vtkSmartPointer<ExitCallback> exit_callback_;
/** \brief The ImageViewer widget. */
vtkSmartPointer<vtkImageViewer> image_viewer_;
/** \brief The render window. */
vtkSmartPointer<vtkRenderWindow> win_;
/** \brief The renderer. */
vtkSmartPointer<vtkRenderer> ren_;
/** \brief Global prop. This is the actual "actor". */
vtkSmartPointer<vtkImageSlice> slice_;
/** \brief The interactor style. */
vtkSmartPointer<ImageViewerInteractorStyle> interactor_style_;
/** \brief The data array representing the image. Used internally. */
boost::shared_array<unsigned char> data_;
/** \brief The data array (representing the image) size. Used internally. */
std::size_t data_size_;
/** \brief Set to false if the interaction loop is running. */
bool stopped_;
/** \brief Global timer ID. Used in destructor only. */
int timer_id_;
// /** \brief Internal blender used to overlay 2D geometry over the image. */
// vtkSmartPointer<vtkImageBlend> blend_;
/** \brief Internal list with different 2D layers shapes. */
LayerMap layer_map_;
/** \brief Image reslice, used for flipping the image. */
vtkSmartPointer<vtkImageFlip> algo_;
/** \brief Internal data array. Used everytime add***Image is called.
* Cleared, everytime the render loop is executed.
*/
std::vector<unsigned char*> image_data_;
struct LayerComparator
{
LayerComparator (const std::string &str) : str_ (str) {}
const std::string &str_;
bool
operator () (const Layer &layer)
{
return (layer.layer_name == str_);
}
};
public:
PCL_MAKE_ALIGNED_OPERATOR_NEW
};
}
}
#include <pcl/visualization/impl/image_viewer.hpp>