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..9049dc6 100644 --- a/src/closest_rotation.cpp +++ b/src/closest_rotation.cpp @@ -1,9 +1,22 @@ #include "closest_rotation.h" +#include +#include + +using namespace Eigen; void closest_rotation( const Eigen::Matrix3d & M, Eigen::Matrix3d & R) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); + JacobiSVD svd(M, ComputeFullU | ComputeFullV); + MatrixXd U = svd.matrixU(); + MatrixXd V = svd.matrixV(); + MatrixXd Vt = V.transpose(); + double det = (U * Vt).determinant(); + Eigen::Matrix3d Omega; + Omega << 1, 0, 0, + 0, 1, 0, + 0, 0, det; + R = U * Omega * Vt; } + diff --git a/src/hausdorff_lower_bound.cpp b/src/hausdorff_lower_bound.cpp index 8608964..d576615 100644 --- a/src/hausdorff_lower_bound.cpp +++ b/src/hausdorff_lower_bound.cpp @@ -1,4 +1,6 @@ #include "hausdorff_lower_bound.h" +#include "point_mesh_distance.h" +#include "random_points_on_mesh.h" double hausdorff_lower_bound( const Eigen::MatrixXd & VX, @@ -7,6 +9,12 @@ double hausdorff_lower_bound( const Eigen::MatrixXi & FY, const int n) { - // Replace with your code - return 0; + Eigen::MatrixXd points, N, P; + Eigen::VectorXd D; + random_points_on_mesh(n, VX, FX, points); + point_mesh_distance(points, VY, FY, D, P, N); + double lowerBound = D.maxCoeff(); + return lowerBound; } + + diff --git a/src/icp_single_iteration.cpp b/src/icp_single_iteration.cpp index 1e8fda9..6969b5b 100644 --- a/src/icp_single_iteration.cpp +++ b/src/icp_single_iteration.cpp @@ -1,4 +1,10 @@ #include "icp_single_iteration.h" +#include "random_points_on_mesh.h" +#include "point_mesh_distance.h" +#include "point_to_plane_rigid_matching.h" +#include "point_to_point_rigid_matching.h" + +using namespace Eigen; void icp_single_iteration( const Eigen::MatrixXd & VX, @@ -10,7 +16,16 @@ void icp_single_iteration( Eigen::Matrix3d & R, Eigen::RowVector3d & t) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); - t = Eigen::RowVector3d::Zero(); + VectorXd D; + MatrixXd points, P, N; + random_points_on_mesh(num_samples, VX, FX, points); + point_mesh_distance(points, VY, FY, D, P, N); + if (method == ICP_METHOD_POINT_TO_POINT) { + point_to_point_rigid_matching(points, P, R, t); + } + else{ + point_to_plane_rigid_matching(points, P, N, R, t); + } } + + diff --git a/src/point_mesh_distance.cpp b/src/point_mesh_distance.cpp index 2e6a070..7daac1b 100644 --- a/src/point_mesh_distance.cpp +++ b/src/point_mesh_distance.cpp @@ -1,4 +1,9 @@ #include "point_mesh_distance.h" +#include "point_triangle_distance.h" +#include + +using namespace Eigen; +using namespace std; void point_mesh_distance( const Eigen::MatrixXd & X, @@ -8,9 +13,34 @@ void point_mesh_distance( 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 + +using namespace Eigen; void point_to_plane_rigid_matching( const Eigen::MatrixXd & X, @@ -7,7 +11,41 @@ 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 diag matrix + MatrixXd diag_matrix(X.rows(), 3 * X.rows()); + diag_matrix << MatrixXd(N.col(0).asDiagonal()), + MatrixXd(N.col(1).asDiagonal()), + MatrixXd(N.col(2).asDiagonal()); + + // construct b = X - P + VectorXd b(3 * N.rows()); + b << X.col(0) - P.col(0), + X.col(1) - P.col(1), + X.col(2) - P.col(2); + b = diag_matrix * b; + + // construct A + VectorXd zeros = VectorXd::Zero(N.rows()); + VectorXd ones = VectorXd::Ones(N.rows()); + MatrixXd A(3 * N.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; + A = diag_matrix * A; + + // compute u + VectorXd u = (A.transpose() * A).inverse() * (-A.transpose() * b); + + double alpha = u(0); + double beta = u(1); + double gamma = u(2); + + Matrix3d M; + M << 1, gamma, -beta, + -gamma, 1, alpha, + beta, -alpha, 1; + + closest_rotation(M, R); + t = RowVector3d(u(3),u(4),u(5)); + +} \ No newline at end of file diff --git a/src/point_to_point_rigid_matching.cpp b/src/point_to_point_rigid_matching.cpp index 079151f..ea8689d 100644 --- a/src/point_to_point_rigid_matching.cpp +++ b/src/point_to_point_rigid_matching.cpp @@ -1,5 +1,9 @@ #include "point_to_point_rigid_matching.h" +#include "closest_rotation.h" #include +#include + +using namespace Eigen; void point_to_point_rigid_matching( const Eigen::MatrixXd & X, @@ -7,8 +11,35 @@ void point_to_point_rigid_matching( Eigen::Matrix3d & R, Eigen::RowVector3d & t) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); - t = Eigen::RowVector3d::Zero(); + // construct b = X - P + VectorXd b(3 * X.rows()); + b << X.col(0) - P.col(0), + X.col(1) - P.col(1), + X.col(2) - P.col(2); + + // construct A + VectorXd zeros = VectorXd::Zero(X.rows()); + VectorXd ones = VectorXd::Ones(X.rows()); + 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; + + // compute u + VectorXd u = (A.transpose() * A).inverse() * (-A.transpose() * b); + + double alpha = u(0); + double beta = u(1); + double gamma = u(2); + + Matrix3d M; + M << 1, gamma, -beta, + -gamma, 1, alpha, + beta, -alpha, 1; + + closest_rotation(M, R); + t = RowVector3d(u(3),u(4),u(5)); + } + diff --git a/src/point_triangle_distance.cpp b/src/point_triangle_distance.cpp index 6405100..3b74eea 100644 --- a/src/point_triangle_distance.cpp +++ b/src/point_triangle_distance.cpp @@ -1,4 +1,9 @@ #include "point_triangle_distance.h" +#include +#include + +using namespace Eigen; +using namespace std; void point_triangle_distance( const Eigen::RowVector3d & x, @@ -8,7 +13,42 @@ void point_triangle_distance( double & d, Eigen::RowVector3d & p) { - // Replace with your code - d = 0; - p = a; -} + RowVector3d normal = (b - a).cross(c - b); + normal.normalize(); + + double area_ABC = normal.dot((b - a).cross(c - b)); + double area_PBC = normal.dot((b - p).cross(c - p)); + double area_PCA = normal.dot((c - p).cross(a - p)); + + double alpha = area_PBC * 1.0 / area_ABC; + double beta = area_PCA * 1.0 / area_ABC; + double gamma = 1.0 - alpha - beta; + + RowVector3d p0 = x - x.dot(normal) * normal; + + // reference: https://stackoverflow.com/questions/14467296/barycentric-coordinate-clamping-on-3d-triangle + Eigen::RowVector3d pc; + + if (gamma < 0) { + double t = (p0 - b).dot(a - b) / (a - b).dot(a - b); + t = max(0.0, min(t, 1.0)); + pc = RowVector3d(t, 1.0 - t, 0.0); + } + else if (beta < 0) { + double t = (p0 - c).dot(c - a) / (c - a).dot(c - a); + t = max(0.0, min(t, 1.0)); + pc = RowVector3d(1.0 - t, 0.0, t); + } + else if (alpha < 0) { + double t = (p0 - c).dot(b - c) / (b - c).dot(b - c); + t = max(0.0, min(t, 1.0)); + pc = RowVector3d(0.0, t, 1.0 - t); + } + else { + pc = RowVector3d(alpha, beta, gamma); + } + + p = pc[0] * a + pc[1] * b + pc[2] * c; + + d = (x - p).norm(); +} \ No newline at end of file diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp index 6e85d75..27c2db7 100644 --- a/src/random_points_on_mesh.cpp +++ b/src/random_points_on_mesh.cpp @@ -1,4 +1,11 @@ #include "random_points_on_mesh.h" +#include +#include +#include +#include + +using namespace Eigen; +using namespace std; void random_points_on_mesh( const int n, @@ -6,8 +13,58 @@ void random_points_on_mesh( const Eigen::MatrixXi & F, Eigen::MatrixXd & X) { - // REPLACE WITH YOUR CODE: X.resize(n,3); - for(int i = 0;i distribution(0.0, 1.0); + + int first, last, mid; + int tri_index; + + RowVector3d v1, v2, v3; + + for (int i = 0; i < X.rows(); i++) { + // randomly pick a triangle + double tri_sample = distribution(generator); + first = 0; + last = F.rows() - 1; + // binary search + while (last - first > 1) { + mid = (first + last) / 2; + if (tri_sample <= cumArea(mid)) { + last = mid; + } + else { + first = mid; + } + } + if (tri_sample <= cumArea(0)) { + tri_index = 0; + } + else { + tri_index = last; + } + + alpha = distribution(generator); + beta = distribution(generator); + + if (alpha + beta > 1) { + alpha = 1 - alpha; + beta = 1 - beta; + } + + v1 = V.row(F(tri_index, 0)); + v2 = V.row(F(tri_index, 1)); + v3 = V.row(F(tri_index, 2)); + + X.row(i) = v1 + alpha * (v2 - v1) + beta * (v3 - v1); + } + }