Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid phantom surfces in marching cubes hoppe #1874

Merged
merged 12 commits into from
Aug 27, 2017
91 changes: 45 additions & 46 deletions surface/include/pcl/surface/impl/marching_cubes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT>
pcl::MarchingCubes<PointNT>::MarchingCubes ()
: min_p_ (), max_p_ (), percentage_extend_grid_ (), iso_level_ ()
: min_p_ (), max_p_ (), percentage_extend_grid_ (), iso_level_ (), dist_ignore_ (-1.0f)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, in PCL we often have such simple constructor definitions right in the header file, motivation being that it's easier to see the default values. While we are at it, would you mind to transfer this to the header and also fill in the values of min_p_, max_p_, etc for clarity?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the whole constructor into header file? Maybe I could move min_p_ and max_p_ to local variable and change to vector3f, it looks a bit stateful to have them as class members. How do you say?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a brief look and they are used in different functions both in parent and deriving class. If you have an idea how to make them local without need to recompute several times, please go ahead!

{
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't this supposed to be moved to the .h file?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this entire constructor to the .h


Expand Down Expand Up @@ -97,8 +97,8 @@ pcl::MarchingCubes<PointNT>::interpolateEdge (Eigen::Vector3f &p1,

//////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointNT> void
pcl::MarchingCubes<PointNT>::createSurface (std::vector<float> &leaf_node,
Eigen::Vector3i &index_3d,
pcl::MarchingCubes<PointNT>::createSurface (const std::vector<float> &leaf_node,
const Eigen::Vector3i &index_3d,
pcl::PointCloud<PointNT> &cloud)
{
int cubeindex = 0;
Expand Down Expand Up @@ -191,7 +191,7 @@ template <typename PointNT> void
pcl::MarchingCubes<PointNT>::getNeighborList1D (std::vector<float> &leaf,
Eigen::Vector3i &index3d)
{
leaf = std::vector<float> (8, 0.0f);
leaf.resize(8);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space between resize and (8)


leaf[0] = getGridValue (index3d);
leaf[1] = getGridValue (index3d + Eigen::Vector3i (1, 0, 0));
Expand All @@ -201,6 +201,15 @@ pcl::MarchingCubes<PointNT>::getNeighborList1D (std::vector<float> &leaf,
leaf[5] = getGridValue (index3d + Eigen::Vector3i (1, 1, 0));
leaf[6] = getGridValue (index3d + Eigen::Vector3i (1, 1, 1));
leaf[7] = getGridValue (index3d + Eigen::Vector3i (0, 1, 1));

for(int i = 0; i < 8; ++i)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after for

{
if(std::isnan(leaf[i]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after if and isnan

{
leaf.clear();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after clear

break;
}
}
}


Expand Down Expand Up @@ -233,36 +242,12 @@ pcl::MarchingCubes<PointNT>::performReconstruction (pcl::PolygonMesh &output)
return;
}

// Create grid
grid_ = std::vector<float> (res_x_*res_y_*res_z_, 0.0f);

// Populate tree
tree_->setInputCloud (input_);

getBoundingBox ();

// Transform the point cloud into a voxel grid
// This needs to be implemented in a child class
voxelizeData ();

// real part of marching cube
performReconstructionProc();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment below regarding the signature change for this block and the code that can also be included inside.


pcl::toPCLPointCloud2 (intermediate_cloud_, output.cloud);

// Run the actual marching cubes algorithm, store it into a point cloud,
// and copy the point cloud + connectivity into output
pcl::PointCloud<PointNT> cloud;

for (int x = 1; x < res_x_-1; ++x)
for (int y = 1; y < res_y_-1; ++y)
for (int z = 1; z < res_z_-1; ++z)
{
Eigen::Vector3i index_3d (x, y, z);
std::vector<float> leaf_node;
getNeighborList1D (leaf_node, index_3d);
createSurface (leaf_node, index_3d, cloud);
}
pcl::toPCLPointCloud2 (cloud, output.cloud);

output.polygons.resize (cloud.size () / 3);
output.polygons.resize (intermediate_cloud_.size () / 3);
for (size_t i = 0; i < output.polygons.size (); ++i)
{
pcl::Vertices v;
Expand All @@ -288,8 +273,28 @@ pcl::MarchingCubes<PointNT>::performReconstruction (pcl::PointCloud<PointNT> &po
return;
}

// real part of marching cube
performReconstructionProc();

points.swap(intermediate_cloud_);

polygons.resize (points.size () / 3);
for (size_t i = 0; i < polygons.size (); ++i)
{
pcl::Vertices v;
v.vertices.resize (3);
for (int j = 0; j < 3; ++j)
v.vertices[j] = static_cast<int> (i) * 3 + j;
polygons[i] = v;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section can also be refactored into performReconstructionProc. It will required you to change the signature of that method to include the pointcloud and the polygon vector.


template <typename PointNT> void
pcl::MarchingCubes<PointNT>::performReconstructionProc ()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above regarding the signature for this one.

{
// Create grid
grid_ = std::vector<float> (res_x_*res_y_*res_z_, 0.0f);
// grid_ = std::vector<float> (res_x_*res_y_*res_z_, 0.0f);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't submit commented lines

grid_ = std::vector<float> (res_x_*res_y_*res_z_, NAN);

// Populate tree
tree_->setInputCloud (input_);
Expand All @@ -302,30 +307,24 @@ pcl::MarchingCubes<PointNT>::performReconstruction (pcl::PointCloud<PointNT> &po

// Run the actual marching cubes algorithm, store it into a point cloud,
// and copy the point cloud + connectivity into output
points.clear ();
intermediate_cloud_.clear();

// preallocate memory assuming a hull. suppose 6 point per voxel
intermediate_cloud_.reserve((size_t)(res_y_*res_z_ + res_x_*res_z_ + res_x_*res_y_) * 2 * 6);

for (int x = 1; x < res_x_-1; ++x)
for (int y = 1; y < res_y_-1; ++y)
for (int z = 1; z < res_z_-1; ++z)
{
Eigen::Vector3i index_3d (x, y, z);
std::vector<float> leaf_node;
getNeighborList1D (leaf_node, index_3d);
createSurface (leaf_node, index_3d, points);
if(!leaf_node.empty())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after if and empty

createSurface (leaf_node, index_3d, intermediate_cloud_);
}

polygons.resize (points.size () / 3);
for (size_t i = 0; i < polygons.size (); ++i)
{
pcl::Vertices v;
v.vertices.resize (3);
for (int j = 0; j < 3; ++j)
v.vertices[j] = static_cast<int> (i) * 3 + j;
polygons[i] = v;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In conjunction with my other comments, leave this here.

}



#define PCL_INSTANTIATE_MarchingCubes(T) template class PCL_EXPORTS pcl::MarchingCubes<T>;

#endif // PCL_SURFACE_IMPL_MARCHING_CUBES_H_
Expand Down
40 changes: 30 additions & 10 deletions surface/include/pcl/surface/impl/marching_cubes_hoppe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,26 +60,46 @@ pcl::MarchingCubesHoppe<PointNT>::~MarchingCubesHoppe ()
template <typename PointNT> void
pcl::MarchingCubesHoppe<PointNT>::voxelizeData ()
{
const bool is_far_ignored = dist_ignore_ > 0.0f;

std::vector<int> nn_indices(1, 0);
std::vector<float> nn_sqr_dists(1, 0.0f);
Eigen::Vector3f point;
PointNT p;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment regarding the two lines above: I'm an apologist of RAII. Does this grant you a super duper performance increase to justify declaring it here?

const Eigen::Vector4f delta = ( max_p_ - min_p_ ).cwiseQuotient( Eigen::Vector4f(
Copy link
Member

@SergioRAgostinho SergioRAgostinho Jun 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand the reason behind declaring this as Vector4f. Was it to make it aligned or something? Note that if you declare it Vector3f (use actually Array3f to use element wise operations later on) you can reduce the expression below, inside the nested for blocks.

Edit: Now I'm definitely sure use you want to perform element wise operations. Read on Eigen about Arrays and block. Create delta as an Array3f for the three top element block of max_p_ min_p_.

float (res_x_), float (res_y_), float (res_z_), 1.0f ) );

Eigen::Vector3f normal;

for (int x = 0; x < res_x_; ++x)
{
const int y_start = x * res_y_ * res_z_;

for (int y = 0; y < res_y_; ++y)
{
const int z_start = y_start + y * res_z_;

for (int z = 0; z < res_z_; ++z)
{
std::vector<int> nn_indices;
std::vector<float> nn_sqr_dists;

Eigen::Vector3f point;
point[0] = min_p_[0] + (max_p_[0] - min_p_[0]) * float (x) / float (res_x_);
point[1] = min_p_[1] + (max_p_[1] - min_p_[1]) * float (y) / float (res_y_);
point[2] = min_p_[2] + (max_p_[2] - min_p_[2]) * float (z) / float (res_z_);
point[0] = min_p_[0] + delta[0] * float (x);
point[1] = min_p_[1] + delta[1] * float (y);
point[2] = min_p_[2] + delta[2] * float (z);

PointNT p;
p.getVector3fMap () = point;

tree_->nearestKSearch (p, 1, nn_indices, nn_sqr_dists);

grid_[x * res_y_*res_z_ + y * res_z_ + z] = input_->points[nn_indices[0]].getNormalVector3fMap ().dot (
point - input_->points[nn_indices[0]].getVector3fMap ());
if( !is_far_ignored || nn_sqr_dists[0] < dist_ignore_ )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after if, no space inside braces

{
normal = input_->points[nn_indices[0]].getNormalVector3fMap ();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const


if(normal.norm() > 0.5f)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? I thought PCL maintains the invariant normal.norm() == 1.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some point clouds I used (maybe not generated with pcl, but with quite fine normal), some points have normal vector with length 0. Would the normal be [0] if the neighborhood is not consistent?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, at least in NormalEstimation class invalid normals are set to NaN (see here). I wonder how norm() function reacts to that.

grid_[z_start + z] = normal.dot (
point - input_->points[nn_indices[0]].getVector3fMap ());
}
}
}
}
}


Expand Down
27 changes: 25 additions & 2 deletions surface/include/pcl/surface/marching_cubes.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,15 @@ namespace pcl
getPercentageExtendGrid ()
{ return percentage_extend_grid_; }

/** \brief Method that sets the parameter that defines the distance to ignore the distance to ignore
* a grid
* \param[in] threshold of distance. if it is negative, then calculate in all grids; otherwise
* ignore grids with distance to point cloud(to nearest point) larger than distIgnore
*/
inline void
setDistanceIgnore (float distIgnore)
{ dist_ignore_ = distIgnore; }

protected:
/** \brief The data structure storing the 3D grid */
std::vector<float> grid_;
Expand All @@ -447,6 +456,15 @@ namespace pcl
/** \brief The iso level to be extracted. */
float iso_level_;

/** \brief ignore the distance function
* if it is negative
* or distance between voxel centroid and point are larger that it. */
float dist_ignore_;

/** \brief the point cloud really generated from Marching Cubes
*/
pcl::PointCloud<PointNT> intermediate_cloud_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the changes requested I think this member can be removed and just exist in a temporary scope inside the methods.


/** \brief Convert the point cloud into voxel data. */
virtual void
voxelizeData () = 0;
Expand All @@ -468,8 +486,8 @@ namespace pcl
* \param cloud point cloud to store the vertices of the polygon
*/
void
createSurface (std::vector<float> &leaf_node,
Eigen::Vector3i &index_3d,
createSurface (const std::vector<float> &leaf_node,
const Eigen::Vector3i &index_3d,
pcl::PointCloud<PointNT> &cloud);

/** \brief Get the bounding box for the input data points. */
Expand Down Expand Up @@ -508,6 +526,11 @@ namespace pcl
performReconstruction (pcl::PointCloud<PointNT> &points,
std::vector<pcl::Vertices> &polygons);

/** \brief real marching cube part. called in two performReconstruction-s
*/
virtual void
performReconstructionProc ();

public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
Expand Down
1 change: 1 addition & 0 deletions surface/include/pcl/surface/marching_cubes_hoppe.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ namespace pcl
using MarchingCubes<PointNT>::res_z_;
using MarchingCubes<PointNT>::min_p_;
using MarchingCubes<PointNT>::max_p_;
using MarchingCubes<PointNT>::dist_ignore_;

typedef typename pcl::PointCloud<PointNT>::Ptr PointCloudPtr;

Expand Down