Skip to main content

apple_vision/person_instance_mask/
mod.rs

1#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
2#![allow(clippy::too_long_first_doc_paragraph)]
3//! `VNGeneratePersonInstanceMaskRequest` — per-person instance mask
4//! (macOS 14+).
5
6use std::ffi::{CStr, CString};
7use std::path::Path;
8use std::ptr;
9
10use crate::error::VisionError;
11use crate::ffi;
12
13/// A returned 8-bit grayscale mask.
14pub struct PersonInstanceMask {
15    pub width: usize,
16    pub height: usize,
17    pub bytes_per_row: usize,
18    data: *mut u8,
19}
20
21impl PersonInstanceMask {
22    /// Row-major byte view into the mask buffer.
23    #[must_use]
24    pub const fn as_bytes(&self) -> &[u8] {
25        unsafe { core::slice::from_raw_parts(self.data, self.bytes_per_row * self.height) }
26    }
27}
28
29impl Drop for PersonInstanceMask {
30    fn drop(&mut self) {
31        if !self.data.is_null() {
32            let size = (self.bytes_per_row * self.height) as isize;
33            unsafe { ffi::vn_mask_buffer_free(self.data, size) };
34            self.data = ptr::null_mut();
35        }
36    }
37}
38
39unsafe impl Send for PersonInstanceMask {}
40unsafe impl Sync for PersonInstanceMask {}
41
42/// Generate a person-instance mask for the image at `path`.
43///
44/// # Errors
45///
46/// Returns [`VisionError`] when the image fails to load or the
47/// Vision request errors.
48pub fn person_instance_mask(
49    path: impl AsRef<Path>,
50) -> Result<Option<PersonInstanceMask>, VisionError> {
51    let path_str = path.as_ref().to_str().ok_or_else(|| VisionError::InvalidArgument("non-UTF-8 path".into()))?;
52    let cpath = CString::new(path_str).map_err(|e| VisionError::InvalidArgument(format!("path NUL byte: {e}")))?;
53    let mut w: isize = 0;
54    let mut h: isize = 0;
55    let mut bpr: isize = 0;
56    let mut data: *mut u8 = ptr::null_mut();
57    let mut err: *mut std::ffi::c_char = ptr::null_mut();
58    let status = unsafe {
59        ffi::vn_person_instance_mask_in_path(
60            cpath.as_ptr(),
61            &mut w,
62            &mut h,
63            &mut bpr,
64            &mut data,
65            &mut err,
66        )
67    };
68    if status != ffi::status::OK {
69        let msg = unsafe { take_err(err) };
70        return Err(VisionError::RequestFailed(msg));
71    }
72    if data.is_null() {
73        return Ok(None);
74    }
75    Ok(Some(PersonInstanceMask {
76        width: w.max(0) as usize,
77        height: h.max(0) as usize,
78        bytes_per_row: bpr.max(0) as usize,
79        data,
80    }))
81}
82
83unsafe fn take_err(p: *mut std::ffi::c_char) -> String {
84    if p.is_null() {
85        return String::new();
86    }
87    let s = unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned();
88    unsafe { libc::free(p.cast()) };
89    s
90}