39 #ifndef PCL_VISUALIZATION_IMAGE_VISUALIZER_HPP_
40 #define PCL_VISUALIZATION_IMAGE_VISUALIZER_HPP_
42 #include <vtkVersion.h>
43 #include <vtkContextActor.h>
44 #include <vtkContextScene.h>
45 #include <vtkImageData.h>
46 #include <vtkImageFlip.h>
47 #include <vtkPointData.h>
48 #include <vtkImageViewer.h>
50 #include <pcl/search/organized.h>
51 #include <pcl/visualization/common/common.h>
55 template <
typename T>
void
57 const pcl::PointCloud<T>& cloud,
58 boost::shared_array<unsigned char>&
data)
61 for (
const auto& point : cloud)
70 template <
typename T>
void
72 const std::string& layer_id,
75 if (data_size_ < cloud.width * cloud.height)
77 data_size_ = cloud.width * cloud.height * 3;
78 data_.reset(
new unsigned char[data_size_]);
81 convertRGBCloudToUChar(cloud, data_);
83 return (addRGBImage(data_.get(), cloud.width, cloud.height, layer_id, opacity));
87 template <
typename T>
void
89 const std::string& layer_id,
92 addRGBImage<T>(cloud, layer_id, opacity);
97 template <
typename T>
bool
99 const typename pcl::PointCloud<T>::ConstPtr&
image,
100 const pcl::PointCloud<T>& mask,
101 double r,
double g,
double b,
102 const std::string& layer_id,
double opacity)
105 if (!
image->isOrganized())
109 LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(),
LayerComparator(layer_id));
110 if (am_it == layer_map_.end())
112 PCL_DEBUG(
"[pcl::visualization::ImageViewer::addMask] No layer with ID'=%s' found. Creating new one...\n", layer_id.c_str());
113 am_it = createLayer(layer_id, getSize()[0] - 1, getSize()[1] - 1, opacity,
false);
117 pcl::search::OrganizedNeighbor<T> search;
118 search.setInputCloud(
image);
119 std::vector<float> xy;
120 xy.reserve(mask.size() * 2);
121 for (std::size_t i = 0; i < mask.size(); ++i)
123 pcl::PointXY p_projected;
124 search.projectPoint(mask[i], p_projected);
126 xy.push_back(p_projected.x);
127 xy.push_back(
static_cast<float> (
image->height) - p_projected.y);
131 points->setColors(
static_cast<unsigned char> (r * 255.0),
132 static_cast<unsigned char> (g * 255.0),
133 static_cast<unsigned char> (b * 255.0));
134 points->setOpacity(opacity);
136 am_it->actor->GetScene()->AddItem(
points);
141 template <
typename T>
bool
143 const typename pcl::PointCloud<T>::ConstPtr&
image,
144 const pcl::PointCloud<T>& mask,
145 const std::string& layer_id,
double opacity)
147 return (addMask(
image, mask, 1.0, 0.0, 0.0, layer_id, opacity));
151 template <
typename T>
bool
153 const typename pcl::PointCloud<T>::ConstPtr&
image,
154 const pcl::PlanarPolygon<T>&
polygon,
155 double r,
double g,
double b,
156 const std::string& layer_id,
double opacity)
159 if (!
image->isOrganized())
163 LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(),
LayerComparator(layer_id));
164 if (am_it == layer_map_.end())
166 PCL_DEBUG(
"[pcl::visualization::ImageViewer::addPlanarPolygon] No layer with ID'=%s' found. Creating new one...\n", layer_id.c_str());
167 am_it = createLayer(layer_id, getSize()[0] - 1, getSize()[1] - 1, opacity,
false);
171 pcl::search::OrganizedNeighbor<T> search;
172 search.setInputCloud(
image);
173 std::vector<float> xy;
174 xy.reserve((
polygon.getContour().size() + 1) * 2);
175 for (std::size_t i = 0; i <
polygon.getContour().
size(); ++i)
178 search.projectPoint(
polygon.getContour()[i], p);
184 xy[xy.size() - 2] = xy[0];
185 xy[xy.size() - 1] = xy[1];
188 poly->setColors(
static_cast<unsigned char> (r * 255.0),
189 static_cast<unsigned char> (g * 255.0),
190 static_cast<unsigned char> (b * 255.0));
191 poly->setOpacity(opacity);
193 am_it->actor->GetScene()->AddItem(poly);
199 template <
typename T>
bool
201 const typename pcl::PointCloud<T>::ConstPtr&
image,
202 const pcl::PlanarPolygon<T>&
polygon,
203 const std::string& layer_id,
double opacity)
205 return (addPlanarPolygon(
image,
polygon, 1.0, 0.0, 0.0, layer_id, opacity));
209 template <
typename T>
bool
211 const typename pcl::PointCloud<T>::ConstPtr&
image,
214 double r,
double g,
double b,
215 const std::string& layer_id,
double opacity)
218 if (!
image->isOrganized())
222 LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(),
LayerComparator(layer_id));
223 if (am_it == layer_map_.end())
225 PCL_DEBUG(
"[pcl::visualization::ImageViewer::addRectangle] No layer with ID'=%s' found. Creating new one...\n", layer_id.c_str());
226 am_it = createLayer(layer_id, getSize()[0] - 1, getSize()[1] - 1, opacity,
false);
230 pcl::search::OrganizedNeighbor<T> search;
231 search.setInputCloud(
image);
233 T p1, p2, p3, p4, p5, p6, p7, p8;
234 p1.x = min_pt.x; p1.y = min_pt.y; p1.z = min_pt.z;
235 p2.x = min_pt.x; p2.y = min_pt.y; p2.z = max_pt.z;
236 p3.x = min_pt.x; p3.y = max_pt.y; p3.z = min_pt.z;
237 p4.x = min_pt.x; p4.y = max_pt.y; p4.z = max_pt.z;
238 p5.x = max_pt.x; p5.y = min_pt.y; p5.z = min_pt.z;
239 p6.x = max_pt.x; p6.y = min_pt.y; p6.z = max_pt.z;
240 p7.x = max_pt.x; p7.y = max_pt.y; p7.z = min_pt.z;
241 p8.x = max_pt.x; p8.y = max_pt.y; p8.z = max_pt.z;
243 std::vector<pcl::PointXY> pp_2d(8);
244 search.projectPoint(p1, pp_2d[0]);
245 search.projectPoint(p2, pp_2d[1]);
246 search.projectPoint(p3, pp_2d[2]);
247 search.projectPoint(p4, pp_2d[3]);
248 search.projectPoint(p5, pp_2d[4]);
249 search.projectPoint(p6, pp_2d[5]);
250 search.projectPoint(p7, pp_2d[6]);
251 search.projectPoint(p8, pp_2d[7]);
253 pcl::PointXY min_pt_2d, max_pt_2d;
254 min_pt_2d.x = min_pt_2d.y = std::numeric_limits<float>::max();
255 max_pt_2d.x = max_pt_2d.y = -std::numeric_limits<float>::max();
257 for (
const auto& point : pp_2d)
259 if (point.x < min_pt_2d.x) min_pt_2d.x = point.x;
260 if (point.y < min_pt_2d.y) min_pt_2d.y = point.y;
261 if (point.x > max_pt_2d.x) max_pt_2d.x = point.x;
262 if (point.y > max_pt_2d.y) max_pt_2d.y = point.y;
264 min_pt_2d.y =
float(
image->height) - min_pt_2d.y;
265 max_pt_2d.y =
float(
image->height) - max_pt_2d.y;
268 rect->setColors(
static_cast<unsigned char> (255.0 * r),
269 static_cast<unsigned char> (255.0 * g),
270 static_cast<unsigned char> (255.0 * b));
271 rect->setOpacity(opacity);
272 rect->set(min_pt_2d.x, min_pt_2d.y, max_pt_2d.x, max_pt_2d.y);
273 am_it->actor->GetScene()->AddItem(rect);
279 template <
typename T>
bool
281 const typename pcl::PointCloud<T>::ConstPtr&
image,
284 const std::string& layer_id,
double opacity)
286 return (addRectangle<T>(
image, min_pt, max_pt, 0.0, 1.0, 0.0, layer_id, opacity));
290 template <
typename T>
bool
292 const typename pcl::PointCloud<T>::ConstPtr&
image,
293 const pcl::PointCloud<T>& mask,
294 double r,
double g,
double b,
295 const std::string& layer_id,
double opacity)
298 if (!
image->isOrganized())
302 LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(),
LayerComparator(layer_id));
303 if (am_it == layer_map_.end())
305 PCL_DEBUG(
"[pcl::visualization::ImageViewer::addRectangle] No layer with ID'=%s' found. Creating new one...\n", layer_id.c_str());
306 am_it = createLayer(layer_id, getSize()[0] - 1, getSize()[1] - 1, opacity,
false);
310 pcl::search::OrganizedNeighbor<T> search;
311 search.setInputCloud(
image);
312 std::vector<pcl::PointXY> pp_2d(mask.size());
313 for (std::size_t i = 0; i < mask.size(); ++i)
314 search.projectPoint(mask[i], pp_2d[i]);
316 pcl::PointXY min_pt_2d, max_pt_2d;
317 min_pt_2d.x = min_pt_2d.y = std::numeric_limits<float>::max();
318 max_pt_2d.x = max_pt_2d.y = -std::numeric_limits<float>::max();
320 for (
const auto& point : pp_2d)
322 if (point.x < min_pt_2d.x) min_pt_2d.x = point.x;
323 if (point.y < min_pt_2d.y) min_pt_2d.y = point.y;
324 if (point.x > max_pt_2d.x) max_pt_2d.x = point.x;
325 if (point.y > max_pt_2d.y) max_pt_2d.y = point.y;
327 min_pt_2d.y =
float(
image->height) - min_pt_2d.y;
328 max_pt_2d.y =
float(
image->height) - max_pt_2d.y;
331 rect->setColors(
static_cast<unsigned char> (255.0 * r),
332 static_cast<unsigned char> (255.0 * g),
333 static_cast<unsigned char> (255.0 * b));
334 rect->setOpacity(opacity);
335 rect->set(min_pt_2d.x, min_pt_2d.y, max_pt_2d.x, max_pt_2d.y);
336 am_it->actor->GetScene()->AddItem(rect);
342 template <
typename T>
bool
344 const typename pcl::PointCloud<T>::ConstPtr&
image,
345 const pcl::PointCloud<T>& mask,
346 const std::string& layer_id,
double opacity)
348 return (addRectangle(
image, mask, 0.0, 1.0, 0.0, layer_id, opacity));
352 template <
typename Po
intT>
bool
354 const pcl::PointCloud<PointT>& source_img,
355 const pcl::PointCloud<PointT>& target_img,
358 const std::string& layer_id)
360 if (correspondences.empty())
362 PCL_DEBUG(
"[pcl::visualization::ImageViewer::addCorrespondences] An empty set of correspondences given! Nothing to display.\n");
367 LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(),
LayerComparator(layer_id));
368 if (am_it == layer_map_.end())
370 PCL_DEBUG(
"[pcl::visualization::ImageViewer::addCorrespondences] No layer with ID='%s' found. Creating new one...\n", layer_id.c_str());
371 am_it = createLayer(layer_id, source_img.width + target_img.width, std::max(source_img.height, target_img.height), 1.0,
false);
374 int src_size = source_img.width * source_img.height * 3;
375 int tgt_size = target_img.width * target_img.height * 3;
378 setSize(source_img.width + target_img.width, std::max(source_img.height, target_img.height));
381 if (data_size_ <
static_cast<std::size_t
> (src_size + tgt_size))
383 data_size_ = src_size + tgt_size;
384 data_.reset(
new unsigned char[data_size_]);
389 for (std::size_t i = 0; i < std::max(source_img.height, target_img.height); ++i)
392 if (i < source_img.height)
394 for (std::size_t k = 0; k < source_img.width; ++k)
396 data_[j++] = source_img[i * source_img.width + k].r;
397 data_[j++] = source_img[i * source_img.width + k].g;
398 data_[j++] = source_img[i * source_img.width + k].b;
403 memcpy(&data_[j], 0, source_img.width * 3);
404 j += source_img.width * 3;
408 if (i < source_img.height)
410 for (std::size_t k = 0; k < target_img.width; ++k)
412 data_[j++] = target_img[i * source_img.width + k].r;
413 data_[j++] = target_img[i * source_img.width + k].g;
414 data_[j++] = target_img[i * source_img.width + k].b;
419 memcpy(&data_[j], 0, target_img.width * 3);
420 j += target_img.width * 3;
424 void*
data =
const_cast<void*
> (
reinterpret_cast<const void*
> (data_.get()));
427 image->SetDimensions(source_img.width + target_img.width, std::max(source_img.height, target_img.height), 1);
428 image->AllocateScalars(VTK_UNSIGNED_CHAR, 3);
429 image->GetPointData()->GetScalars()->SetVoidArray(
data, data_size_, 1);
431 image_item->set(0, 0,
image);
432 interactor_style_->adjustCamera(
image, ren_);
433 am_it->actor->GetScene()->AddItem(image_item);
434 image_viewer_->SetSize(
image->GetDimensions()[0],
image->GetDimensions()[1]);
437 for (std::size_t i = 0; i < correspondences.size(); i += nth)
441 unsigned char u_r =
static_cast<unsigned char> (255.0 * r);
442 unsigned char u_g =
static_cast<unsigned char> (255.0 * g);
443 unsigned char u_b =
static_cast<unsigned char> (255.0 * b);
445 query_circle->setColors(u_r, u_g, u_b);
447 match_circle->setColors(u_r, u_g, u_b);
449 line->setColors(u_r, u_g, u_b);
451 float query_x = correspondences[i].index_query % source_img.width;
452 float match_x = correspondences[i].index_match % target_img.width + source_img.width;
453 float query_y = getSize()[1] - correspondences[i].index_query / source_img.width;
454 float match_y = getSize()[1] - correspondences[i].index_match / target_img.width;
456 query_circle->set(query_x, query_y, 3.0);
457 match_circle->set(match_x, match_y, 3.0);
458 line->set(query_x, query_y, match_x, match_y);
460 am_it->actor->GetScene()->AddItem(query_circle);
461 am_it->actor->GetScene()->AddItem(match_circle);
462 am_it->actor->GetScene()->AddItem(line);
std::shared_ptr< core::Tensor > image
boost::geometry::model::polygon< point_xy > polygon
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)
Add a generic 2D mask to an image.
bool addRectangle(const pcl::PointXY &min_pt, const pcl::PointXY &max_pt, const std::string &layer_id="rectangles", double opacity=1.0)
Add a 2D box and color its edges with a given color.
void showRGBImage(const unsigned char *data, unsigned width, unsigned height, const std::string &layer_id="rgb_image", double opacity=1.0)
Show a 2D RGB image on screen.
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)
Add a generic 2D planar polygon to an image.
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")
Add the specified correspondences to the display.
void convertRGBCloudToUChar(const pcl::PointCloud< T > &cloud, boost::shared_array< unsigned char > &data)
Convert the RGB information in a PointCloud<T> to an unsigned char array.
void addRGBImage(const unsigned char *data, unsigned width, unsigned height, const std::string &layer_id="rgb_image", double opacity=1.0, bool autoresize=true)
Add an RGB 2D image layer, but do not render it (use spin/spinOnce to update).
RGBAColorsTableType getRandomColors(size_t randomColorsNumber)
std::vector< Pair > Correspondences