Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
texture_mapping.hpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2010, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage, Inc. nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $Id: texture_mapping.hpp 6064 2012-06-29 17:57:23Z raph $
35  *
36  */
37 
38 #ifndef PCL_SURFACE_IMPL_TEXTURE_MAPPING_HPP_
39 #define PCL_SURFACE_IMPL_TEXTURE_MAPPING_HPP_
40 
42 
44 template<typename PointInT> std::vector<Eigen::Vector2f>
46  const Eigen::Vector3f &p1,
47  const Eigen::Vector3f &p2,
48  const Eigen::Vector3f &p3)
49 {
50  std::vector<Eigen::Vector2f> tex_coordinates;
51  // process for each face
52  Eigen::Vector3f p1p2 (p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]);
53  Eigen::Vector3f p1p3 (p3[0] - p1[0], p3[1] - p1[1], p3[2] - p1[2]);
54  Eigen::Vector3f p2p3 (p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2]);
55 
56  // Normalize
57  p1p2 = p1p2 / std::sqrt (p1p2.dot (p1p2));
58  p1p3 = p1p3 / std::sqrt (p1p3.dot (p1p3));
59  p2p3 = p2p3 / std::sqrt (p2p3.dot (p2p3));
60 
61  // compute vector normal of a face
62  Eigen::Vector3f f_normal = p1p2.cross (p1p3);
63  f_normal = f_normal / std::sqrt (f_normal.dot (f_normal));
64 
65  // project vector field onto the face: vector v1_projected = v1 - Dot(v1, n) * n;
66  Eigen::Vector3f f_vector_field = vector_field_ - vector_field_.dot (f_normal) * f_normal;
67 
68  // Normalize
69  f_vector_field = f_vector_field / std::sqrt (f_vector_field.dot (f_vector_field));
70 
71  // texture coordinates
72  Eigen::Vector2f tp1, tp2, tp3;
73 
74  double alpha = std::acos (f_vector_field.dot (p1p2));
75 
76  // distance between 3 vertices of triangles
77  double e1 = (p2 - p3).norm () / f_;
78  double e2 = (p1 - p3).norm () / f_;
79  double e3 = (p1 - p2).norm () / f_;
80 
81  // initialize
82  tp1[0] = 0.0;
83  tp1[1] = 0.0;
84 
85  tp2[0] = static_cast<float> (e3);
86  tp2[1] = 0.0;
87 
88  // determine texture coordinate tp3;
89  double cos_p1 = (e2 * e2 + e3 * e3 - e1 * e1) / (2 * e2 * e3);
90  double sin_p1 = sqrt (1 - (cos_p1 * cos_p1));
91 
92  tp3[0] = static_cast<float> (cos_p1 * e2);
93  tp3[1] = static_cast<float> (sin_p1 * e2);
94 
95  // rotating by alpha (angle between V and pp1 & pp2)
96  Eigen::Vector2f r_tp2, r_tp3;
97  r_tp2[0] = static_cast<float> (tp2[0] * std::cos (alpha) - tp2[1] * std::sin (alpha));
98  r_tp2[1] = static_cast<float> (tp2[0] * std::sin (alpha) + tp2[1] * std::cos (alpha));
99 
100  r_tp3[0] = static_cast<float> (tp3[0] * std::cos (alpha) - tp3[1] * std::sin (alpha));
101  r_tp3[1] = static_cast<float> (tp3[0] * std::sin (alpha) + tp3[1] * std::cos (alpha));
102 
103  // shifting
104  tp1[0] = tp1[0];
105  tp2[0] = r_tp2[0];
106  tp3[0] = r_tp3[0];
107  tp1[1] = tp1[1];
108  tp2[1] = r_tp2[1];
109  tp3[1] = r_tp3[1];
110 
111  float min_x = tp1[0];
112  float min_y = tp1[1];
113  if (min_x > tp2[0])
114  min_x = tp2[0];
115  if (min_x > tp3[0])
116  min_x = tp3[0];
117  if (min_y > tp2[1])
118  min_y = tp2[1];
119  if (min_y > tp3[1])
120  min_y = tp3[1];
121 
122  if (min_x < 0)
123  {
124  tp1[0] = tp1[0] - min_x;
125  tp2[0] = tp2[0] - min_x;
126  tp3[0] = tp3[0] - min_x;
127  }
128  if (min_y < 0)
129  {
130  tp1[1] = tp1[1] - min_y;
131  tp2[1] = tp2[1] - min_y;
132  tp3[1] = tp3[1] - min_y;
133  }
134 
135  tex_coordinates.push_back (tp1);
136  tex_coordinates.push_back (tp2);
137  tex_coordinates.push_back (tp3);
138  return (tex_coordinates);
139 }
140 
142 template<typename PointInT> void
144 {
145  // mesh information
146  int nr_points = tex_mesh.cloud.width * tex_mesh.cloud.height;
147  int point_size = static_cast<int> (tex_mesh.cloud.data.size ()) / nr_points;
148 
149  // temporary PointXYZ
150  float x, y, z;
151  // temporary face
152  Eigen::Vector3f facet[3];
153 
154  // texture coordinates for each mesh
155  std::vector<std::vector<Eigen::Vector2f> > texture_map;
156 
157  for (size_t m = 0; m < tex_mesh.tex_polygons.size (); ++m)
158  {
159  // texture coordinates for each mesh
160  std::vector<Eigen::Vector2f> texture_map_tmp;
161 
162  // processing for each face
163  for (size_t i = 0; i < tex_mesh.tex_polygons[m].size (); ++i)
164  {
165  size_t idx;
166 
167  // get facet information
168  for (size_t j = 0; j < tex_mesh.tex_polygons[m][i].vertices.size (); ++j)
169  {
170  idx = tex_mesh.tex_polygons[m][i].vertices[j];
171  memcpy (&x, &tex_mesh.cloud.data[idx * point_size + tex_mesh.cloud.fields[0].offset], sizeof(float));
172  memcpy (&y, &tex_mesh.cloud.data[idx * point_size + tex_mesh.cloud.fields[1].offset], sizeof(float));
173  memcpy (&z, &tex_mesh.cloud.data[idx * point_size + tex_mesh.cloud.fields[2].offset], sizeof(float));
174  facet[j][0] = x;
175  facet[j][1] = y;
176  facet[j][2] = z;
177  }
178 
179  // get texture coordinates of each face
180  std::vector<Eigen::Vector2f> tex_coordinates = mapTexture2Face (facet[0], facet[1], facet[2]);
181  for (size_t n = 0; n < tex_coordinates.size (); ++n)
182  texture_map_tmp.push_back (tex_coordinates[n]);
183  }// end faces
184 
185  // texture materials
186  std::stringstream tex_name;
187  tex_name << "material_" << m;
188  tex_name >> tex_material_.tex_name;
189  tex_material_.tex_file = tex_files_[m];
190  tex_mesh.tex_materials.push_back (tex_material_);
191 
192  // texture coordinates
193  tex_mesh.tex_coordinates.push_back (texture_map_tmp);
194  }// end meshes
195 }
196 
198 template<typename PointInT> void
200 {
201  // mesh information
202  int nr_points = tex_mesh.cloud.width * tex_mesh.cloud.height;
203  int point_size = static_cast<int> (tex_mesh.cloud.data.size ()) / nr_points;
204 
205  float x_lowest = 100000;
206  float x_highest = 0;
207  float y_lowest = 100000;
208  //float y_highest = 0 ;
209  float z_lowest = 100000;
210  float z_highest = 0;
211  float x_, y_, z_;
212 
213  for (int i = 0; i < nr_points; ++i)
214  {
215  memcpy (&x_, &tex_mesh.cloud.data[i * point_size + tex_mesh.cloud.fields[0].offset], sizeof(float));
216  memcpy (&y_, &tex_mesh.cloud.data[i * point_size + tex_mesh.cloud.fields[1].offset], sizeof(float));
217  memcpy (&z_, &tex_mesh.cloud.data[i * point_size + tex_mesh.cloud.fields[2].offset], sizeof(float));
218  // x
219  if (x_ <= x_lowest)
220  x_lowest = x_;
221  if (x_ > x_lowest)
222  x_highest = x_;
223 
224  // y
225  if (y_ <= y_lowest)
226  y_lowest = y_;
227  //if (y_ > y_lowest) y_highest = y_;
228 
229  // z
230  if (z_ <= z_lowest)
231  z_lowest = z_;
232  if (z_ > z_lowest)
233  z_highest = z_;
234  }
235  // x
236  float x_range = (x_lowest - x_highest) * -1;
237  float x_offset = 0 - x_lowest;
238  // x
239  // float y_range = (y_lowest - y_highest)*-1;
240  // float y_offset = 0 - y_lowest;
241  // z
242  float z_range = (z_lowest - z_highest) * -1;
243  float z_offset = 0 - z_lowest;
244 
245  // texture coordinates for each mesh
246  std::vector<std::vector<Eigen::Vector2f> > texture_map;
247 
248  for (size_t m = 0; m < tex_mesh.tex_polygons.size (); ++m)
249  {
250  // texture coordinates for each mesh
251  std::vector<Eigen::Vector2f> texture_map_tmp;
252 
253  // processing for each face
254  for (size_t i = 0; i < tex_mesh.tex_polygons[m].size (); ++i)
255  {
256  size_t idx;
257  Eigen::Vector2f tmp_VT;
258  for (size_t j = 0; j < tex_mesh.tex_polygons[m][i].vertices.size (); ++j)
259  {
260  idx = tex_mesh.tex_polygons[m][i].vertices[j];
261  memcpy (&x_, &tex_mesh.cloud.data[idx * point_size + tex_mesh.cloud.fields[0].offset], sizeof(float));
262  memcpy (&y_, &tex_mesh.cloud.data[idx * point_size + tex_mesh.cloud.fields[1].offset], sizeof(float));
263  memcpy (&z_, &tex_mesh.cloud.data[idx * point_size + tex_mesh.cloud.fields[2].offset], sizeof(float));
264 
265  // calculate uv coordinates
266  tmp_VT[0] = (x_ + x_offset) / x_range;
267  tmp_VT[1] = (z_ + z_offset) / z_range;
268  texture_map_tmp.push_back (tmp_VT);
269  }
270  }// end faces
271 
272  // texture materials
273  std::stringstream tex_name;
274  tex_name << "material_" << m;
275  tex_name >> tex_material_.tex_name;
276  tex_material_.tex_file = tex_files_[m];
277  tex_mesh.tex_materials.push_back (tex_material_);
278 
279  // texture coordinates
280  tex_mesh.tex_coordinates.push_back (texture_map_tmp);
281  }// end meshes
282 }
283 
285 template<typename PointInT> void
287 {
288 
289  if (tex_mesh.tex_polygons.size () != cams.size () + 1)
290  {
291  PCL_ERROR ("The mesh should be divided into nbCamera+1 sub-meshes.\n");
292  PCL_ERROR ("You provided %d cameras and a mesh containing %d sub-meshes.\n", cams.size (), tex_mesh.tex_polygons.size ());
293  return;
294  }
295 
296  PCL_INFO ("You provided %d cameras and a mesh containing %d sub-meshes.\n", cams.size (), tex_mesh.tex_polygons.size ());
297 
300 
301  // convert mesh's cloud to pcl format for ease
302  pcl::fromROSMsg (tex_mesh.cloud, *originalCloud);
303 
304  // texture coordinates for each mesh
305  std::vector<std::vector<Eigen::Vector2f> > texture_map;
306 
307  for (size_t m = 0; m < cams.size (); ++m)
308  {
309  // get current camera parameters
310  Camera current_cam = cams[m];
311 
312  // get camera transform
313  Eigen::Affine3f cam_trans = current_cam.pose;
314 
315  // transform cloud into current camera frame
316  pcl::transformPointCloud (*originalCloud, *camera_transformed_cloud, cam_trans.inverse ());
317 
318  // vector of texture coordinates for each face
319  std::vector<Eigen::Vector2f> texture_map_tmp;
320 
321  // processing each face visible by this camera
322  pcl::PointXYZ pt;
323  size_t idx;
324  for (size_t i = 0; i < tex_mesh.tex_polygons[m].size (); ++i)
325  {
326  Eigen::Vector2f tmp_VT;
327  // for each point of this face
328  for (size_t j = 0; j < tex_mesh.tex_polygons[m][i].vertices.size (); ++j)
329  {
330  // get point
331  idx = tex_mesh.tex_polygons[m][i].vertices[j];
332  pt = camera_transformed_cloud->points[idx];
333 
334  // compute UV coordinates for this point
335  getPointUVCoordinates (pt, current_cam, tmp_VT);
336  texture_map_tmp.push_back (tmp_VT);
337 
338  }// end points
339  }// end faces
340 
341  // texture materials
342  std::stringstream tex_name;
343  tex_name << "material_" << m;
344  tex_name >> tex_material_.tex_name;
345  tex_material_.tex_file = current_cam.texture_file;
346  tex_mesh.tex_materials.push_back (tex_material_);
347 
348  // texture coordinates
349  tex_mesh.tex_coordinates.push_back (texture_map_tmp);
350  }// end cameras
351 
352  // push on extra empty UV map (for unseen faces) so that obj writer does not crash!
353  std::vector<Eigen::Vector2f> texture_map_tmp;
354  for (size_t i = 0; i < tex_mesh.tex_polygons[cams.size ()].size (); ++i)
355  for (size_t j = 0; j < tex_mesh.tex_polygons[cams.size ()][i].vertices.size (); ++j)
356  {
357  Eigen::Vector2f tmp_VT;
358  tmp_VT[0] = -1;
359  tmp_VT[1] = -1;
360  texture_map_tmp.push_back (tmp_VT);
361  }
362 
363  tex_mesh.tex_coordinates.push_back (texture_map_tmp);
364 
365  // push on an extra dummy material for the same reason
366  std::stringstream tex_name;
367  tex_name << "material_" << cams.size ();
368  tex_name >> tex_material_.tex_name;
369  tex_material_.tex_file = "occluded.jpg";
370  tex_mesh.tex_materials.push_back (tex_material_);
371 
372 }
373 
375 template<typename PointInT> bool
377 {
378  Eigen::Vector3f direction;
379  direction (0) = pt.x;
380  direction (1) = pt.y;
381  direction (2) = pt.z;
382 
383  std::vector<int> indices;
384 
385  PointCloudConstPtr cloud (new PointCloud());
386  cloud = octree->getInputCloud();
387 
388  double distance_threshold = octree->getResolution();
389 
390  // raytrace
391  octree->getIntersectedVoxelIndices(direction, -direction, indices);
392 
393  int nbocc = static_cast<int> (indices.size ());
394  for (size_t j = 0; j < indices.size (); j++)
395  {
396  // if intersected point is on the over side of the camera
397  if (pt.z * cloud->points[indices[j]].z < 0)
398  {
399  nbocc--;
400  continue;
401  }
402 
403  if (fabs (cloud->points[indices[j]].z - pt.z) <= distance_threshold)
404  {
405  // points are very close to each-other, we do not consider the occlusion
406  nbocc--;
407  }
408  }
409 
410  if (nbocc == 0)
411  return (false);
412  else
413  return (true);
414 }
415 
417 template<typename PointInT> void
419  PointCloudPtr &filtered_cloud,
420  const double octree_voxel_size, std::vector<int> &visible_indices,
421  std::vector<int> &occluded_indices)
422 {
423  // variable used to filter occluded points by depth
424  double maxDeltaZ = octree_voxel_size;
425 
426  // create an octree to perform rayTracing
427  OctreePtr octree (new Octree (octree_voxel_size));
428  // create octree structure
429  octree->setInputCloud (input_cloud);
430  // update bounding box automatically
431  octree->defineBoundingBox ();
432  // add points in the tree
433  octree->addPointsFromInputCloud ();
434 
435  visible_indices.clear ();
436 
437  // for each point of the cloud, raycast toward camera and check intersected voxels.
438  Eigen::Vector3f direction;
439  std::vector<int> indices;
440  for (size_t i = 0; i < input_cloud->points.size (); ++i)
441  {
442  direction (0) = input_cloud->points[i].x;
443  direction (1) = input_cloud->points[i].y;
444  direction (2) = input_cloud->points[i].z;
445 
446  // if point is not occluded
447  octree->getIntersectedVoxelIndices (direction, -direction, indices);
448 
449  int nbocc = static_cast<int> (indices.size ());
450  for (size_t j = 0; j < indices.size (); j++)
451  {
452  // if intersected point is on the over side of the camera
453  if (input_cloud->points[i].z * input_cloud->points[indices[j]].z < 0)
454  {
455  nbocc--;
456  continue;
457  }
458 
459  if (fabs (input_cloud->points[indices[j]].z - input_cloud->points[i].z) <= maxDeltaZ)
460  {
461  // points are very close to each-other, we do not consider the occlusion
462  nbocc--;
463  }
464  }
465 
466  if (nbocc == 0)
467  {
468  // point is added in the filtered mesh
469  filtered_cloud->points.push_back (input_cloud->points[i]);
470  visible_indices.push_back (static_cast<int> (i));
471  }
472  else
473  {
474  occluded_indices.push_back (static_cast<int> (i));
475  }
476  }
477 
478 }
479 
481 template<typename PointInT> void
482 pcl::TextureMapping<PointInT>::removeOccludedPoints (const pcl::TextureMesh &tex_mesh, pcl::TextureMesh &cleaned_mesh, const double octree_voxel_size)
483 {
484  // copy mesh
485  cleaned_mesh = tex_mesh;
486 
489 
490  // load points into a PCL format
491  pcl::fromROSMsg (tex_mesh.cloud, *cloud);
492 
493  std::vector<int> visible, occluded;
494  removeOccludedPoints (cloud, filtered_cloud, octree_voxel_size, visible, occluded);
495 
496  // Now that we know which points are visible, let's iterate over each face.
497  // if the face has one invisible point => out!
498  for (size_t polygons = 0; polygons < cleaned_mesh.tex_polygons.size (); ++polygons)
499  {
500  // remove all faces from cleaned mesh
501  cleaned_mesh.tex_polygons[polygons].clear ();
502  // iterate over faces
503  for (size_t faces = 0; faces < tex_mesh.tex_polygons[polygons].size (); ++faces)
504  {
505  // check if all the face's points are visible
506  bool faceIsVisible = true;
507  std::vector<int>::iterator it;
508 
509  // iterate over face's vertex
510  for (size_t points = 0; points < tex_mesh.tex_polygons[polygons][faces].vertices.size (); ++points)
511  {
512  it = find (occluded.begin (), occluded.end (), tex_mesh.tex_polygons[polygons][faces].vertices[points]);
513 
514  if (it == occluded.end ())
515  {
516  // point is not in the occluded vector
517  // PCL_INFO (" VISIBLE!\n");
518  }
519  else
520  {
521  // point was occluded
522  // PCL_INFO(" OCCLUDED!\n");
523  faceIsVisible = false;
524  }
525  }
526 
527  if (faceIsVisible)
528  {
529  cleaned_mesh.tex_polygons[polygons].push_back (tex_mesh.tex_polygons[polygons][faces]);
530  }
531 
532  }
533  }
534 }
535 
537 template<typename PointInT> void
539  const double octree_voxel_size)
540 {
541  PointCloudPtr cloud (new PointCloud);
542 
543  // load points into a PCL format
544  pcl::fromROSMsg (tex_mesh.cloud, *cloud);
545 
546  std::vector<int> visible, occluded;
547  removeOccludedPoints (cloud, filtered_cloud, octree_voxel_size, visible, occluded);
548 
549 }
550 
552 template<typename PointInT> int
554  const pcl::texture_mapping::CameraVector &cameras, const double octree_voxel_size,
555  PointCloud &visible_pts)
556 {
557  if (tex_mesh.tex_polygons.size () != 1)
558  {
559  PCL_ERROR ("The mesh must contain only 1 sub-mesh!\n");
560  return (-1);
561  }
562 
563  if (cameras.size () == 0)
564  {
565  PCL_ERROR ("Must provide at least one camera info!\n");
566  return (-1);
567  }
568 
569  // copy mesh
570  sorted_mesh = tex_mesh;
571  // clear polygons from cleaned_mesh
572  sorted_mesh.tex_polygons.clear ();
573 
577 
578  // load points into a PCL format
579  pcl::fromROSMsg (tex_mesh.cloud, *original_cloud);
580 
581  // for each camera
582  for (size_t cam = 0; cam < cameras.size (); ++cam)
583  {
584  // get camera pose as transform
585  Eigen::Affine3f cam_trans = cameras[cam].pose;
586 
587  // transform original cloud in camera coordinates
588  pcl::transformPointCloud (*original_cloud, *transformed_cloud, cam_trans.inverse ());
589 
590  // find occlusions on transformed cloud
591  std::vector<int> visible, occluded;
592  removeOccludedPoints (transformed_cloud, filtered_cloud, octree_voxel_size, visible, occluded);
593  visible_pts = *filtered_cloud;
594 
595  // find visible faces => add them to polygon N for camera N
596  // add polygon group for current camera in clean
597  std::vector<pcl::Vertices> visibleFaces_currentCam;
598  // iterate over the faces of the current mesh
599  for (size_t faces = 0; faces < tex_mesh.tex_polygons[0].size (); ++faces)
600  {
601  // check if all the face's points are visible
602  bool faceIsVisible = true;
603  std::vector<int>::iterator it;
604 
605  // iterate over face's vertex
606  for (size_t current_pt_indice = 0; faceIsVisible && current_pt_indice < tex_mesh.tex_polygons[0][faces].vertices.size (); ++current_pt_indice)
607  {
608  // TODO this is far too long! Better create an helper function that raycasts here.
609  it = find (occluded.begin (), occluded.end (), tex_mesh.tex_polygons[0][faces].vertices[current_pt_indice]);
610 
611  if (it == occluded.end ())
612  {
613  // point is not occluded
614  // does it land on the camera's image plane?
615  pcl::PointXYZ pt = transformed_cloud->points[tex_mesh.tex_polygons[0][faces].vertices[current_pt_indice]];
616  Eigen::Vector2f dummy_UV;
617  if (!getPointUVCoordinates (pt, cameras[cam], dummy_UV))
618  {
619  // point is not visible by the camera
620  faceIsVisible = false;
621  }
622  }
623  else
624  {
625  faceIsVisible = false;
626  }
627  }
628 
629  if (faceIsVisible)
630  {
631  // push current visible face into the sorted mesh
632  visibleFaces_currentCam.push_back (tex_mesh.tex_polygons[0][faces]);
633  // remove it from the unsorted mesh
634  tex_mesh.tex_polygons[0].erase (tex_mesh.tex_polygons[0].begin () + faces);
635  faces--;
636  }
637 
638  }
639  sorted_mesh.tex_polygons.push_back (visibleFaces_currentCam);
640  }
641 
642  // we should only have occluded and non-visible faces left in tex_mesh.tex_polygons[0]
643  // we need to add them as an extra polygon in the sorted mesh
644  sorted_mesh.tex_polygons.push_back (tex_mesh.tex_polygons[0]);
645  return (0);
646 }
647 
649 template<typename PointInT> void
652  const double octree_voxel_size, const bool show_nb_occlusions,
653  const int max_occlusions)
654  {
655  // variable used to filter occluded points by depth
656  double maxDeltaZ = octree_voxel_size * 2.0;
657 
658  // create an octree to perform rayTracing
660  octree = new pcl::octree::OctreePointCloudSearch<PointInT> (octree_voxel_size);
661  // create octree structure
662  octree->setInputCloud (input_cloud);
663  // update bounding box automatically
664  octree->defineBoundingBox ();
665  // add points in the tree
666  octree->addPointsFromInputCloud ();
667 
668  // ray direction
669  Eigen::Vector3f direction;
670 
671  std::vector<int> indices;
672  // point from where we ray-trace
673  pcl::PointXYZI pt;
674 
675  std::vector<double> zDist;
676  std::vector<double> ptDist;
677  // for each point of the cloud, ray-trace toward the camera and check intersected voxels.
678  for (size_t i = 0; i < input_cloud->points.size (); ++i)
679  {
680  direction (0) = input_cloud->points[i].x;
681  pt.x = input_cloud->points[i].x;
682  direction (1) = input_cloud->points[i].y;
683  pt.y = input_cloud->points[i].y;
684  direction (2) = input_cloud->points[i].z;
685  pt.z = input_cloud->points[i].z;
686 
687  // get number of occlusions for that point
688  indices.clear ();
689  int nbocc = octree->getIntersectedVoxelIndices (direction, -direction, indices);
690 
691  nbocc = static_cast<int> (indices.size ());
692 
693  // TODO need to clean this up and find tricks to get remove aliasaing effect on planes
694  for (size_t j = 0; j < indices.size (); j++)
695  {
696  // if intersected point is on the over side of the camera
697  if (pt.z * input_cloud->points[indices[j]].z < 0)
698  {
699  nbocc--;
700  }
701  else if (fabs (input_cloud->points[indices[j]].z - pt.z) <= maxDeltaZ)
702  {
703  // points are very close to each-other, we do not consider the occlusion
704  nbocc--;
705  }
706  else
707  {
708  zDist.push_back (fabs (input_cloud->points[indices[j]].z - pt.z));
709  ptDist.push_back (pcl::euclideanDistance (input_cloud->points[indices[j]], pt));
710  }
711  }
712 
713  if (show_nb_occlusions)
714  (nbocc <= max_occlusions) ? (pt.intensity = static_cast<float> (nbocc)) : (pt.intensity = static_cast<float> (max_occlusions));
715  else
716  (nbocc == 0) ? (pt.intensity = 0) : (pt.intensity = 1);
717 
718  colored_cloud->points.push_back (pt);
719  }
720 
721  if (zDist.size () >= 2)
722  {
723  std::sort (zDist.begin (), zDist.end ());
724  std::sort (ptDist.begin (), ptDist.end ());
725  }
726 }
727 
729 template<typename PointInT> void
731  double octree_voxel_size, bool show_nb_occlusions, int max_occlusions)
732 {
733  // load points into a PCL format
735  pcl::fromROSMsg (tex_mesh.cloud, *cloud);
736 
737  showOcclusions (cloud, colored_cloud, octree_voxel_size, show_nb_occlusions, max_occlusions);
738 }
739 
741 template<typename PointInT> void
743 {
744 
745  if (mesh.tex_polygons.size () != 1)
746  return;
747 
749 
750  pcl::fromROSMsg (mesh.cloud, *mesh_cloud);
751 
752  std::vector<pcl::Vertices> faces;
753 
754  for (int current_cam = 0; current_cam < static_cast<int> (cameras.size ()); ++current_cam)
755  {
756  PCL_INFO ("Processing camera %d of %d.\n", current_cam+1, cameras.size ());
757 
758  // transform mesh into camera's frame
760  pcl::transformPointCloud (*mesh_cloud, *camera_cloud, cameras[current_cam].pose.inverse ());
761 
762  // CREATE UV MAP FOR CURRENT FACES
764  std::vector<pcl::Vertices>::iterator current_face;
765  std::vector<bool> visibility;
766  visibility.resize (mesh.tex_polygons[current_cam].size ());
767  std::vector<UvIndex> indexes_uv_to_points;
768  // for each current face
769 
770  //TODO change this
771  pcl::PointXY nan_point;
772  nan_point.x = std::numeric_limits<float>::quiet_NaN ();
773  nan_point.y = std::numeric_limits<float>::quiet_NaN ();
774  UvIndex u_null;
775  u_null.idx_cloud = -1;
776  u_null.idx_face = -1;
777 
778  int cpt_invisible=0;
779  for (int idx_face = 0; idx_face < static_cast<int> (mesh.tex_polygons[current_cam].size ()); ++idx_face)
780  {
781  //project each vertice, if one is out of view, stop
782  pcl::PointXY uv_coord1;
783  pcl::PointXY uv_coord2;
784  pcl::PointXY uv_coord3;
785 
786  if (isFaceProjected (cameras[current_cam],
787  camera_cloud->points[mesh.tex_polygons[current_cam][idx_face].vertices[0]],
788  camera_cloud->points[mesh.tex_polygons[current_cam][idx_face].vertices[1]],
789  camera_cloud->points[mesh.tex_polygons[current_cam][idx_face].vertices[2]],
790  uv_coord1,
791  uv_coord2,
792  uv_coord3))
793  {
794  // face is in the camera's FOV
795 
796  // add UV coordinates
797  projections->points.push_back (uv_coord1);
798  projections->points.push_back (uv_coord2);
799  projections->points.push_back (uv_coord3);
800 
801  // remember corresponding face
802  UvIndex u1, u2, u3;
803  u1.idx_cloud = mesh.tex_polygons[current_cam][idx_face].vertices[0];
804  u2.idx_cloud = mesh.tex_polygons[current_cam][idx_face].vertices[1];
805  u3.idx_cloud = mesh.tex_polygons[current_cam][idx_face].vertices[2];
806  u1.idx_face = idx_face; u2.idx_face = idx_face; u3.idx_face = idx_face;
807  indexes_uv_to_points.push_back (u1);
808  indexes_uv_to_points.push_back (u2);
809  indexes_uv_to_points.push_back (u3);
810 
811  //keep track of visibility
812  visibility[idx_face] = true;
813  }
814  else
815  {
816  projections->points.push_back (nan_point);
817  projections->points.push_back (nan_point);
818  projections->points.push_back (nan_point);
819  indexes_uv_to_points.push_back (u_null);
820  indexes_uv_to_points.push_back (u_null);
821  indexes_uv_to_points.push_back (u_null);
822  //keep track of visibility
823  visibility[idx_face] = false;
824  cpt_invisible++;
825  }
826  }
827 
828  // projections contains all UV points of the current faces
829  // indexes_uv_to_points links a uv point to its point in the camera cloud
830  // visibility contains tells if a face was in the camera FOV (false = skip)
831 
832  // TODO handle case were no face could be projected
833  if (visibility.size () - cpt_invisible !=0)
834  {
835  //create kdtree
837  kdtree.setInputCloud (projections);
838 
839  std::vector<int> idxNeighbors;
840  std::vector<float> neighborsSquaredDistance;
841  // af first (idx_pcan < current_cam), check if some of the faces attached to previous cameras occlude the current faces
842  // then (idx_pcam == current_cam), check for self occlusions. At this stage, we skip faces that were already marked as occluded
843  cpt_invisible = 0;
844  for (int idx_pcam = 0 ; idx_pcam <= current_cam ; ++idx_pcam)
845  {
846  // project all faces
847  for (int idx_face = 0; idx_face < static_cast<int> (mesh.tex_polygons[idx_pcam].size ()); ++idx_face)
848  {
849 
850  if (idx_pcam == current_cam && !visibility[idx_face])
851  {
852  // we are now checking for self occlusions within the current faces
853  // the current face was already declared as occluded.
854  // therefore, it cannot occlude another face anymore => we skip it
855  continue;
856  }
857 
858  // project each vertice, if one is out of view, stop
859  pcl::PointXY uv_coord1;
860  pcl::PointXY uv_coord2;
861  pcl::PointXY uv_coord3;
862 
863  if (isFaceProjected (cameras[current_cam],
864  camera_cloud->points[mesh.tex_polygons[idx_pcam][idx_face].vertices[0]],
865  camera_cloud->points[mesh.tex_polygons[idx_pcam][idx_face].vertices[1]],
866  camera_cloud->points[mesh.tex_polygons[idx_pcam][idx_face].vertices[2]],
867  uv_coord1,
868  uv_coord2,
869  uv_coord3))
870  {
871  // face is in the camera's FOV
872  //get its circumsribed circle
873  double radius;
874  pcl::PointXY center;
875  getTriangleCircumcenterAndSize (uv_coord1, uv_coord2, uv_coord3, center, radius);
876 
877  // get points inside circ.circle
878  if (kdtree.radiusSearch (center, radius, idxNeighbors, neighborsSquaredDistance) > 0 )
879  {
880  // for each neighbor
881  for (size_t i = 0; i < idxNeighbors.size (); ++i)
882  {
883  if (std::max (camera_cloud->points[mesh.tex_polygons[idx_pcam][idx_face].vertices[0]].z,
884  std::max (camera_cloud->points[mesh.tex_polygons[idx_pcam][idx_face].vertices[1]].z,
885  camera_cloud->points[mesh.tex_polygons[idx_pcam][idx_face].vertices[2]].z))
886  < camera_cloud->points[indexes_uv_to_points[idxNeighbors[i]].idx_cloud].z)
887  {
888  // neighbor is farther than all the face's points. Check if it falls into the triangle
889  if (checkPointInsideTriangle(uv_coord1, uv_coord2, uv_coord3, projections->points[idxNeighbors[i]]))
890  {
891  // current neighbor is inside triangle and is closer => the corresponding face
892  visibility[indexes_uv_to_points[idxNeighbors[i]].idx_face] = false;
893  cpt_invisible++;
894  //TODO we could remove the projections of this face from the kd-tree cloud, but I fond it slower, and I need the point to keep ordered to querry UV coordinates later
895  }
896  }
897  }
898  }
899  }
900  }
901  }
902  }
903 
904  // now, visibility is true for each face that belongs to the current camera
905  // if a face is not visible, we push it into the next one.
906 
907  if (static_cast<int> (mesh.tex_coordinates.size ()) <= current_cam)
908  {
909  std::vector<Eigen::Vector2f> dummy_container;
910  mesh.tex_coordinates.push_back (dummy_container);
911  }
912  mesh.tex_coordinates[current_cam].resize (3 * visibility.size ());
913 
914  std::vector<pcl::Vertices> occluded_faces;
915  occluded_faces.resize (visibility.size ());
916  std::vector<pcl::Vertices> visible_faces;
917  visible_faces.resize (visibility.size ());
918 
919  int cpt_occluded_faces = 0;
920  int cpt_visible_faces = 0;
921 
922  for (size_t idx_face = 0 ; idx_face < visibility.size () ; ++idx_face)
923  {
924  if (visibility[idx_face])
925  {
926  // face is visible by the current camera copy UV coordinates
927  mesh.tex_coordinates[current_cam][cpt_visible_faces * 3](0) = projections->points[idx_face*3].x;
928  mesh.tex_coordinates[current_cam][cpt_visible_faces * 3](1) = projections->points[idx_face*3].y;
929 
930  mesh.tex_coordinates[current_cam][cpt_visible_faces * 3 + 1](0) = projections->points[idx_face*3 + 1].x;
931  mesh.tex_coordinates[current_cam][cpt_visible_faces * 3 + 1](1) = projections->points[idx_face*3 + 1].y;
932 
933  mesh.tex_coordinates[current_cam][cpt_visible_faces * 3 + 2](0) = projections->points[idx_face*3 + 2].x;
934  mesh.tex_coordinates[current_cam][cpt_visible_faces * 3 + 2](1) = projections->points[idx_face*3 + 2].y;
935 
936  visible_faces[cpt_visible_faces] = mesh.tex_polygons[current_cam][idx_face];
937 
938  cpt_visible_faces++;
939  }
940  else
941  {
942  // face is occluded copy face into temp vector
943  occluded_faces[cpt_occluded_faces] = mesh.tex_polygons[current_cam][idx_face];
944  cpt_occluded_faces++;
945  }
946  }
947  mesh.tex_coordinates[current_cam].resize (cpt_visible_faces*3);
948 
949  occluded_faces.resize (cpt_occluded_faces);
950  mesh.tex_polygons.push_back (occluded_faces);
951 
952  visible_faces.resize (cpt_visible_faces);
953  mesh.tex_polygons[current_cam].clear ();
954  mesh.tex_polygons[current_cam] = visible_faces;
955 
956  int nb_faces = 0;
957  for (int i = 0; i < static_cast<int> (mesh.tex_polygons.size ()); i++)
958  nb_faces += static_cast<int> (mesh.tex_polygons[i].size ());
959  }
960 
961  // we have been through all the cameras.
962  // if any faces are left, they were not visible by any camera
963  // we still need to produce uv coordinates for them
964 
965  if (mesh.tex_coordinates.size() <= cameras.size ())
966  {
967  std::vector<Eigen::Vector2f> dummy_container;
968  mesh.tex_coordinates.push_back(dummy_container);
969  }
970 
971 
972  for(size_t idx_face = 0 ; idx_face < mesh.tex_polygons[cameras.size()].size() ; ++idx_face)
973  {
974  Eigen::Vector2f UV1, UV2, UV3;
975  UV1(0) = -1.0; UV1(1) = -1.0;
976  UV2(0) = -1.0; UV2(1) = -1.0;
977  UV3(0) = -1.0; UV3(1) = -1.0;
978  mesh.tex_coordinates[cameras.size()].push_back(UV1);
979  mesh.tex_coordinates[cameras.size()].push_back(UV2);
980  mesh.tex_coordinates[cameras.size()].push_back(UV3);
981  }
982 
983 }
984 
986 template<typename PointInT> inline void
987 pcl::TextureMapping<PointInT>::getTriangleCircumcenterAndSize(const pcl::PointXY &p1, const pcl::PointXY &p2, const pcl::PointXY &p3, pcl::PointXY &circomcenter, double &radius)
988 {
989  // we simplify the problem by translating the triangle's origin to its first point
990  pcl::PointXY ptB, ptC;
991  ptB.x = p2.x - p1.x; ptB.y = p2.y - p1.y; // B'=B-A
992  ptC.x = p3.x - p1.x; ptC.y = p3.y - p1.y; // C'=C-A
993 
994  double D = 2.0*(ptB.x*ptC.y - ptB.y*ptC.x); // D'=2(B'x*C'y - B'y*C'x)
995 
996  // Safety check to avoid division by zero
997  if(D == 0)
998  {
999  circomcenter.x = p1.x;
1000  circomcenter.y = p1.y;
1001  }
1002  else
1003  {
1004  // compute squares once
1005  double bx2 = ptB.x * ptB.x; // B'x^2
1006  double by2 = ptB.y * ptB.y; // B'y^2
1007  double cx2 = ptC.x * ptC.x; // C'x^2
1008  double cy2 = ptC.y * ptC.y; // C'y^2
1009 
1010  // compute circomcenter's coordinates (translate back to original coordinates)
1011  circomcenter.x = static_cast<float> (p1.x + (ptC.y*(bx2 + by2) - ptB.y*(cx2 + cy2)) / D);
1012  circomcenter.y = static_cast<float> (p1.y + (ptB.x*(cx2 + cy2) - ptC.x*(bx2 + by2)) / D);
1013  }
1014 
1015  radius = sqrt( (circomcenter.x - p1.x)*(circomcenter.x - p1.x) + (circomcenter.y - p1.y)*(circomcenter.y - p1.y));//2.0* (p1.x*(p2.y - p3.y) + p2.x*(p3.y - p1.y) + p3.x*(p1.y - p2.y));
1016 }
1017 
1019 template<typename PointInT> inline bool
1020 pcl::TextureMapping<PointInT>::getPointUVCoordinates(const pcl::PointXYZ &pt, const Camera &cam, pcl::PointXY &UV_coordinates)
1021 {
1022  if (pt.z > 0)
1023  {
1024  // compute image center and dimension
1025  double sizeX = cam.width;
1026  double sizeY = cam.height;
1027  double cx = sizeX / 2.0;
1028  double cy = sizeY / 2.0;
1029 
1030  // project point on camera's image plane
1031  UV_coordinates.x = static_cast<float> ((cam.focal_length * (pt.x / pt.z) + cx) / sizeX); //horizontal
1032  UV_coordinates.y = 1.0f - static_cast<float> ((cam.focal_length * (pt.y / pt.z) + cy) / sizeY); //vertical
1033 
1034  // point is visible!
1035  if (UV_coordinates.x >= 0.0 && UV_coordinates.x <= 1.0 && UV_coordinates.y >= 0.0 && UV_coordinates.y <= 1.0)
1036  return (true); // point was visible by the camera
1037  }
1038 
1039  // point is NOT visible by the camera
1040  UV_coordinates.x = -1.0f;
1041  UV_coordinates.y = -1.0f;
1042  return (false); // point was not visible by the camera
1043 }
1044 
1046 template<typename PointInT> inline bool
1048 {
1049  // Compute vectors
1050  Eigen::Vector2d v0, v1, v2;
1051  v0(0) = p3.x - p1.x; v0(1) = p3.y - p1.y; // v0= C - A
1052  v1(0) = p2.x - p1.x; v1(1) = p2.y - p1.y; // v1= B - A
1053  v2(0) = pt.x - p1.x; v2(1) = pt.y - p1.y; // v2= P - A
1054 
1055  // Compute dot products
1056  double dot00 = v0.dot(v0); // dot00 = dot(v0, v0)
1057  double dot01 = v0.dot(v1); // dot01 = dot(v0, v1)
1058  double dot02 = v0.dot(v2); // dot02 = dot(v0, v2)
1059  double dot11 = v1.dot(v1); // dot11 = dot(v1, v1)
1060  double dot12 = v1.dot(v2); // dot12 = dot(v1, v2)
1061 
1062  // Compute barycentric coordinates
1063  double invDenom = 1.0 / (dot00*dot11 - dot01*dot01);
1064  double u = (dot11*dot02 - dot01*dot12) * invDenom;
1065  double v = (dot00*dot12 - dot01*dot02) * invDenom;
1066 
1067  // Check if point is in triangle
1068  return ((u >= 0) && (v >= 0) && (u + v < 1));
1069 }
1070 
1072 template<typename PointInT> inline bool
1073 pcl::TextureMapping<PointInT>::isFaceProjected (const Camera &camera, const pcl::PointXYZ &p1, const pcl::PointXYZ &p2, const pcl::PointXYZ &p3, pcl::PointXY &proj1, pcl::PointXY &proj2, pcl::PointXY &proj3)
1074 {
1075  return (getPointUVCoordinates(p1, camera, proj1)
1076  &&
1077  getPointUVCoordinates(p2, camera, proj2)
1078  &&
1079  getPointUVCoordinates(p3, camera, proj3)
1080  );
1081 }
1082 
1083 #define PCL_INSTANTIATE_TextureMapping(T) \
1084  template class PCL_EXPORTS pcl::TextureMapping<T>;
1085 
1086 #endif /* TEXTURE_MAPPING_HPP_ */
1087