ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
image_viewer.hpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2009-2012, Willow Garage, Inc.
6  * Copyright (c) 2012-, Open Perception, Inc.
7  *
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * * Redistributions of source code must retain the above copyright
15  * notice, this list of conditions and the following disclaimer.
16  * * Redistributions in binary form must reproduce the above
17  * copyright notice, this list of conditions and the following
18  * disclaimer in the documentation and/or other materials provided
19  * with the distribution.
20  * * Neither the name of the copyright holder(s) nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  */
38 
39 #ifndef PCL_VISUALIZATION_IMAGE_VISUALIZER_HPP_
40 #define PCL_VISUALIZATION_IMAGE_VISUALIZER_HPP_
41 
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>
49 
50 #include <pcl/search/organized.h>
51 #include <pcl/visualization/common/common.h>
53 
55 template <typename T> void
57  const pcl::PointCloud<T>& cloud,
58  boost::shared_array<unsigned char>& data)
59 {
60  int j = 0;
61  for (const auto& point : cloud)
62  {
63  data[j++] = point.r;
64  data[j++] = point.g;
65  data[j++] = point.b;
66  }
67 }
68 
70 template <typename T> void
71 pcl::visualization::ImageViewer::addRGBImage(const pcl::PointCloud<T>& cloud,
72  const std::string& layer_id,
73  double opacity)
74 {
75  if (data_size_ < cloud.width * cloud.height)
76  {
77  data_size_ = cloud.width * cloud.height * 3;
78  data_.reset(new unsigned char[data_size_]);
79  }
80 
81  convertRGBCloudToUChar(cloud, data_);
82 
83  return (addRGBImage(data_.get(), cloud.width, cloud.height, layer_id, opacity));
84 }
85 
87 template <typename T> void
88 pcl::visualization::ImageViewer::showRGBImage(const pcl::PointCloud<T>& cloud,
89  const std::string& layer_id,
90  double opacity)
91 {
92  addRGBImage<T>(cloud, layer_id, opacity);
93  render();
94 }
95 
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)
103 {
104  // We assume that the data passed into image is organized, otherwise this doesn't make sense
105  if (!image->isOrganized())
106  return (false);
107 
108  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
109  LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(), LayerComparator(layer_id));
110  if (am_it == layer_map_.end())
111  {
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);
114  }
115 
116  // Construct a search object to get the camera parameters
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)
122  {
123  pcl::PointXY p_projected;
124  search.projectPoint(mask[i], p_projected);
125 
126  xy.push_back(p_projected.x);
127  xy.push_back(static_cast<float> (image->height) - p_projected.y);
128  }
129 
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);
135  points->set(xy);
136  am_it->actor->GetScene()->AddItem(points);
137  return (true);
138 }
139 
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)
146 {
147  return (addMask(image, mask, 1.0, 0.0, 0.0, layer_id, opacity));
148 }
149 
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)
157 {
158  // We assume that the data passed into image is organized, otherwise this doesn't make sense
159  if (!image->isOrganized())
160  return (false);
161 
162  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
163  LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(), LayerComparator(layer_id));
164  if (am_it == layer_map_.end())
165  {
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);
168  }
169 
170  // Construct a search object to get the camera parameters and fill points
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)
176  {
177  pcl::PointXY p;
178  search.projectPoint(polygon.getContour()[i], p);
179  xy.push_back(p.x);
180  xy.push_back(p.y);
181  }
182 
183  // Close the polygon
184  xy[xy.size() - 2] = xy[0];
185  xy[xy.size() - 1] = xy[1];
186 
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);
192  poly->set(xy);
193  am_it->actor->GetScene()->AddItem(poly);
194 
195  return (true);
196 }
197 
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)
204 {
205  return (addPlanarPolygon(image, polygon, 1.0, 0.0, 0.0, layer_id, opacity));
206 }
207 
209 template <typename T> bool
211  const typename pcl::PointCloud<T>::ConstPtr& image,
212  const T& min_pt,
213  const T& max_pt,
214  double r, double g, double b,
215  const std::string& layer_id, double opacity)
216 {
217  // We assume that the data passed into image is organized, otherwise this doesn't make sense
218  if (!image->isOrganized())
219  return (false);
220 
221  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
222  LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(), LayerComparator(layer_id));
223  if (am_it == layer_map_.end())
224  {
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);
227  }
228 
229  // Construct a search object to get the camera parameters
230  pcl::search::OrganizedNeighbor<T> search;
231  search.setInputCloud(image);
232  // Project the 8 corners
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;
242 
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]);
252 
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();
256  // Search for the two extrema
257  for (const auto& point : pp_2d)
258  {
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;
263  }
264  min_pt_2d.y = float(image->height) - min_pt_2d.y;
265  max_pt_2d.y = float(image->height) - max_pt_2d.y;
266 
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);
274 
275  return (true);
276 }
277 
279 template <typename T> bool
281  const typename pcl::PointCloud<T>::ConstPtr& image,
282  const T& min_pt,
283  const T& max_pt,
284  const std::string& layer_id, double opacity)
285 {
286  return (addRectangle<T>(image, min_pt, max_pt, 0.0, 1.0, 0.0, layer_id, opacity));
287 }
288 
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)
296 {
297  // We assume that the data passed into image is organized, otherwise this doesn't make sense
298  if (!image->isOrganized())
299  return (false);
300 
301  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
302  LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(), LayerComparator(layer_id));
303  if (am_it == layer_map_.end())
304  {
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);
307  }
308 
309  // Construct a search object to get the camera parameters
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]);
315 
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();
319  // Search for the two extrema
320  for (const auto& point : pp_2d)
321  {
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;
326  }
327  min_pt_2d.y = float(image->height) - min_pt_2d.y;
328  max_pt_2d.y = float(image->height) - max_pt_2d.y;
329 
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);
337 
338  return (true);
339 }
340 
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)
347 {
348  return (addRectangle(image, mask, 0.0, 1.0, 0.0, layer_id, opacity));
349 }
350 
352 template <typename PointT> bool
354  const pcl::PointCloud<PointT>& source_img,
355  const pcl::PointCloud<PointT>& target_img,
356  const pcl::Correspondences& correspondences,
357  int nth,
358  const std::string& layer_id)
359 {
360  if (correspondences.empty())
361  {
362  PCL_DEBUG("[pcl::visualization::ImageViewer::addCorrespondences] An empty set of correspondences given! Nothing to display.\n");
363  return (false);
364  }
365 
366  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
367  LayerMap::iterator am_it = std::find_if(layer_map_.begin(), layer_map_.end(), LayerComparator(layer_id));
368  if (am_it == layer_map_.end())
369  {
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);
372  }
373 
374  int src_size = source_img.width * source_img.height * 3;
375  int tgt_size = target_img.width * target_img.height * 3;
376 
377  // Set window size
378  setSize(source_img.width + target_img.width, std::max(source_img.height, target_img.height));
379 
380  // Set data size
381  if (data_size_ < static_cast<std::size_t> (src_size + tgt_size))
382  {
383  data_size_ = src_size + tgt_size;
384  data_.reset(new unsigned char[data_size_]);
385  }
386 
387  // Copy data in VTK format
388  int j = 0;
389  for (std::size_t i = 0; i < std::max(source_img.height, target_img.height); ++i)
390  {
391  // Still need to copy the source?
392  if (i < source_img.height)
393  {
394  for (std::size_t k = 0; k < source_img.width; ++k)
395  {
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;
399  }
400  }
401  else
402  {
403  memcpy(&data_[j], 0, source_img.width * 3);
404  j += source_img.width * 3;
405  }
406 
407  // Still need to copy the target?
408  if (i < source_img.height)
409  {
410  for (std::size_t k = 0; k < target_img.width; ++k)
411  {
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;
415  }
416  }
417  else
418  {
419  memcpy(&data_[j], 0, target_img.width * 3);
420  j += target_img.width * 3;
421  }
422  }
423 
424  void* data = const_cast<void*> (reinterpret_cast<const void*> (data_.get()));
425 
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]);
435 
436  // Draw lines between the best corresponding points
437  for (std::size_t i = 0; i < correspondences.size(); i += nth)
438  {
439  double r, g, b;
440  getRandomColors(r, g, b);
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);
450 
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;
455 
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);
459 
460  am_it->actor->GetScene()->AddItem(query_circle);
461  am_it->actor->GetScene()->AddItem(match_circle);
462  am_it->actor->GetScene()->AddItem(line);
463  }
464 
465  return (true);
466 }
467 
468 #endif // PCL_VISUALIZATION_IMAGE_VISUALIZER_HPP_
std::shared_ptr< core::Tensor > image
int size
int points
boost::geometry::model::polygon< point_xy > polygon
Definition: TreeIso.cpp:37
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).
GraphType data
Definition: graph_cut.cc:138
float
RGBAColorsTableType getRandomColors(size_t randomColorsNumber)
std::vector< Pair > Correspondences