projective-grid 0.14.0

Image-free, target-agnostic projective grid recovery: label 2D feature points with (i, j) lattice coordinates under perspective
Documentation
use std::collections::HashMap;

use nalgebra::{Matrix3, Point2};
use projective_grid::expert::geometry::estimate_homography;
use projective_grid::expert::lattice::{
    predict_grid_position, GridTransform, D4_TRANSFORMS, D6_TRANSFORMS,
};
use projective_grid::{
    Coord, CoordinateHypothesis, GridDimensions, LatticeKind, LocalAxis, OrientedFeature,
    PointFeature,
};

#[test]
fn feature_evidence_types_are_constructible() {
    let point = PointFeature::new(12, Point2::new(10.0_f32, 20.0));
    let axis0 = LocalAxis::new(0.0, Some(0.05));
    let axis1 = LocalAxis::new(std::f32::consts::FRAC_PI_2, Some(0.05));
    let axis2 = LocalAxis::new(std::f32::consts::PI / 3.0, None);

    let one = OrientedFeature::<1>::new(point, [axis0]);
    let two = OrientedFeature::<2>::new(point, [axis0, axis1]);
    let three = OrientedFeature::<3>::new(point, [axis0, axis1, axis2]);
    let hypothesis = CoordinateHypothesis::new(12, Coord::new(3, 4), Some(0.8));

    assert_eq!(one.axes.len(), 1);
    assert_eq!(two.axes.len(), 2);
    assert_eq!(three.axes.len(), 3);
    assert_eq!(hypothesis.source_index, point.source_index);
    assert_eq!(hypothesis.coord, Coord::new(3, 4));
}

#[test]
fn dimensions_and_model_mapping_are_explicit() {
    let dims = GridDimensions::new(9, 6);
    assert_eq!(dims.width, 9);
    assert_eq!(dims.height, 6);

    let square = LatticeKind::Square.model_point(Coord::new(2, 5));
    assert_eq!(square, Point2::new(2.0, 5.0));

    let hex = LatticeKind::Hex.model_point(Coord::new(1, 2));
    assert!((hex.x - 2.0).abs() < 1e-5);
    assert!((hex.y - 3.0_f32.sqrt()).abs() < 1e-5);
}

#[test]
fn symmetry_tables_stay_lattice_tagged() {
    assert_eq!(D4_TRANSFORMS.len(), 8);
    assert_eq!(D6_TRANSFORMS.len(), 12);
    assert!(D4_TRANSFORMS
        .iter()
        .all(|t| t.lattice() == LatticeKind::Square));
    assert!(D6_TRANSFORMS
        .iter()
        .all(|t| t.lattice() == LatticeKind::Hex));
}

#[test]
fn detector_builder_primitives_stay_on_expert_surface() {
    let transform = GridTransform::identity(LatticeKind::Square).with_translation([4, -2]);
    assert_eq!(transform.apply(Coord::new(3, 5)), Coord::new(7, 3));

    let mut labelled = HashMap::new();
    labelled.insert(Coord::new(-1, 0), Point2::new(-2.0_f32, 1.0));
    labelled.insert(Coord::new(1, 0), Point2::new(2.0_f32, 1.0));
    let predicted = predict_grid_position(&labelled, Coord::new(0, 0), LatticeKind::Square)
        .expect("opposite pair predicts the center");
    assert_eq!(predicted.position, Point2::new(0.0, 1.0));

    let source = [
        Point2::new(0.0_f32, 0.0),
        Point2::new(1.0, 0.0),
        Point2::new(1.0, 1.0),
        Point2::new(0.0, 1.0),
    ];
    let expected = Matrix3::new(2.0, 0.0, 3.0, 0.0, 2.0, 5.0, 0.0, 0.0, 1.0);
    let destination = source.map(|point| {
        let homogeneous = expected * point.to_homogeneous();
        Point2::new(homogeneous.x / homogeneous.z, homogeneous.y / homogeneous.z)
    });
    let estimated = estimate_homography(&source, &destination).expect("four-point homography");
    assert!((estimated.apply(Point2::new(0.5, 0.5)) - Point2::new(4.0, 6.0)).norm() < 1e-5);
}