Merge PR #985
This commit is contained in:
commit
e9892a6f9d
|
|
@ -51,11 +51,11 @@ impl Homography {
|
|||
let c01 = m[5] * m[6] - m[3] * m[8];
|
||||
let c02 = m[3] * m[7] - m[4] * m[6];
|
||||
let det = m[0] * c00 + m[1] * c01 + m[2] * c02;
|
||||
if det.abs() < 1e-300 {
|
||||
if !det.is_finite() || det.abs() < 1e-300 {
|
||||
return None;
|
||||
}
|
||||
let inv = 1.0 / det;
|
||||
Some(Homography([
|
||||
let coefficients = [
|
||||
c00 * inv,
|
||||
(m[2] * m[7] - m[1] * m[8]) * inv,
|
||||
(m[1] * m[5] - m[2] * m[4]) * inv,
|
||||
|
|
@ -65,11 +65,17 @@ impl Homography {
|
|||
c02 * inv,
|
||||
(m[1] * m[6] - m[0] * m[7]) * inv,
|
||||
(m[0] * m[4] - m[1] * m[3]) * inv,
|
||||
]))
|
||||
];
|
||||
coefficients.iter().all(|v| v.is_finite()).then_some(Homography(coefficients))
|
||||
}
|
||||
|
||||
/// The homography mapping `src[i]` → `dst[i]` (4 correspondences, DLT with h33 = 1).
|
||||
pub fn from_quads(src: &[Point; 4], dst: &[Point; 4]) -> Option<Homography> {
|
||||
// Failed feature matches and invalid perspective parameters can contain
|
||||
// non-finite coordinates. Do not return a transform that poisons rendering.
|
||||
if src.iter().chain(dst.iter()).any(|p| !p.x.is_finite() || !p.y.is_finite()) {
|
||||
return None;
|
||||
}
|
||||
let mut a = [[0.0f64; 9]; 8];
|
||||
for i in 0..4 {
|
||||
let (x, y) = (src[i].x, src[i].y);
|
||||
|
|
@ -80,7 +86,7 @@ impl Homography {
|
|||
// Gaussian elimination with partial pivoting on the 8×8 system (last column = rhs).
|
||||
for col in 0..8 {
|
||||
let piv = (col..8).max_by(|&i, &j| a[i][col].abs().total_cmp(&a[j][col].abs()))?;
|
||||
if a[piv][col].abs() < 1e-12 {
|
||||
if !a[piv][col].is_finite() || a[piv][col].abs() < 1e-12 {
|
||||
return None;
|
||||
}
|
||||
a.swap(col, piv);
|
||||
|
|
@ -94,7 +100,7 @@ impl Homography {
|
|||
}
|
||||
}
|
||||
let h: Vec<f64> = (0..8).map(|i| a[i][8] / a[i][i]).collect();
|
||||
Some(Homography([h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], 1.0]))
|
||||
h.iter().all(|v| v.is_finite()).then_some(Homography([h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], 1.0]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,4 +127,19 @@ mod tests {
|
|||
let src = [Point::new(0.0, 0.0); 4];
|
||||
assert!(Homography::from_quads(&src, &src).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonfinite_projective_and_affine_transforms_are_rejected() {
|
||||
let src = [Point::new(0.0, 0.0), Point::new(1.0, 0.0), Point::new(1.0, 1.0), Point::new(0.0, 1.0)];
|
||||
let mut invalid = src;
|
||||
invalid[2].x = f64::NAN;
|
||||
assert!(Homography::from_quads(&invalid, &src).is_none());
|
||||
invalid[2].x = f64::INFINITY;
|
||||
assert!(Homography::from_quads(&src, &invalid).is_none());
|
||||
assert!(Homography([f64::NAN, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]).inverse().is_none());
|
||||
assert!(Homography([f64::INFINITY, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]).inverse().is_none());
|
||||
assert!(Affine([f64::NAN, 0.0, 0.0, 1.0, 0.0, 0.0]).inverse().is_none());
|
||||
assert!(Affine([f64::INFINITY, 0.0, 0.0, 1.0, 0.0, 0.0]).inverse().is_none());
|
||||
assert_eq!(Affine::IDENTITY.inverse(), Some(Affine::IDENTITY));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,12 +234,13 @@ impl Affine {
|
|||
}
|
||||
pub fn inverse(&self) -> Option<Affine> {
|
||||
let det = self.determinant();
|
||||
if det.abs() < 1e-300 {
|
||||
if !det.is_finite() || det.abs() < 1e-300 {
|
||||
return None;
|
||||
}
|
||||
let [a, b, c, d, e, f] = self.0;
|
||||
let inv = 1.0 / det;
|
||||
Some(Affine([d * inv, -b * inv, -c * inv, a * inv, (c * f - d * e) * inv, (b * e - a * f) * inv]))
|
||||
let coefficients = [d * inv, -b * inv, -c * inv, a * inv, (c * f - d * e) * inv, (b * e - a * f) * inv];
|
||||
coefficients.iter().all(|v| v.is_finite()).then_some(Affine(coefficients))
|
||||
}
|
||||
/// Bounding box of a transformed rectangle.
|
||||
pub fn transform_rect_bbox(&self, r: &Rect) -> Rect {
|
||||
|
|
|
|||
Loading…
Reference in New Issue