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..75b24ff 100644
--- a/src/closest_rotation.cpp
+++ b/src/closest_rotation.cpp
@@ -1,9 +1,26 @@
#include "closest_rotation.h"
+#include
+#include
void closest_rotation(
const Eigen::Matrix3d & M,
Eigen::Matrix3d & R)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
+ // SVD inspired by Eigen::JacobiSVD library
+ Eigen::JacobiSVD svd(M,
+ Eigen::ComputeFullU | Eigen::ComputeFullV);
+
+ Eigen::Matrix3d U = svd.matrixU();
+ Eigen::Matrix3d V = svd.matrixV();
+
+ double det = (U * V.transpose()).determinant();
+
+ Eigen::Matrix3d mid;
+ mid << 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, det;
+
+ // As ISSUE #38 poited out, should times Vx by R.T
+ // Fix here since I cannot change main.cpp
+ R = (U * mid * V.transpose()).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..3b9ad88 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_plane_rigid_matching.h"
+#include "point_to_point_rigid_matching.h"
void icp_single_iteration(
const Eigen::MatrixXd & VX,
@@ -10,7 +14,16 @@ void icp_single_iteration(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ Eigen::MatrixXd X, P, N;
+ random_points_on_mesh(num_samples, VX, FX, X);
+ Eigen::VectorXd D;
+ point_mesh_distance(X, VY, FY, D, P, N);
+ if (method == ICP_METHOD_POINT_TO_PLANE) {
+ // To Plane
+ point_to_plane_rigid_matching(X, P, N, R, t);
+ return;
+ }
+
+ // To Point
+ point_to_point_rigid_matching(X, P, R, t);
}
diff --git a/src/point_mesh_distance.cpp b/src/point_mesh_distance.cpp
index 2e6a070..88ca97d 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
void point_mesh_distance(
const Eigen::MatrixXd & X,
@@ -8,9 +10,40 @@ void point_mesh_distance(
Eigen::MatrixXd & P,
Eigen::MatrixXd & N)
{
- // Replace with your code
+ D.resize(X.rows());
P.resizeLike(X);
- N = Eigen::MatrixXd::Zero(X.rows(),X.cols());
- for(int i = 0;i::max();
+ Eigen::RowVector3d projection;
+ Eigen::RowVector3d normal;
+
+ // Loop over faces to find the closest point
+ for(int j = 0; j < FY.rows(); j ++) {
+ Eigen::RowVector3d a = VY.row(FY(j, 0));
+ Eigen::RowVector3d b = VY.row(FY(j, 1));
+ Eigen::RowVector3d c = VY.row(FY(j, 2));
+ double cur_min;
+ Eigen::RowVector3d cur_p;
+ point_triangle_distance(x, a, b, c, cur_min, cur_p);
+ if (min > cur_min) {
+ min = cur_min;
+ projection = cur_p;
+ normal = All_normal.row(j);
+ }
+ }
+ // Able to add to the result
+ D(i) = min;
+ P.row(i) = projection;
+ N.row(i) = normal;
+ }
}
diff --git a/src/point_to_plane_rigid_matching.cpp b/src/point_to_plane_rigid_matching.cpp
index 1978510..cec7e2f 100644
--- a/src/point_to_plane_rigid_matching.cpp
+++ b/src/point_to_plane_rigid_matching.cpp
@@ -1,4 +1,6 @@
#include "point_to_plane_rigid_matching.h"
+#include "closest_rotation.h"
+#include
void point_to_plane_rigid_matching(
const Eigen::MatrixXd & X,
@@ -7,7 +9,48 @@ void point_to_plane_rigid_matching(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ // Fetch some useful variables
+ int rows = X.rows();
+ Eigen::VectorXd X1 = X.col(0);
+ Eigen::VectorXd X2 = X.col(1);
+ Eigen::VectorXd X3 = X.col(2);
+ Eigen::VectorXd diff1 = X1 - P.col(0);
+ Eigen::VectorXd diff2 = X2 - P.col(1);
+ Eigen::VectorXd diff3 = X3 - P.col(2);
+ Eigen::MatrixXd N1 = N.col(0).asDiagonal();
+ Eigen::MatrixXd N2 = N.col(1).asDiagonal();
+ Eigen::MatrixXd N3 = N.col(2).asDiagonal();
+ Eigen::VectorXd zero = Eigen::VectorXd::Zero(rows);
+ Eigen::VectorXd one = Eigen::VectorXd::Ones(rows);
+
+ // Construct Matrix A and right hand side value:
+ Eigen::MatrixXd left_N(rows, 3*rows);
+ left_N << N1, N2, N3;
+
+ Eigen::MatrixXd A(3*rows, 6);
+ A << zero, X3, -X2, one, zero, zero,
+ -X3, zero, X1, zero, one, zero,
+ X2, -X1, zero, zero, zero, one;
+ Eigen::MatrixXd new_A = left_N * A;
+
+ Eigen::VectorXd rhs(3*rows);
+ rhs << diff1,
+ diff2,
+ diff3;
+ Eigen::VectorXd new_rhs = left_N * rhs;
+
+ // Compute u:
+ Eigen::MatrixXd AAT = new_A.transpose()*new_A;
+ new_rhs = -1 * (new_A.transpose()*new_rhs);
+ Eigen::VectorXd u = AAT.colPivHouseholderQr().solve(new_rhs);
+
+ // Construct M:
+ Eigen::Matrix3d M;
+ M << 1, -u(2), u(1),
+ u(2), 1, -u(0),
+ -u(1), u(0), 1;
+
+ // Calculate R and t:
+ 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..28b3850 100644
--- a/src/point_to_point_rigid_matching.cpp
+++ b/src/point_to_point_rigid_matching.cpp
@@ -1,5 +1,7 @@
#include "point_to_point_rigid_matching.h"
#include
+#include "closest_rotation.h"
+#include
void point_to_point_rigid_matching(
const Eigen::MatrixXd & X,
@@ -7,8 +9,41 @@ void point_to_point_rigid_matching(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ // Fetch some useful variables
+ int rows = X.rows();
+ Eigen::VectorXd X1 = X.col(0);
+ Eigen::VectorXd X2 = X.col(1);
+ Eigen::VectorXd X3 = X.col(2);
+ Eigen::VectorXd diff1 = X1 - P.col(0);
+ Eigen::VectorXd diff2 = X2 - P.col(1);
+ Eigen::VectorXd diff3 = X3 - P.col(2);
+ Eigen::VectorXd zero = Eigen::VectorXd::Zero(rows);
+ Eigen::VectorXd one = Eigen::VectorXd::Ones(rows);
+
+ // Construct Matrix A and right hand side value:
+ Eigen::MatrixXd A(3*rows, 6);
+ A << zero, X3, -X2, one, zero, zero,
+ -X3, zero, X1, zero, one, zero,
+ X2, -X1, zero, zero, zero, one;
+
+ Eigen::VectorXd rhs(3*rows);
+ rhs << diff1,
+ diff2,
+ diff3;
+
+ // Compute u:
+ Eigen::MatrixXd AAT = A.transpose()*A;
+ rhs = -1 * (A.transpose()*rhs);
+ Eigen::VectorXd u = AAT.colPivHouseholderQr().solve(rhs);
+
+ // Construct M:
+ Eigen::Matrix3d M;
+ M << 1, -u(2), u(1),
+ u(2), 1, -u(0),
+ -u(1), u(0), 1;
+
+ // Calculate R and t:
+ 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..3f11083 100644
--- a/src/point_triangle_distance.cpp
+++ b/src/point_triangle_distance.cpp
@@ -8,7 +8,37 @@ void point_triangle_distance(
double & d,
Eigen::RowVector3d & p)
{
- // Replace with your code
- d = 0;
- p = a;
+ Eigen::RowVector3d edge_ba = b - a;
+ Eigen::RowVector3d edge_ca = c - a;
+ Eigen::RowVector3d edge_xa = x - a;
+
+ // Find the normal vector and projection
+ Eigen::RowVector3d norm_vec = (edge_ba.cross(edge_ca)).normalized();
+ Eigen::RowVector3d px = x - (norm_vec.dot(edge_xa))*norm_vec;
+
+ // Use Barycentric algorithm to compute distance:
+ // Idea inspired by https://gamedev.stackexchange.com/questions/23743/whats-the-most-efficient-way-to-find-barycentric-coordinates
+ Eigen::RowVector3d edge_pxa = px - a;
+ double edge_ba_norm = edge_ba.dot(edge_ba);
+ double edge_ca_norm = edge_ca.dot(edge_ca);
+ double edge_ba_ca = edge_ba.dot(edge_ca);
+ double edge_ba_pxa = edge_ba.dot(edge_pxa);
+ double edge_ca_pxa = edge_ca.dot(edge_pxa);
+
+ double denominator = edge_ba_norm*edge_ca_norm - edge_ba_ca*edge_ba_ca;
+
+ double c2 = (edge_ca_norm*edge_ba_pxa - edge_ba_ca*edge_ca_pxa) / denominator;
+ double c3 = (edge_ba_norm*edge_ca_pxa - edge_ba_ca*edge_ba_pxa) / denominator;
+ double c1 = 1 - c2 - c3;
+
+ // Handle if not inside the triangle by clamping these coordinates:
+ c1 = c1 < 0 ? 0 : c1;
+ c1 = c1 > 1 ? 1 : c1;
+ c2 = c2 < 0 ? 0 : c2;
+ c2 = c2 > 1 ? 1 : c2;
+ c3 = 1 - c1 - c2;
+
+ // Find value:
+ p = c1 * a + c2 * b + c3 * c;
+ d = (p - x).norm();
}
diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp
index 6e85d75..9e90df7 100644
--- a/src/random_points_on_mesh.cpp
+++ b/src/random_points_on_mesh.cpp
@@ -1,4 +1,37 @@
#include "random_points_on_mesh.h"
+#include
+#include
+#include
+#include
+
+int find_index(Eigen::MatrixXd &C, double r) {
+ // Handle edge case:
+ if (r < C(0)) {
+ return 0;
+ }
+
+ // Binary Search:
+ int begin = 0;
+ int end = C.rows() - 1;
+ int mid = int((end + begin) / 2);
+
+ while(begin + 1 < end) {
+ double cur_mid = C(mid);
+ if (cur_mid == r) {
+ return mid + 1;
+ }
+ else if (cur_mid > r){
+ if (C(mid-1) uniform_distribution(0.0, 1.0);
+
+ for(int i = 0; i < X.rows(); i ++) {
+ // Find x:
+ double threshold = uniform_distribution(random_generator);
+ int ind = find_index(cum_area, threshold);
+ Eigen::RowVectorXd v0 = V.row(F(ind, 0));
+ Eigen::RowVectorXd v1 = V.row(F(ind, 1));
+ Eigen::RowVectorXd v2 = V.row(F(ind, 2));
+
+ double alpha = uniform_distribution(random_generator);
+ double beta = uniform_distribution(random_generator);
+ if (alpha + beta > 1) {
+ alpha = 1 - alpha;
+ beta = 1 - beta;
+ }
+
+ X.row(i) = v0 + alpha*(v1 - v0) + beta*(v2 - v0);
+ }
}