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..40040c8 100644 --- a/src/closest_rotation.cpp +++ b/src/closest_rotation.cpp @@ -1,9 +1,16 @@ #include "closest_rotation.h" +#include +#include void closest_rotation( - const Eigen::Matrix3d & M, - Eigen::Matrix3d & R) + const Eigen::Matrix3d & M, + Eigen::Matrix3d & R) { - // Replace with your code - R = Eigen::Matrix3d::Identity(); + Eigen::JacobiSVD svd(M, Eigen::ComputeFullU | Eigen::ComputeFullV); + Eigen::MatrixXd U = svd.matrixU(); + Eigen::MatrixXd V = svd.matrixV(); + Eigen::Matrix3d Omega(3, 3); + double det = (U * V.transpose()).determinant(); + Omega << 1, 0, 0, 0, 1, 0, 0, 0, det; + R = U * Omega * V.transpose(); } diff --git a/src/hausdorff_lower_bound.cpp b/src/hausdorff_lower_bound.cpp index 8608964..3536d7c 100644 --- a/src/hausdorff_lower_bound.cpp +++ b/src/hausdorff_lower_bound.cpp @@ -1,12 +1,22 @@ #include "hausdorff_lower_bound.h" +#include "random_points_on_mesh.h" +#include "point_mesh_distance.h" double hausdorff_lower_bound( - const Eigen::MatrixXd & VX, - const Eigen::MatrixXi & FX, - const Eigen::MatrixXd & VY, - const Eigen::MatrixXi & FY, - const int n) + const Eigen::MatrixXd & VX, + const Eigen::MatrixXi & FX, + const Eigen::MatrixXd & VY, + const Eigen::MatrixXi & FY, + const int n) { - // Replace with your code - return 0; + // random choose points + Eigen::MatrixXd X; + random_points_on_mesh(n, VX, FX, X); + + // compute point to mesh distance + Eigen::VectorXd D; + Eigen::MatrixXd P, N; + 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..8c565bb 100644 --- a/src/icp_single_iteration.cpp +++ b/src/icp_single_iteration.cpp @@ -1,16 +1,29 @@ #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; + random_points_on_mesh(num_samples, VX, FX, X); + + Eigen::VectorXd D; + Eigen::MatrixXd P, N; + 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 if (method == ICP_METHOD_POINT_TO_PLANE) { + 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..9841eab 100644 --- a/src/point_mesh_distance.cpp +++ b/src/point_mesh_distance.cpp @@ -1,16 +1,43 @@ #include "point_mesh_distance.h" +#include "point_triangle_distance.h" +#include 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 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(); + // compute [diag(N0), diag(N1), diag(N2)] + Eigen::MatrixXd normal(X.rows(), 3*X.rows()); + Eigen::MatrixXd N0 = N.col(0).asDiagonal(); + Eigen::MatrixXd N1 = N.col(1).asDiagonal(); + Eigen::MatrixXd N2 = N.col(2).asDiagonal(); + normal << N0, N1, N2; + // compute A + Eigen::RowVectorXd zero = Eigen::RowVectorXd::Zero(X.rows()); + Eigen::RowVectorXd ones = Eigen::RowVectorXd::Ones(X.rows()); + Eigen::MatrixXd A(3*X.rows(), 6); + A << zero, X.col(2), -X.col(1), ones, zero, zero, + -X.col(2), 0, X.col(0), zero, ones, zero, + X.col(1), -X.col(0), zero, zero, zero, ones; + // compute X - P + Eigen::MatrixXd xp(3*X.rows(), 3); + xp << X.col(0) - P.col(0), X.col(1) - P.col(1), X.col(2) - P.col(2); + // compute u + A = normal * A; + xp = normal * xp; + Eigen::RowVectorXd u; + // u = [alpha, beta, gamma, t0, t1, t2] + u = (A.transpose() * A).inverse() * -A.transpose() * xp; + // get t + t << u(3), u(4), u(5); + // compute M and R + Eigen::Matrix3d M; + M << 1, -u(2), u(1), u(2), 1, -u(0), -u(1), u(0), 1; + closest_rotation(M, R); } diff --git a/src/point_to_point_rigid_matching.cpp b/src/point_to_point_rigid_matching.cpp index 079151f..4523e72 100644 --- a/src/point_to_point_rigid_matching.cpp +++ b/src/point_to_point_rigid_matching.cpp @@ -1,14 +1,22 @@ #include "point_to_point_rigid_matching.h" -#include +#include "closest_rotation.h" 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(); + // closed form + Eigen::RowVector3d x = X.colwise().mean(); + Eigen::RowVector3d p = P.colwise().mean(); + Eigen::MatrixXd x_bar = X.rowwise() - x; + Eigen::MatrixXd p_bar = P.rowwise() - p; + + Eigen::Matrix3d M = p_bar.transpose() * x_bar; + closest_rotation(M, R); + + // p - (R * x^T)^T = p - x * R^T + t = p - x * R.transpose(); } diff --git a/src/point_triangle_distance.cpp b/src/point_triangle_distance.cpp index 6405100..72869a0 100644 --- a/src/point_triangle_distance.cpp +++ b/src/point_triangle_distance.cpp @@ -1,14 +1,49 @@ #include "point_triangle_distance.h" +#include 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; + // calculate normal vector + Eigen::RowVector3d normal = (a - b).cross(a - c); + normal.normalize(); + + // project x on to triangle plane + Eigen::RowVector3d px = (x - a).dot(normal) * normal + x; + + /* + 1 |6 / 3 + --A-C--- + 4 |/ 5 + B + /| + /2| */ + // if following value is positive, then px is in the triangle side of edge + double in_ab = (a - px).cross(a - b).dot(normal); + double in_bc = (b - px).cross(b - c).dot(normal); + double in_ca = (c - px).cross(c - a).dot(normal); + + // find p + if (in_ab >= 0 && in_bc >= 0 && in_ca >= 0) { + p = px; // inside + } else if (in_ab < 0 && !in_ca < 0) { + p = a; // case 1 + } else if (in_ab < 0 && in_bc < 0) { + p = b; // case 2 + } else if (in_bc < 0 && in_ca < 0) { + p = c; // case 3 + } else if (in_ab < 0) { + p = (a - px).dot(a - b) * (a - b) + a; // case 4 + } else if (in_bc < 0) { + p = (b - px).dot(b - c) * (b - c) + b; // case 5 + } else if (in_ca < 0) { + p = (c - px).dot(c - a) * (c - a) + c; // case 6 + } + + d = (x - p).norm(); } diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp index 6e85d75..e820282 100644 --- a/src/random_points_on_mesh.cpp +++ b/src/random_points_on_mesh.cpp @@ -1,13 +1,58 @@ #include "random_points_on_mesh.h" +#include +#include + + +int binary_find(Eigen::VectorXd lst, double val) { + int idx = 0; + if (lst.rows() <= 1) return idx; + + int mid = lst.rows()/2; + if (lst(mid) < val) { + idx = mid + binary_find(lst.segment(mid, lst.rows()), val); + } else { + idx = binary_find(lst.head(mid), val); + } + return idx; +} + void random_points_on_mesh( - const int n, - const Eigen::MatrixXd & V, - const Eigen::MatrixXi & F, - Eigen::MatrixXd & X) + const int n, + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + Eigen::MatrixXd & X) { - // REPLACE WITH YOUR CODE: - X.resize(n,3); - for(int i = 0;i dis(0.0, 1.0); + + X.resize(n, 3); + for (int i = 0; i < X.rows(); i++) { + // find random index idx + double gamma = dis(gen); + int idx = binary_find(csum, gamma); + + // generate and configurate alpha, beta + double alpha = dis(gen); + double beta = dis(gen); + if (alpha + beta > 1) { + alpha = 1 - alpha; + beta = 1 - beta; + } + + Eigen::VectorXd v1 = V.row(F(idx, 0)); + Eigen::VectorXd v2 = V.row(F(idx, 1)); + Eigen::VectorXd v3 = V.row(F(idx, 2)); + + X.row(i) = v1 + alpha * (v2 - v1) + beta * (v3 - v1); + } +}