use crate::{DistortionModel, Mat3, Pt2, Real, Vec3};
pub fn pixel_to_normalized(pixel: Pt2, intrinsics: &Mat3) -> Pt2 {
let k_inv = intrinsics
.try_inverse()
.expect("intrinsics matrix should be invertible");
let v = k_inv * Vec3::new(pixel.x, pixel.y, 1.0);
Pt2::new(v.x / v.z, v.y / v.z)
}
pub fn normalized_to_pixel(normalized: Pt2, intrinsics: &Mat3) -> Pt2 {
let v = intrinsics * Vec3::new(normalized.x, normalized.y, 1.0);
Pt2::new(v.x / v.z, v.y / v.z)
}
pub fn undistort_pixel<D: DistortionModel<Real>>(
pixel: Pt2,
intrinsics: &Mat3,
distortion: &D,
) -> Pt2 {
let normalized = pixel_to_normalized(pixel, intrinsics);
distortion.undistort(&normalized)
}
pub fn distort_to_pixel<D: DistortionModel<Real>>(
normalized: Pt2,
intrinsics: &Mat3,
distortion: &D,
) -> Pt2 {
let distorted = distortion.distort(&normalized);
normalized_to_pixel(distorted, intrinsics)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::BrownConrady5;
#[test]
fn pixel_normalized_roundtrip() {
let k = Mat3::new(800.0, 0.0, 640.0, 0.0, 780.0, 360.0, 0.0, 0.0, 1.0);
let pixel_orig = Pt2::new(700.0, 400.0);
let normalized = pixel_to_normalized(pixel_orig, &k);
let pixel_back = normalized_to_pixel(normalized, &k);
assert!((pixel_back.x - pixel_orig.x).abs() < 1e-10);
assert!((pixel_back.y - pixel_orig.y).abs() < 1e-10);
}
#[test]
fn principal_point_maps_to_origin() {
let k = Mat3::new(800.0, 0.0, 640.0, 0.0, 800.0, 480.0, 0.0, 0.0, 1.0);
let pixel = Pt2::new(640.0, 480.0);
let normalized = pixel_to_normalized(pixel, &k);
assert!(normalized.x.abs() < 1e-10);
assert!(normalized.y.abs() < 1e-10);
}
#[test]
fn undistort_pixel_no_distortion() {
let k = Mat3::new(800.0, 0.0, 640.0, 0.0, 800.0, 480.0, 0.0, 0.0, 1.0);
let distortion = BrownConrady5 {
k1: 0.0,
k2: 0.0,
k3: 0.0,
p1: 0.0,
p2: 0.0,
iters: 5,
};
let pixel = Pt2::new(700.0, 500.0);
let undist = undistort_pixel(pixel, &k, &distortion);
let expected = pixel_to_normalized(pixel, &k);
assert!((undist.x - expected.x).abs() < 1e-10);
assert!((undist.y - expected.y).abs() < 1e-10);
}
#[test]
fn distort_undistort_approximate_roundtrip() {
let k = Mat3::new(800.0, 0.0, 640.0, 0.0, 800.0, 480.0, 0.0, 0.0, 1.0);
let distortion = BrownConrady5 {
k1: -0.3,
k2: 0.1,
k3: 0.0,
p1: 0.001,
p2: -0.001,
iters: 5,
};
let normalized_orig = Pt2::new(-0.1, 0.05);
let pixel_dist = distort_to_pixel(normalized_orig, &k, &distortion);
let normalized_undist = undistort_pixel(pixel_dist, &k, &distortion);
let diff = (normalized_undist - normalized_orig).norm();
assert!(diff < 1e-6, "roundtrip error too large: {}", diff);
}
}