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..7c0d961 100644 --- a/src/closest_rotation.cpp +++ b/src/closest_rotation.cpp @@ -1,4 +1,7 @@ #include "closest_rotation.h" +#include +#include +#include void closest_rotation( const Eigen::Matrix3d & M, @@ -6,4 +9,8 @@ void closest_rotation( { // Replace with your code R = Eigen::Matrix3d::Identity(); + Eigen::JacobiSVD svd(M, Eigen::ComputeFullU | Eigen::ComputeFullV); + Eigen::Matrix3d Omega = (svd.matrixU() * svd.matrixV().transpose()); + R(2,2) = Omega.determinant(); + R = svd.matrixU() * R * svd.matrixV().transpose(); } diff --git a/src/hausdorff_lower_bound.cpp b/src/hausdorff_lower_bound.cpp index 8608964..638a3b8 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, @@ -8,5 +10,16 @@ double hausdorff_lower_bound( const int n) { // Replace with your code - return 0; + // Sample points from X mesh + Eigen::MatrixXd sample_P; + random_points_on_mesh(n, VX, FX, sample_P); + + // Figure out their closest points to the Y mesh + Eigen::VectorXd D; + Eigen::MatrixXd P; + Eigen::MatrixXd N; + point_mesh_distance(sample_P, VY, FY, D, P, N); + + // compute max D for the hausdorff lb + return D.maxCoeff(); } diff --git a/src/icp_single_iteration.cpp b/src/icp_single_iteration.cpp index 1e8fda9..39d8c10 100644 --- a/src/icp_single_iteration.cpp +++ b/src/icp_single_iteration.cpp @@ -1,4 +1,8 @@ #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, @@ -11,6 +15,23 @@ void icp_single_iteration( Eigen::RowVector3d & t) { // Replace with your code - R = Eigen::Matrix3d::Identity(); - t = Eigen::RowVector3d::Zero(); + + // Sample source mesh + Eigen::MatrixXd X; + random_points_on_mesh(num_samples, VX, FX, X); + + // Project sample points onto mesh Y + Eigen::VectorXd D; + Eigen::MatrixXd Py; + Eigen::MatrixXd Ny; + point_mesh_distance(X, VY, FY, D, Py, Ny); + + // Do one iteration of the chosen method to obtain an R and a t + if (method == ICP_METHOD_POINT_TO_POINT) { + point_to_point_rigid_matching(X, Py, R, t); + } + + else if (method == ICP_METHOD_POINT_TO_PLANE) { + point_to_plane_rigid_matching(X, Py, Ny, R, t); + } } diff --git a/src/point_mesh_distance.cpp b/src/point_mesh_distance.cpp index 2e6a070..e874be7 100644 --- a/src/point_mesh_distance.cpp +++ b/src/point_mesh_distance.cpp @@ -1,4 +1,6 @@ #include "point_mesh_distance.h" +#include "point_triangle_distance.h" +#include "igl/per_face_normals.h" void point_mesh_distance( const Eigen::MatrixXd & X, @@ -9,8 +11,36 @@ void point_mesh_distance( Eigen::MatrixXd & N) { // Replace with your code + D.resize(X.rows()); P.resizeLike(X); + Eigen::MatrixXd NY = Eigen::MatrixXd::Zero(FY.rows(), 3); + igl::per_face_normals(VY, FY, Eigen::Vector3d::Zero(), NY); N = Eigen::MatrixXd::Zero(X.rows(),X.cols()); - for(int i = 0;i void point_to_plane_rigid_matching( const Eigen::MatrixXd & X, @@ -7,7 +9,45 @@ void point_to_plane_rigid_matching( Eigen::Matrix3d & R, Eigen::RowVector3d & t) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); - t = Eigen::RowVector3d::Zero(); + // Construct matrix A containing points + Eigen::MatrixXd A = Eigen::MatrixXd::Zero(3 * X.rows(), 6); + Eigen::VectorXd b(3 * X.rows()); + A.block(0, 1, X.rows(), 1) = -X.col(2); + A.block(0, 2, X.rows(), 1) = X.col(1); + A.block(0, 3, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1); + A.block(X.rows(), 0, X.rows(), 1) = X.col(2); + A.block(X.rows(), 2, X.rows(), 1) = -X.col(0); + A.block(X.rows(), 4, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1); + A.block(2 * X.rows(), 0, X.rows(), 1) = -X.col(1); + A.block(2 * X.rows(), 1, X.rows(), 1) = X.col(0); + A.block(2 * X.rows(), 5, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1); + + // Construct matrix D of normals + Eigen::MatrixXd D(X.rows(), 3 * X.rows()); + Eigen::MatrixXd D_1 = N.col(0).asDiagonal(); + Eigen::MatrixXd D_2 = N.col(1).asDiagonal(); + Eigen::MatrixXd D_3 = N.col(2).asDiagonal(); + D.block(0, 0, X.rows(), X.rows()) = D_1; + D.block(0, X.rows(), X.rows(), X.rows()) = D_2; + D.block(0, 2 * X.rows(), X.rows(), X.rows()) = D_3; + + // Construct vector P -X + b << P.col(0) - X.col(0), + P.col(1) - X.col(1), + P.col(2) - X.col(2); + + // Solve for the least squares solution of ||DAx - D(P-X)||^2. + A = D * A; + b = D * b; + Eigen::MatrixXd L = A.transpose() * A; + Eigen::VectorXd r = A.transpose() * b; + Eigen::VectorXd u = L.fullPivLu().solve(r); + + // Extract R and t from solution of optimization problem + 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.tail(3); } diff --git a/src/point_to_point_rigid_matching.cpp b/src/point_to_point_rigid_matching.cpp index 079151f..f4ee8f6 100644 --- a/src/point_to_point_rigid_matching.cpp +++ b/src/point_to_point_rigid_matching.cpp @@ -1,5 +1,6 @@ #include "point_to_point_rigid_matching.h" -#include +#include "closest_rotation.h" +#include void point_to_point_rigid_matching( const Eigen::MatrixXd & X, @@ -8,7 +9,36 @@ void point_to_point_rigid_matching( Eigen::RowVector3d & t) { // Replace with your code - R = Eigen::Matrix3d::Identity(); - t = Eigen::RowVector3d::Zero(); + + // Construct matrix A and vector P - X + Eigen::MatrixXd A = Eigen::MatrixXd::Zero(3 * X.rows(), 6); + Eigen::VectorXd b(3 * X.rows()); + A.block(0, 1, X.rows(), 1) = -X.col(2); + A.block(0, 2, X.rows(), 1) = X.col(1); + A.block(0, 3, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1); + A.block(X.rows(), 0, X.rows(), 1) = X.col(2); + A.block(X.rows(), 2, X.rows(), 1) = -X.col(0); + A.block(X.rows(), 4, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1); + A.block(2 * X.rows(), 0, X.rows(), 1) = -X.col(1); + A.block(2 * X.rows(), 1, X.rows(), 1) = X.col(0); + A.block(2 * X.rows(), 5, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1); + + b << P.col(0) - X.col(0), + P.col(1) - X.col(1), + P.col(2) - X.col(2); + + // Solve for the least squares solution u, which minimizes ||Au - (P - X)||^2. + Eigen::MatrixXd L = A.transpose() * A; + b = A.transpose() * b; + Eigen::VectorXd u = L.fullPivLu().solve(b); + + // Extract the new R and t + 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.tail(3); } diff --git a/src/point_triangle_distance.cpp b/src/point_triangle_distance.cpp index 6405100..b78375e 100644 --- a/src/point_triangle_distance.cpp +++ b/src/point_triangle_distance.cpp @@ -9,6 +9,49 @@ void point_triangle_distance( Eigen::RowVector3d & p) { // Replace with your code - d = 0; - p = a; + + // Project on plane spanned by triangle and see if it lies in it + // Consulted formula at https://math.stackexchange.com/questions/544946/determine-if-projection-of-3d-point-onto-plane-is-within-a-triangle + + // Project x on plane containing the triangle + Eigen::RowVector3d u = b - a; + Eigen::RowVector3d v = c - a; + Eigen::RowVector3d tri_norm = u.cross(v); + tri_norm = tri_norm / tri_norm.norm(); + Eigen::RowVector3d w = x - a; + double gamma = (u.cross(w)).dot(tri_norm); + double beta = (w.cross(v)).dot(tri_norm); + double alpha = 1 - gamma - beta; + + // if projection lies outside triangle, project on different lines of the triangle + + if (alpha > 1 || alpha < 0 || beta > 1 || beta < 0 || gamma > 0 || gamma < 1) { + // consulted formula at https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line + double proj_ab = (a - x).dot(u) / (u.norm() * u.norm()); + double proj_ac = (a - x).dot(v) / (v.norm() * v.norm()); + double proj_bc = (b - x).dot(v-u) / ((v-u).norm() * (v-u).norm()); + proj_ab = std::min(std::max(proj_ab, 0.0), 1.0); + proj_ac = std::min(std::max(proj_ac, 0.0), 1.0); + proj_bc = std::min(std::max(proj_bc, 0.0), 1.0); + double dist_ab = ((a-x) - (proj_ab * u)).norm(); + double dist_ac = ((a-x) - (proj_ac * v)).norm(); + double dist_bc = ((b-x) - (proj_bc * (v-u))).norm(); + double min_dist = std::min(std::min(dist_ab, dist_ac), dist_bc); + if (min_dist == dist_ac) { + d = dist_ac; + p = proj_ab * u + a; + } + if (min_dist == dist_ac) { + d = dist_ac; + p = proj_ac * v + a; + } + if (min_dist == dist_bc) { + d = dist_bc; + p = proj_bc * (v-u) + b; + } + } + else { + p = alpha * a + beta * b + gamma * c; + d = (x - p).norm(); + } } diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp index 6e85d75..af6abeb 100644 --- a/src/random_points_on_mesh.cpp +++ b/src/random_points_on_mesh.cpp @@ -1,4 +1,7 @@ #include "random_points_on_mesh.h" +#include +#include +#include void random_points_on_mesh( const int n, @@ -8,6 +11,40 @@ void random_points_on_mesh( { // REPLACE WITH YOUR CODE: X.resize(n,3); - for(int i = 0;i distribution(0.0,1.0); + + for(int i = 0;i 1) { + alpha = 1 - alpha; + beta = 1 - beta; + } + + // Linear search on cum_A to find the index of face; + int idx = 0; + while (cum_A(idx) < gamma) { + idx++; + } + + // Sample point from V(idx) + Eigen::Vector3i Tri = F.row(idx); + Eigen::Vector3d v = V.row(Tri(0)); + Eigen::Vector3d w = V.row(Tri(1)) - V.row(Tri(0)); + Eigen::Vector3d u = V.row(Tri(2)) - V.row(Tri(0)); + Eigen::Vector3d point = v + alpha * u + beta * w; + X.row(i) = point; + } }