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

implementation of ball pivoting for surface reconstruction #1950

Open
wants to merge 36 commits into
base: master
Choose a base branch
from

Conversation

t-lou
Copy link
Contributor

@t-lou t-lou commented Jul 26, 2017

algorithm from

@article{bernardini1999ball,
   title={The ball-pivoting algorithm for surface reconstruction},
   author={Bernardini, Fausto and Mittleman, Joshua and Rushmeier, Holly and Silva, Cl{\'a}udio and Taubin, Gabriel},
   journal={IEEE transactions on visualization and computer graphics},
   volume={5},
   number={4},
   pages={349--359},
   year={1999},
   publisher={IEEE}
 }

general structure from https://github.com/rodschulz/BPA
fitted from https://github.com/t-lou/bpa_remake

example
snapshot00
input point cloud
snapshot01
output surface

@SergioRAgostinho SergioRAgostinho added this to the pcl-1.9.0 milestone Jul 26, 2017
@SergioRAgostinho SergioRAgostinho added the needs: code review Specify why not closed/merged yet label Jul 26, 2017
@SergioRAgostinho
Copy link
Member

Thanks for contributing this. It looks pretty neat 👍

Some preliminary comments:

  • You haven't submitted any unit tests. How do you know your implementation is doing exactly what it's supposed to? The reconstructed image you posted is definitely a good sign but the question is still valid. I'm bringing this up because we need a mechanism to ensure us that if we have to modify your implementation we're not breaking it's intended functionality, constraints, assumptions, etc...
  • Can provide you submit also an example? Something like the images you posted, if possible with pointclouds we already have in repo's test folder.
  • Can you also submit a tutorial explaining a little bit what's the primer behind this method? What are its strong points? What made you use it/implement it/need it instead of the other surface reconstruction methods we provide for instance?

@SergioRAgostinho SergioRAgostinho added the needs: author reply Specify why not closed/merged yet label Jul 26, 2017
@t-lou
Copy link
Contributor Author

t-lou commented Jul 26, 2017

I'll do unit test soon.

But where should I write the example and tutorial?

@taketwo
Copy link
Member

taketwo commented Jul 26, 2017

All our tutorials are contained in this same repository under the "doc" directory. You can take any of the existing tutorials as an example. But I would say let's first concentrate on adding unit tests, reviewing and merging this. The tutorial can be added in a separate PR later on.

Unfortunately I don't have any time today/tomorrow, but I will go through the code and leave comments later this week.

@taketwo taketwo self-assigned this Jul 26, 2017
@t-lou
Copy link
Contributor Author

t-lou commented Jul 26, 2017

The unit test I wrote failed. But it is successful locally. Can you help me?

@SergioRAgostinho
Copy link
Member

Unfortunately there's not much I can do on my side other than run it on a Darwin platform.

You need to think about a synthetic small test case, in which you can theoretically predict the outcome, without having to run the algorithm. You should be able to fully understand what are the algorithm's capabilities and limitations, and come up with test scenarios which exploit success and failure conditions.

Loading the bun.pcd and then performing reconstruction, gives me the impression you're doing the exactly reverse process which is better than not having a unit test, but it's not a good unit test.

My advice is to rewrite the unit test following the guidelines I just gave you.

@t-lou
Copy link
Contributor Author

t-lou commented Jul 26, 2017

Do you mean composing a hand-made simple cloud and doing unit test with it?

But aren't many other unit tests done with the reverse way?

@SergioRAgostinho
Copy link
Member

Do you mean composing a hand-made simple cloud and doing unit test with it?

Exactly

But aren't many other unit tests done with the reverse way?

In our repo? Definitely. But it doesn't necessarily make them good unit tests. :)

Copy link
Member

@taketwo taketwo left a comment

Choose a reason for hiding this comment

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

Thanks for contributing. I think we will need several rounds of reviews to get this merged.

My first request will be on the structural side of things. The Edge and Front classes are currently members of the BPA class and, as such, are implicitly templated on the point type. However, their implementation is virtually independent of the point type. Only exception is the Edge::center_ member field of type PointNT. However, only the XYZ coordinates of it are used, so it can be replaced with Eigen::Vector3f.

With this is mind, I'd propose to strip these classes off from BPA. They can remain in the same header file, maybe renamed to BallPivotingEdge and BallPivotingFront to make their scope clear. The implementation can be moved to the "cpp" file because it is template-free.

This change will decrease compilation time and the binary size, because useless instantiations of virtually the same classes will be avoided.

#define PCL_BALL_PIVOTING_H_

/**
* This class is an implementation of ball pivoting algorithm with some different details
Copy link
Member

Choose a reason for hiding this comment

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

This Doxygen comment should be moved to be directly above the class declaration.
We typically have just authors/title/journal instead of a full BiBTeX entry, i.e. in this case

Bernardini F., Mittleman J., Rushmeier H., Silva C., Taubin G., "The ball-pivoting algorithm for surface reconstruction", TVCG'99

Also, please add \ingroup surface tag.
Finally, it would be great to have a sentence or two with high-level usage guideline. For example (just copied from the paper, assume these claims are actually true):

BPA exhibits linear time complexity and robustness to noise in real scanned data. The output mesh is a manifold of an alpha-shape of the point set.
BPA requires that the points are distributed over the entire surface with a spatial frequency greater than or equal to a certain minimum value. An estimate of surface normal should be available for every point.

Copy link
Member

@taketwo taketwo left a comment

Choose a reason for hiding this comment

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

I think that Edge should be a plain data struct. In its current form it has no "business logic", i.e. methods besides from member field accessors. And the accessors are trivial, there are no invariants to be maintained (besides from the size of id_vertices_ vector, which as I commented, should be just an array).

@@ -121,6 +122,7 @@ if(build)
"include/pcl/${SUBSYS_NAME}/marching_cubes.h"
"include/pcl/${SUBSYS_NAME}/marching_cubes_hoppe.h"
"include/pcl/${SUBSYS_NAME}/marching_cubes_rbf.h"
"include/pcl/${SUBSYS_NAME}/ball_pivoting.h"
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 forget that the header with helper structures should also be installed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this issue still open?

Copy link
Member

Choose a reason for hiding this comment

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

No, it's not an issue, I see you added "ball_pivoting_front.h", thanks.

* Software License Agreement (BSD License)
*
* Point Cloud Library (PCL) - www.pointclouds.org
* Copyright (c) 2014-, Open Perception, Inc.
Copy link
Member

Choose a reason for hiding this comment

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

2017

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is it necessary? not the starting time of the license?

Copy link
Member

Choose a reason for hiding this comment

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

This is a brand new file, created in 2017. You can not claim copyright for the time before it existed!

#ifndef PCL_BALL_PIVOTING_H_
#define PCL_BALL_PIVOTING_H_

#include <memory>
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this a c++11 header? Why do we need it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for pair, not right?

Copy link
Member

Choose a reason for hiding this comment

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

No, std::pair comes from <utility> header. But you don't need to include it explicitly, for example map already has it.


#include <memory>
#include <vector>
#include <map>
Copy link
Member

Choose a reason for hiding this comment

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

map and set includes should be in the "ball_pivoting_front.h", that's where they are needed.

* Software License Agreement (BSD License)
*
* Point Cloud Library (PCL) - www.pointclouds.org
* Copyright (c) 2014-, Open Perception, Inc.
Copy link
Member

Choose a reason for hiding this comment

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

2017

* returns the center of ball
* @return
*/
Eigen::Vector3f
Copy link
Member

Choose a reason for hiding this comment

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

Better return by const reference to avoid copies.

class Edge
{
protected:
/** list of vertice index, should have length 2 */
Copy link
Member

Choose a reason for hiding this comment

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

If this list should have a fixed length, why not using good ol' array? I.e. uint32_t id_vertices_[2];

Edge ();

/**
* a fake constructor with only vertice index on edge
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if "fake" is a good terminology, it's not clear what is implied. What is the state of the other variables? Is an edge constructed this way valid?

* @return
*/
uint32_t
getIdVertice (const size_t id) const
Copy link
Member

Choose a reason for hiding this comment

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

Should be getIdVertex

@taketwo
Copy link
Member

taketwo commented Jul 30, 2017

Speaking of the Edge structure, since it's just a data container, I think there is no need to have getter functions (e.g. getCenter, isBackBall, etc.). The member fields can simply be public. Also, there is no need to define an empty destructor.

@t-lou
Copy link
Contributor Author

t-lou commented Jul 31, 2017

Buenas mis amigos. Is the code and unit test better now?

Copy link
Member

@taketwo taketwo left a comment

Choose a reason for hiding this comment

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

Thanks for the effort so far, I think we are getting close.
A general comment: we should not have free functions inside the "hpp" file. They are an implementation detail of BPA, but by keeping them free you "leak" them into the rest of PCL. Please turn them into private class methods, maybe static if you want. Also, please follow the PCL style guide regarding the function naming, not snake_case(), but camelCase().

/*
* Software License Agreement (BSD License)
*
* Copyright (c) 2017, Willow Garage, Inc.
Copy link
Member

Choose a reason for hiding this comment

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

This should be Open Perception

* 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 Willow Garage, Inc. nor the names of its
Copy link
Member

Choose a reason for hiding this comment

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

Here also, no Willow Garage. Please just copy the license from your other files.

} // namespace pcl

// Instantiations of specific point types
PCL_INSTANTIATE(BallPivoting, (pcl::PointNormal)(pcl::PointXYZRGBNormal)(pcl::PointXYZINormal))
Copy link
Member

Choose a reason for hiding this comment

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

This should be conditionally compiled depending on the PCL_NO_PRECOMPILE macro. Please take this as an example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both XYZ and Normal are necessary, which macro should I use? I didn't find "PCL_XYZ_NORMAL_POINT_TYPES" or "PCL_XYZNORMAL_POINT_TYPES"

Copy link
Member

Choose a reason for hiding this comment

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

I did not mean to change the point types, my comment was about conditional compilation. I.e. something like:

#ifndef PCL_NO_PRECOMPILE
#include <pcl/point_types.h>
#include <pcl/impl/instantiate.hpp>
PCL_INSTANTIATE(BallPivoting, (pcl::PointNormal)(pcl::PointXYZRGBNormal)(pcl::PointXYZINormal))
#endif  

Copy link
Contributor Author

Choose a reason for hiding this comment

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

now there is no problem, i though you meant compilation with different range of types


#include <boost/make_shared.hpp>

#include <pcl/impl/instantiate.hpp>
Copy link
Member

Choose a reason for hiding this comment

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

This include should be inside the conditional compilation branch, see my comment in the end of this file.

* @param num_point_in_radius
* @param ratio_success
*/
virtual void
Copy link
Member

Choose a reason for hiding this comment

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

Why is this virtual? Do we expect other algorithms deriving from BPA that will want to modify this behavior?

* @param ratio_success
*/
virtual void
setEstimatedRadius (const int num_sample_point, const int num_point_in_radius, const float ratio_success);
Copy link
Member

Choose a reason for hiding this comment

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

I think this is not a clear name. What about setSearchRadiusAutomatic()? This will make connection with the setSearchRadius() function, thus making clear for the user that these two functions have the same purpose of setting the search radius. Further, I think we should provide some reasonable default values for the arguments.

*/
template<typename PointNT>
double
guess_radius (const typename PCLSurfaceBase<PointNT>::KdTreePtr &kdtree,
Copy link
Member

Choose a reason for hiding this comment

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

I do not see a reason to have this as a free function. This simply provides an implementation for setEstimatedRadius() and is only used there.

BTW, these are the default parameters I asked for.

* @param point0
* @param point1
* @param center
* @param plane the rotation is along the normal vector of plane
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to explicitly pass the plane? Isn't it implicitly determined by the three points?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The direction can be inferred in normal vector of the plane, either alpha in one direction or 360-alpha in the other

Copy link
Member

Choose a reason for hiding this comment

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

Hm, I still not sure if I get this. The angle between these two vectors is simply

alpha = acos(vc0.dot(vc1));

And then, based on direction of the plane normal, you want to change it to 2pi - alpha?

virtual void
setEstimatedRadius (const int num_sample_point, const int num_point_in_radius, const float ratio_success);
void
setSearchRadiusAutomatically (const int num_sample_point = 0,
Copy link
Member

Choose a reason for hiding this comment

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

Can this be a typo? Before when the function was called guess_radius the default was 500.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

should not be, now non-positive parameter would be changed to one ratio of point cloud size, 10% or 20% i remember

Copy link
Member

Choose a reason for hiding this comment

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

This definitely needs to be documented. What if instead of number of sample points this function take the fraction of sample points, between 0 and 1? And the default value will be explicitly 0.2?

* @param point0
* @param point1
* @param center
* @param plane the rotation is along the normal vector of plane
Copy link
Member

Choose a reason for hiding this comment

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

Hm, I still not sure if I get this. The angle between these two vectors is simply

alpha = acos(vc0.dot(vc1));

And then, based on direction of the plane normal, you want to change it to 2pi - alpha?

@t-lou t-lou closed this Jan 16, 2018
@t-lou t-lou deleted the bpa branch January 16, 2018 21:06
@t-lou t-lou restored the bpa branch January 16, 2018 21:06
@t-lou t-lou reopened this Jan 16, 2018
@t-lou
Copy link
Contributor Author

t-lou commented Jan 16, 2018

Hi, sorry for the miss-operations. After some reading, I think geometry.h is better as a container for the first two function: plane in between and circle centre, how do you think? I can also correct this odd thing, that the functions are defined in .h file.

Maybe also the case that angle.hpp should be a cpp file, as there is no template. How do you like?

@t-lou
Copy link
Contributor Author

t-lou commented Apr 23, 2018

Hello, the CI seems to fail because time went out, what could I do about it? Thx :)

@SergioRAgostinho
Copy link
Member

Rebase with master. The tests were split in two jobs IIRC and you should no longer have timeouts.

@t-lou
Copy link
Contributor Author

t-lou commented May 5, 2018

Hello, thank you for your suggestion, it looks like the VTK cannot be found.. What should I do?

@SergioRAgostinho SergioRAgostinho removed this from the pcl-1.9.0 milestone Aug 26, 2018
@stale
Copy link

stale bot commented Feb 21, 2020

This pull request has been automatically marked as stale because it hasn't had
any activity in the past 60 days. Commenting or adding a new commit to the
pull request will revert this.

Come back whenever you have time. We look forward to your contribution.

@stale stale bot added the status: stale label Feb 21, 2020
@larshg larshg added the changelog: new feature Meta-information for changelog generation label May 30, 2023
@stale stale bot removed the status: stale label May 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog: new feature Meta-information for changelog generation module: surface needs: code review Specify why not closed/merged yet needs: more work Specify why not closed/merged yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants