diff --git a/README.html b/README.html index 6c13281..c163d77 100644 --- a/README.html +++ b/README.html @@ -343,7 +343,7 @@

Constant function approximation

Linear function approximation

-

If we make make a slightly more appropriate assuming that in the neighborhood +

If we make make a slightly more appropriate assumption that in the neighborhood of the \(P_Y(\z₀)\) the surface \(Y\) is a plane, then we can improve this approximation while keeping \(\f\) linear in \(\z\):

diff --git a/README.md b/README.md index bce5dc2..ce58753 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ derived our gradients geometrically. ### Linear function approximation -If we make make a slightly more appropriate assuming that in the neighborhood +If we make make a slightly more appropriate assumption that in the neighborhood of the $P_Y(\z₀)$ the surface $Y$ is a plane, then we can improve this approximation while keeping $\f$ linear in $\z$: diff --git a/src/closest_rotation.cpp b/src/closest_rotation.cpp index 54403c1..0342acc 100644 --- a/src/closest_rotation.cpp +++ b/src/closest_rotation.cpp @@ -1,9 +1,18 @@ #include "closest_rotation.h" +#include +#include void closest_rotation( const Eigen::Matrix3d & M, Eigen::Matrix3d & R) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); + + Eigen::JacobiSVD svd(M, Eigen::ComputeThinU | Eigen::ComputeThinV); + Eigen::MatrixXd U = svd.matrixU(); + Eigen::MatrixXd V = svd.matrixV(); + Eigen::Matrix3d sigma; + sigma << 1, 0 , 0, + 0, 1 , 0, + 0, 0, (U * V.transpose()).determinant(); + R = U * sigma * V.transpose(); } diff --git a/src/hausdorff_lower_bound.cpp b/src/hausdorff_lower_bound.cpp index 8608964..617e8d2 100644 --- a/src/hausdorff_lower_bound.cpp +++ b/src/hausdorff_lower_bound.cpp @@ -1,4 +1,6 @@ #include "hausdorff_lower_bound.h" +#include "random_points_on_mesh.h" +#include "point_mesh_distance.h" double hausdorff_lower_bound( const Eigen::MatrixXd & VX, @@ -7,6 +9,11 @@ double hausdorff_lower_bound( const Eigen::MatrixXi & FY, const int n) { - // Replace with your code - return 0; + Eigen::MatrixXd X, P, N; + Eigen::VectorXd D; + + random_points_on_mesh(n, VX, FX, X); + point_mesh_distance(X, VY, FY, D, P, N); + + return D.maxCoeff(); } diff --git a/src/icp_single_iteration.cpp b/src/icp_single_iteration.cpp index 1e8fda9..993b615 100644 --- a/src/icp_single_iteration.cpp +++ b/src/icp_single_iteration.cpp @@ -1,16 +1,31 @@ #include "icp_single_iteration.h" +#include "random_points_on_mesh.h" +#include "point_mesh_distance.h" +#include "point_to_point_rigid_matching.h" +#include "point_to_plane_rigid_matching.h" void icp_single_iteration( - const Eigen::MatrixXd & VX, - const Eigen::MatrixXi & FX, - const Eigen::MatrixXd & VY, - const Eigen::MatrixXi & FY, - const int num_samples, - const ICPMethod method, - Eigen::Matrix3d & R, - Eigen::RowVector3d & t) + const Eigen::MatrixXd &VX, + const Eigen::MatrixXi &FX, + const Eigen::MatrixXd &VY, + const Eigen::MatrixXi &FY, + const int num_samples, + const ICPMethod method, + Eigen::Matrix3d &R, + Eigen::RowVector3d &t) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); - t = Eigen::RowVector3d::Zero(); + Eigen::MatrixXd X, P, N; + Eigen::VectorXd D; + + random_points_on_mesh(num_samples, VX, FX, X); + point_mesh_distance(X, VY, FY, D, P, N); + + if (method == ICP_METHOD_POINT_TO_POINT) + { + point_to_point_rigid_matching(X, P, R, t); + } + else + { + point_to_plane_rigid_matching(X, P, N, R, t); + } } diff --git a/src/point_mesh_distance.cpp b/src/point_mesh_distance.cpp index 2e6a070..1069583 100644 --- a/src/point_mesh_distance.cpp +++ b/src/point_mesh_distance.cpp @@ -1,16 +1,39 @@ #include "point_mesh_distance.h" +#include "point_triangle_distance.h" +#include "igl/per_face_normals.h" void point_mesh_distance( - const Eigen::MatrixXd & X, - const Eigen::MatrixXd & VY, - const Eigen::MatrixXi & FY, - Eigen::VectorXd & D, - Eigen::MatrixXd & P, - Eigen::MatrixXd & N) + const Eigen::MatrixXd &X, + const Eigen::MatrixXd &VY, + const Eigen::MatrixXi &FY, + Eigen::VectorXd &D, + Eigen::MatrixXd &P, + Eigen::MatrixXd &N) { - // Replace with your code P.resizeLike(X); - N = Eigen::MatrixXd::Zero(X.rows(),X.cols()); - for(int i = 0;i::max(); + Eigen::RowVector3d minP; + for (int j = 0; j < FY.rows(); j++) + { + double d; + Eigen::RowVector3d p; + point_triangle_distance(X.row(i),VY.row(FY(j,0)), VY.row(FY(j,1)), VY.row(FY(j,2)), d, p); + if (minD < d) + { + minD = d; + minP = p; + } + } + + D(i) = minD; + P.row(i) = minP; + } + + igl::per_face_normals(VY,FY,N); } diff --git a/src/point_to_plane_rigid_matching.cpp b/src/point_to_plane_rigid_matching.cpp index 1978510..599363e 100644 --- a/src/point_to_plane_rigid_matching.cpp +++ b/src/point_to_plane_rigid_matching.cpp @@ -1,13 +1,42 @@ #include "point_to_plane_rigid_matching.h" +#include "closest_rotation.h" +#include void point_to_plane_rigid_matching( - const Eigen::MatrixXd & X, - const Eigen::MatrixXd & P, - const Eigen::MatrixXd & N, - Eigen::Matrix3d & R, - Eigen::RowVector3d & t) + const Eigen::MatrixXd &X, + const Eigen::MatrixXd &P, + const Eigen::MatrixXd &N, + Eigen::Matrix3d &R, + Eigen::RowVector3d &t) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); - t = Eigen::RowVector3d::Zero(); + + Eigen::VectorXd ones = Eigen::VectorXd::Ones(X.rows()); + Eigen::VectorXd zeros = Eigen::VectorXd::Zero(X.rows()); + + Eigen::VectorXd B; + B << X.col(0) - P.col(0), X.col(1) - P.col(1), X.col(2) - P.col(2); + + Eigen::MatrixXd A(3 * X.rows(), 6); + A << zeros, X.col(2), -X.col(1), ones, zeros, zeros, + -X.col(2), zeros, X.col(0), zeros, ones, zeros, + X.col(1), -X.col(0), zeros, zeros, zeros, ones; + + Eigen::MatrixXd diagN(X.rows(),3*X.rows()); + diagN << Eigen::MatrixXd (N.col(0).asDiagonal()), + Eigen::MatrixXd (N.col(1).asDiagonal()), + Eigen::MatrixXd (N.col(2).asDiagonal()); + + A = diagN * A; + B = diagN * B; + Eigen::VectorXd u = (A.transpose() * A).inverse() * (-A.transpose() * B); + + Eigen::Matrix3d M; + + M << 1, -u(2), u(1), + u(2), 1, -u(0), + -u(1), u(0), 1; + + closest_rotation(M, R); + + t << u(3), u(4), u(5); } diff --git a/src/point_to_point_rigid_matching.cpp b/src/point_to_point_rigid_matching.cpp index 079151f..6f58bed 100644 --- a/src/point_to_point_rigid_matching.cpp +++ b/src/point_to_point_rigid_matching.cpp @@ -1,14 +1,34 @@ #include "point_to_point_rigid_matching.h" #include +#include "closest_rotation.h" +#include +#include void point_to_point_rigid_matching( - const Eigen::MatrixXd & X, - const Eigen::MatrixXd & P, - Eigen::Matrix3d & R, - Eigen::RowVector3d & t) + const Eigen::MatrixXd &X, + const Eigen::MatrixXd &P, + Eigen::Matrix3d &R, + Eigen::RowVector3d &t) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); - t = Eigen::RowVector3d::Zero(); -} + Eigen::VectorXd ones = Eigen::VectorXd::Ones(X.rows()); + Eigen::VectorXd zeros = Eigen::VectorXd::Zero(X.rows()); + + Eigen::VectorXd B(X.rows() * 3); + B << X.col(0) - P.col(0), X.col(1) - P.col(1), X.col(2) - P.col(2); + + Eigen::MatrixXd A(3 * X.rows(), 6); + A << zeros, X.col(2), -X.col(1), ones, zeros, zeros, + -X.col(2), zeros, X.col(0), zeros, ones, zeros, + X.col(1), -X.col(0), zeros, zeros, zeros, ones; + + Eigen::VectorXd u = (A.transpose() * A).inverse() * (-A.transpose() * B); + Eigen::Matrix3d M; + M << 1, -u(2), u(1), + u(2), 1, -u(0), + -u(1), u(0), 1; + + closest_rotation(M, R); + + t << u(3), u(4), u(5); +} diff --git a/src/point_triangle_distance.cpp b/src/point_triangle_distance.cpp index 6405100..2ae0d78 100644 --- a/src/point_triangle_distance.cpp +++ b/src/point_triangle_distance.cpp @@ -1,14 +1,83 @@ #include "point_triangle_distance.h" +#include + +void point_on_line(const Eigen::RowVector3d &x, + const Eigen::RowVector3d &a, + const Eigen::RowVector3d &b, + const Eigen::RowVector3d &projectP, + double &d, + Eigen::RowVector3d &p) +{ + Eigen::RowVector3d nab = ((b - projectP).cross(a - projectP)).cross(b - a); + nab.normalize(); + double d_ab = abs((a - projectP).dot(nab)); + Eigen::RowVector3d pab = projectP + d_ab * nab; + double t = (pab - a).dot(b - a) / (b - a).dot(b - a); + if (t < 0) + { + p = a; + d = (x - p).norm(); + } + if (0 < t < 1) + { + p = pab; + d = (x - p).norm(); + } + if (t > 1) + { + p = b; + d = (x - p).norm(); + } +} void point_triangle_distance( - const Eigen::RowVector3d & x, - const Eigen::RowVector3d & a, - const Eigen::RowVector3d & b, - const Eigen::RowVector3d & c, - double & d, - Eigen::RowVector3d & p) + const Eigen::RowVector3d &x, + const Eigen::RowVector3d &a, + const Eigen::RowVector3d &b, + const Eigen::RowVector3d &c, + double &d, + Eigen::RowVector3d &p) { - // Replace with your code - d = 0; - p = a; + Eigen::RowVector3d v1 = b - a; + Eigen::RowVector3d v2 = -a + c; + Eigen::RowVector3d n = v1.cross(v2); + n.normalize(); + + double distance = abs((x - a).dot(n)); + Eigen::RowVector3d projectP = x - distance * n; + Eigen::RowVector3d v3 = projectP - a; + Eigen::RowVector3d v4, v5, v6; + + // based on https://math.stackexchange.com/questions/544946/determine-if-projection-of-3d-point-onto-plane-is-within-a-triangle + double u = (v2.dot(v2) * v3.dot(v1) - v1.dot(v2) * v3.dot(v2)) * 1.0 / + (v2.dot(v2) * v1.dot(v1) - v1.dot(v2) * v1.dot(v2)); + + double v = (v1.dot(v1) * v3.dot(v2) - v1.dot(v2) * v3.dot(v1)) * 1.0 / + (v2.dot(v2) * v1.dot(v1) - v1.dot(v2) * v1.dot(v2)); + + if (u >= 0 && v >= 0 && (u + v <= 1)) + { //inside + p = a + u * v1 + v * v2; + d = distance; + } + else if (u >= 0 and v < 0) + { + // On ab + point_on_line(x, a, b, projectP, d, p); + } + else if (u < 0 && v >= 0) + { + //On ac + point_on_line(x, a, c, projectP, d, p); + } + else if (u >= 0 && v >= 0 && u + v > 1) + { + //On bc + point_on_line(x, b, c, projectP, d, p); + } + else + { //Outside + p = a; + d = (x - p).norm(); + } } diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp index 6e85d75..ee01ca1 100644 --- a/src/random_points_on_mesh.cpp +++ b/src/random_points_on_mesh.cpp @@ -1,13 +1,47 @@ #include "random_points_on_mesh.h" +#include +#include +#include "igl/doublearea.h" +#include "igl/cumsum.h" -void random_points_on_mesh( - const int n, - const Eigen::MatrixXd & V, - const Eigen::MatrixXi & F, - Eigen::MatrixXd & X) +int return_index(Eigen::VectorXd C, double r) { - // REPLACE WITH YOUR CODE: - X.resize(n,3); - for(int i = 0;i= r) + return i; + } } +void random_points_on_mesh( + const int n, + const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + Eigen::MatrixXd &X) +{ + X.resize(n, 3); + + Eigen::MatrixXd list; + Eigen::VectorXd C; + igl::doublearea(V, F, list); + igl::cumsum(list, 1, C); + + //uniformed distribution from http://www.cplusplus.com/reference/random/uniform_real_distribution/ + std::default_random_engine generator; + std::uniform_real_distribution distribution(0.0, 1.0); + + for (int i = 0; i < X.rows(); i++) + { + double a = distribution(generator); + double b = distribution(generator); + double r = distribution(generator); + + if (a + b > 1.0) + { + a = 1 - a; + b = 1 - b; + } + int idx = return_index(C, r); + X.row(i) = V.row(F(idx, 0)) + a * (V.row(F(idx, 1)) - V.row(F(idx, 0))) + b * (V.row(F(idx, 2)) - V.row(F(idx, 0))); + } +}