-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
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
Add new mls projection method. Deprecated MovingLeastSquares::setPolynomialFit()
.
#1960
Add new mls projection method. Deprecated MovingLeastSquares::setPolynomialFit()
.
#1960
Conversation
a42b935
to
7d78a8b
Compare
7d78a8b
to
bb53295
Compare
Any preferences on the structure (free functions)? |
Hi, I'm away all next week, so no inputs from my side till next next week. |
I'm sorry, it is a click mistake. :'( |
Sorry for the delay guys. I'll be making a push on things until the end of the week. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @Levi-Armstrong. I'm sorry to ask for this, but I think I'll need some help to understand all changes you did. I can see you added a projection method, but you also moved a lot of code around, and it's not being easy for me to understand all changes you did.
Can you provide me with a bullet list with more details? I'm especially concerned with the refactoring process you did.
I'm also unsure about the free functions and constants but I first I need to get a better feel for the PR first.
Apologies once again.
surface/include/pcl/surface/mls.h
Outdated
|
||
namespace pcl | ||
{ | ||
|
||
const double MLS_PROJECTION_CONVERGENCE_TOLERANCE = 1e-8; | ||
const double MLS_MINIMUM_PRINCIPLE_CURVATURE = 1e-5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about having this here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where would you like them? I could also do away with them and put the values in there respected places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say so. They both are implementation details and each is used only in a single place. Just move these const definitions into their respective functions, nobody will miss them in the main header.
surface/include/pcl/surface/mls.h
Outdated
Eigen::Vector3d mean; /**< \brief The mean point of all the neighbors. */ | ||
Eigen::Vector3d plane_normal; /**< \brief The normal of the local plane of the query point. */ | ||
Eigen::Vector3d u_axis; /**< \brief The axis corresponding to the u-coordinates of the local plane of the query point. */ | ||
Eigen::Vector3d v_axis; /**< \brief The axis corresponding to the v-coordinates of the local plane of the query point. */ | ||
Eigen::VectorXd c_vec; /**< \brief The polynomial coefficients Example: z = c_vec[0] + c_vec[1]*v + c_vec[2]*v^2 + c_vec[3]*u + c_vec[4]*u*v + c_vec[5]*u^2 */ | ||
int num_neighbors; /**< \brief The number of neighbors used to create the mls surface. */ | ||
float curvature; /**< \brief The curvature at the query point. */ | ||
int order; /**< \brief The order of the polynomial used if polynomial_fit = true */ | ||
bool polynomial_fit; /**< \brief If True, a polynomial surface was fitted to the neighbors as the mls surface */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think would use a negative order
value to indicate no polynomial_fit was done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this. I can make it so zero indicates no polynomial fit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then consider this variable to be unsigned and probably you don't need 32bits to represent the order of your polynomial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the unsigned
recommendation, it expresses the intent about the expected valid range and semantics better. However, I don't think that using a smaller int will add anything to the picture and make the code more expressive. From the storage viewpoint it will probably also make no difference due to alignment.
That being said, I wonder if this variable is needed at all. Isn't it the same as c_vec.length()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only issue I see with using c_vec.length() is you can get a length of zero if the fit failed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
Sorry, I was on travel all last week. As for the free function, I went back and forth on this on whether to make them free function or member function. The main advantage is if they are free functions I can easily write tests for these individual function, but I am not opposed to moving them to member function if this is preferred. In short most of the refactoring was to reduce code duplication and make it easier to read given the addition of different projection methods.
|
surface/include/pcl/surface/mls.h
Outdated
@@ -474,7 +626,7 @@ namespace pcl | |||
} | |||
|
|||
/** \brief Smooth a given point and its neighborghood using Moving Least Squares. | |||
* \param[in] index the inex of the query point in the input cloud | |||
* \param[in] index the index of the query point in the input cloud | |||
* \param[in] nn_indices the set of nearest neighbors indices for pt | |||
* \param[in] nn_sqr_dists the set of nearest neighbors squared distances for pt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This param is gone
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will remove it.
{ | ||
} | ||
|
||
void pcl::getMLSCoordinates (const Eigen::Vector3d &pt, const pcl::MLSResult &mls_result, double &u, double &v, double &w) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return type should be on a separate line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will make the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comments applies to a number of methods below.
P_weight_Pt.llt ().solveInPlace (c_vec); | ||
} | ||
|
||
if (cache_mls_results_) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand right that caching became unconditional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is conditional. Since the the vector gets resized I just fill out the object when it is passed to the function instead of constructing a new object within the computeMLSPointNormal method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right. Thanks for clarification.
Thanks for the explanation. I will try to make the best out of it 😅
In general, you should not bend method visibility in order to be able write unit tests. Writing the tests comes after you've decided what makes sense to be public in your API.
For future PRs, please split logic changes like the ones you described into multiple commits. It makes it easier to review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still lacking insight into this, but overall I feel this code is messy. I also don't like that the default precision is double for almost everything.
I aknowledge that my comments are more directed towards the original implementation and not necessarily this PR :/
surface/include/pcl/surface/mls.h
Outdated
Eigen::Vector3d mean; /**< \brief The mean point of all the neighbors. */ | ||
Eigen::Vector3d plane_normal; /**< \brief The normal of the local plane of the query point. */ | ||
Eigen::Vector3d u_axis; /**< \brief The axis corresponding to the u-coordinates of the local plane of the query point. */ | ||
Eigen::Vector3d v_axis; /**< \brief The axis corresponding to the v-coordinates of the local plane of the query point. */ | ||
Eigen::VectorXd c_vec; /**< \brief The polynomial coefficients Example: z = c_vec[0] + c_vec[1]*v + c_vec[2]*v^2 + c_vec[3]*u + c_vec[4]*u*v + c_vec[5]*u^2 */ | ||
int num_neighbors; /**< \brief The number of neighbors used to create the mls surface. */ | ||
float curvature; /**< \brief The curvature at the query point. */ | ||
int order; /**< \brief The order of the polynomial used if polynomial_fit = true */ | ||
bool polynomial_fit; /**< \brief If True, a polynomial surface was fitted to the neighbors as the mls surface */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then consider this variable to be unsigned and probably you don't need 32bits to represent the order of your polynomial.
surface/include/pcl/surface/mls.h
Outdated
double z_v; /**< @brief The partial derivative dz/dv. */ | ||
double z_uu; /**< @brief The partial derivative d^2z/du^2. */ | ||
double z_vv; /**< @brief The partial derivative d^2z/dv^2. */ | ||
double z_uv; /**< @brief The partial derivative d^2z/dudv. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is applicable for the rest. You use double precision by default. Is this really required?
surface/include/pcl/surface/mls.h
Outdated
* \param[out] v The v-coordinate of the point in local MLS frame. | ||
* \param[out] w The w-coordinate of the point in local MLS frame. | ||
*/ | ||
void getMLSCoordinates (const Eigen::Vector3d &pt, const pcl::MLSResult &mls_result, double &u, double &v, double &w); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and a number of methods which take a reference to a const MLSResult feel like they should be methods of MLSResult.
surface/include/pcl/surface/mls.h
Outdated
* \param[out] u The u-coordinate of the point in local MLS frame. | ||
* \param[out] v The v-coordinate of the point in local MLS frame. | ||
*/ | ||
void getMLSCoordinates (const Eigen::Vector3d &pt, const pcl::MLSResult &mls_result, double &u, double &v); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
surface/include/pcl/surface/mls.h
Outdated
* \param[in] mls_result The MLS results data | ||
* \return The polynomial value at the provide uv coordinates. | ||
*/ | ||
double getPolynomialValue (const double u, const double v, const pcl::MLSResult &mls_result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest move to method.
double F2u = d.z_uv * gw + d.z_v * d.z_u - d.z_uv * w; | ||
double F2v = 1 + d.z_vv * gw + d.z_v * d.z_v - d.z_vv * w; | ||
|
||
Eigen::MatrixXd J (2, 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This matrix has known size at compile time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the idea behind Sergio's message was that J
can be changed to a fixed-sized Eigen type, which will (maybe) make the following matrix inverse computation faster by using a specialized 2x2 implementation (compared to generic size implementation).
Actually, I would expect Eigen to be smart enough to perform runtime dispatching to specialized implementations, but we'd need to dig into the sources to find this out. So the easiest way to ensure we are as efficient as possible is to just switch to Eigen::Matrix2d
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the idea behind Sergio's message was that J can be changed to a fixed-sized Eigen type, which will (maybe) make the following matrix inverse computation faster by using a specialized 2x2 implementation (compared to generic size implementation).
It was this exactly. Sorry for being unclear.
J(1, 1) = F2v; | ||
|
||
Eigen::Vector2d err (e1, e2); | ||
Eigen::MatrixXd update = J.inverse () * err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this one as well it's 2x1
Eigen::Vector4d model_coefficients; | ||
pcl::eigen33 (covariance_matrix, eigen_value, eigen_vector); | ||
model_coefficients.head<3> ().matrix () = eigen_vector; | ||
model_coefficients[3] = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the constructor instead
mls_result.curvature = static_cast<float> (covariance_matrix.trace ()); | ||
// Compute the curvature surface change | ||
if (mls_result.curvature != 0) | ||
mls_result.curvature = fabsf (float (eigen_value / double (mls_result.curvature))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the curvature casted to double?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, please always use std::abs
et al., we had problems with MSVC if I remember correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that is how it was in the original implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, my comment was in reference to the cast to double.
for (size_t ni = 0; ni < mls_result.num_neighbors; ++ni) | ||
{ | ||
de_meaned[ni][0] = cloud.points[nn_indices[ni]].x - mls_result.mean[0]; | ||
de_meaned[ni][1] = cloud.points[nn_indices[ni]].y - mls_result.mean[1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure vectorization is properly exploited here. You should use the vector maps for this operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't follow. Can you provide more detail?
I will work on the requested changes today. |
I am happy to change things from the original implementation, just let me know what it is if it has not already been mentioned above. |
For the mls class should the setPolynomialFit be removed and just use the order for determining if a polynomial fit should be used like the request in the MLSResults structure? The reason I ask is with the change to MLSResults I had to add logic to the setPolynomialFit and SetPolynomialOrder to insure order get set properly since the order is the only parameter used within the MLSResults to determine if a polynomial fit was used. |
ccc98d9
to
44f59da
Compare
@SergioRAgostinho and @taketwo I have made most of the requested changes except a few which I had questions about. |
0fa5ea3
to
07a5533
Compare
07a5533
to
491cc44
Compare
@taketwo and @SergioRAgostinho I removed the mls variable polynomial_fit_ and fix style guide related issues. Should I fix item internal to pcl that use the setPolynomialFit (MLS Test and Command Line Tool)? |
You mean to remove usage of depreciated functions within the library? Definitely! |
55fd8be
to
c60e97c
Compare
OK, The use of setPolynomialFit has been removed. |
Yeah. I'm somewhat constrained right now, so the moment you find it ready, just mark it accepted. I'll do flash review and merge.
…Sent from my Phone
On 18 Sep 2017, at 10:23, Sergey Alexandrov ***@***.***> wrote:
For the mls class should the setPolynomialFit be removed and just use the order for determining if a polynomial fit should be used
I agree that having two functions is confusing. But I'd prefer to keep the old one with a depreciation warning.
remove polynomial_fit_ field
add depreciation warning
update docstring for setPolynomialOrder()
Regarding the rest of the PR, from my side it looks good. Please just make sure to format spaces between function name and brackets according to the PCL style guide.
@SergioRAgostinho Do you think we can merge this soon so that Levi can move on with other PRs?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just waiting for the tools build job to complete (since it pertains this PR) and then I'll proceed with the merge.
FYI, our tools job is exceeding the build time. It needs some intervention. |
This qualifies for new feature, doesn't it? |
Probably. I'll add the label. |
PR PointCloudLibrary#1960 deprecated `setPolynomialFit()` but did not update the openni_mls_smoothing app.
PR PointCloudLibrary#1960 deprecated `setPolynomialFit()` but did not update the openni_mls_smoothing app.
MovingLeastSquares::setPolynomialFit()
.
@Levi-Armstrong Hi, I am reading the code of |
This PR adds a new projection method which projects the point orthogonal to the mls surface. This produces a better reconstruction in areas of high curvature when using a polynomial fit. There are several free function added that could be used by other mls techniques and meshing algorithms.
Note: This is builds on PR #1952 which has not been merged but I wanted to get this submitted to start discussions on the new features added.