17 std::vector<Eigen::Vector2d>& out) -> Eigen::Matrix3d {
18 out.resize(pts.size());
20 const Eigen::Vector2d centroid =
21 std::accumulate(pts.begin(), pts.end(), Eigen::Vector2d{0, 0}) /
22 std::max<size_t>(1, pts.size());
23 const double mean_dist = std::accumulate(pts.begin(), pts.end(), 0.0,
24 [¢roid](
double sum,
const Eigen::Vector2d& p) {
25 return sum + (p - centroid).norm();
27 static_cast<double>(std::max<size_t>(1, pts.size()));
28 const double sigma = (mean_dist > 0) ? std::numbers::sqrt2 / mean_dist : 1.0;
30 Eigen::Matrix3d transform = Eigen::Matrix3d::Identity();
31 transform(0, 0) = sigma;
32 transform(1, 1) = sigma;
33 transform(0, 2) = -sigma * centroid.x();
34 transform(1, 2) = -sigma * centroid.y();
36 std::transform(pts.begin(), pts.end(), out.begin(),
37 [&transform](
const Eigen::Vector2d& pt) -> Eigen::Vector2d {
38 Eigen::Vector3d hp(pt.x(), pt.y(), 1.0);
39 Eigen::Vector3d hn = transform * hp;
40 return hn.hnormalized();
46 const std::vector<Eigen::Vector2d>& dst_uv)
49 const auto npts =
static_cast<Eigen::Index
>(src_xy.size());
50 Eigen::MatrixXd amtx(2 * npts, 9);
51 for (Eigen::Index i = 0; i < npts; ++i) {
52 const double xcoord = src_xy[i].x();
53 const double ycoord = src_xy[i].y();
54 const double ucoord = dst_uv[i].x();
55 const double vcoord = dst_uv[i].y();
57 amtx.row(2 * i) << -xcoord, -ycoord, -1, 0, 0, 0, ucoord * xcoord, ucoord * ycoord, ucoord;
58 amtx.row(2 * i + 1) << 0, 0, 0, -xcoord, -ycoord, -1, vcoord * xcoord, vcoord * ycoord,
62 Eigen::JacobiSVD<Eigen::MatrixXd> svd(amtx, Eigen::ComputeFullV);
63 Eigen::VectorXd hvec = svd.matrixV().col(8);
65 hmtx << hvec(0), hvec(1), hvec(2), hvec(3), hvec(4), hvec(5), hvec(6), hvec(7), hvec(8);
66 return hmtx / hmtx(2, 2);
81 const Eigen::Vector2d& uv) ->
double {
82 auto hmul = [](
const Eigen::Matrix3d& M,
const Eigen::Vector2d& p) -> Eigen::Vector2d {
83 Eigen::Vector3d hp(p.x(), p.y(), 1.0);
84 Eigen::Vector3d q = M * hp;
85 return Eigen::Vector2d{q.hnormalized()};
87 const Eigen::Matrix3d hmtxinv = hmtx.inverse();
88 const Eigen::Vector2d uv_hat = hmul(hmtx, xy);
89 const Eigen::Vector2d xy_hat = hmul(hmtxinv, uv);
90 const double e1 = (uv - uv_hat).norm();
91 const double e2 = (xy - xy_hat).norm();
92 return std::sqrt(0.5 * (e1 * e1 + e2 * e2));
100[[nodiscard]]
auto has_near_collinear_triplet(
const std::vector<Datum>& data,
124 -> std::optional<Model> {
125 if (sample.size() < k_min_samples) {
126 std::cout <<
"HomographyEstimator::fit: sample too small\n";
129 std::vector<Eigen::Vector2d> src;
130 std::vector<Eigen::Vector2d> dst;
131 src.reserve(sample.size());
132 dst.reserve(sample.size());
133 for (
int idx : sample) {
134 src.push_back(data[idx].object_xy);
135 dst.push_back(data[idx].image_uv);
138 if (!std::isfinite(hmtx(0, 0))) {
139 std::cout <<
"HomographyEstimator::fit: non-finite homography\n";
151 -> std::optional<Model> {
152 if (inliers.size() < k_min_samples) {
155 std::vector<Eigen::Vector2d> src;
156 std::vector<Eigen::Vector2d> dst;
157 src.reserve(inliers.size());
158 dst.reserve(inliers.size());
159 for (
int idx : inliers) {
160 src.push_back(data[idx].object_xy);
161 dst.push_back(data[idx].image_uv);
164 if (!std::isfinite(hmtx(0, 0))) {