Skip to main content

apple_vision/registration/
mod.rs

1#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
2#![allow(clippy::too_long_first_doc_paragraph)]
3//! `VNTranslationalImageRegistrationRequest` +
4//! `VNHomographicImageRegistrationRequest` — pixel-space alignment
5//! between two images.
6
7use std::ffi::{CStr, CString};
8use std::path::Path;
9use std::ptr;
10
11use crate::error::VisionError;
12use crate::ffi;
13
14/// 2D translation in source-image coordinates needed to align the
15/// floating image to the target.
16#[derive(Debug, Clone, Copy, PartialEq)]
17pub struct TranslationalAlignment {
18    pub tx: f64,
19    pub ty: f64,
20}
21
22/// 3×3 row-major homography matrix that warps the floating image
23/// into the target's coordinate system.
24#[derive(Debug, Clone, Copy, PartialEq)]
25pub struct HomographicAlignment {
26    pub matrix: [[f32; 3]; 3],
27}
28
29/// Compute a translational alignment from `floating` to `target`.
30///
31/// # Errors
32///
33/// Returns [`VisionError`] when either image fails to load or the
34/// Vision request errors.
35pub fn register_translational(
36    target: impl AsRef<Path>,
37    floating: impl AsRef<Path>,
38) -> Result<TranslationalAlignment, VisionError> {
39    let target_str = target.as_ref().to_str().ok_or_else(|| VisionError::InvalidArgument("non-UTF-8 target path".into()))?;
40    let tp = CString::new(target_str).map_err(|e| VisionError::InvalidArgument(format!("target path NUL byte: {e}")))?;
41    let floating_str = floating.as_ref().to_str().ok_or_else(|| VisionError::InvalidArgument("non-UTF-8 floating path".into()))?;
42    let fp = CString::new(floating_str).map_err(|e| VisionError::InvalidArgument(format!("floating path NUL byte: {e}")))?;
43    let mut out = ffi::TranslationalAlignmentRaw { tx: 0.0, ty: 0.0 };
44    let mut err: *mut std::ffi::c_char = ptr::null_mut();
45    let status = unsafe {
46        ffi::vn_register_translational_in_paths(tp.as_ptr(), fp.as_ptr(), &mut out, &mut err)
47    };
48    if status != ffi::status::OK {
49        let msg = unsafe { take_err(err) };
50        return Err(VisionError::RequestFailed(msg));
51    }
52    Ok(TranslationalAlignment {
53        tx: out.tx,
54        ty: out.ty,
55    })
56}
57
58/// Compute a homographic (perspective) alignment from `floating` to
59/// `target`.
60///
61/// # Errors
62///
63/// Returns [`VisionError`] when either image fails to load or the
64/// Vision request errors.
65pub fn register_homographic(
66    target: impl AsRef<Path>,
67    floating: impl AsRef<Path>,
68) -> Result<HomographicAlignment, VisionError> {
69    let target_str = target.as_ref().to_str().ok_or_else(|| VisionError::InvalidArgument("non-UTF-8 target path".into()))?;
70    let tp = CString::new(target_str).map_err(|e| VisionError::InvalidArgument(format!("target path NUL byte: {e}")))?;
71    let floating_str = floating.as_ref().to_str().ok_or_else(|| VisionError::InvalidArgument("non-UTF-8 floating path".into()))?;
72    let fp = CString::new(floating_str).map_err(|e| VisionError::InvalidArgument(format!("floating path NUL byte: {e}")))?;
73    let mut out = ffi::HomographicAlignmentRaw {
74        m00: 0.0,
75        m01: 0.0,
76        m02: 0.0,
77        m10: 0.0,
78        m11: 0.0,
79        m12: 0.0,
80        m20: 0.0,
81        m21: 0.0,
82        m22: 0.0,
83        _pad: 0.0,
84    };
85    let mut err: *mut std::ffi::c_char = ptr::null_mut();
86    let status = unsafe {
87        ffi::vn_register_homographic_in_paths(tp.as_ptr(), fp.as_ptr(), &mut out, &mut err)
88    };
89    if status != ffi::status::OK {
90        let msg = unsafe { take_err(err) };
91        return Err(VisionError::RequestFailed(msg));
92    }
93    Ok(HomographicAlignment {
94        matrix: [
95            [out.m00, out.m01, out.m02],
96            [out.m10, out.m11, out.m12],
97            [out.m20, out.m21, out.m22],
98        ],
99    })
100}
101
102unsafe fn take_err(p: *mut std::ffi::c_char) -> String {
103    if p.is_null() {
104        return String::new();
105    }
106    let s = unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned();
107    unsafe { libc::free(p.cast()) };
108    s
109}