[READ-ONLY] Mirror of https://github.com/mrgnw/spatial-maker.
0

Configure Feed

Select the types of activity you want to include in your feed.

spatial-maker / src / depth_coreml.rs
3.4 kB 131 lines
1use crate::error::{SpatialError, SpatialResult}; 2use image::{DynamicImage, ImageBuffer, Luma}; 3use ndarray::Array2; 4use std::ffi::CString; 5 6const INPUT_SIZE: u32 = 518; 7const IMAGENET_MEAN: [f32; 3] = [0.485, 0.456, 0.406]; 8const IMAGENET_STD: [f32; 3] = [0.229, 0.224, 0.225]; 9 10extern "C" { 11 fn coreml_load_model(path: *const std::os::raw::c_char) -> *mut std::os::raw::c_void; 12 fn coreml_unload_model(model: *mut std::os::raw::c_void); 13 fn coreml_infer_depth( 14 model: *mut std::os::raw::c_void, 15 rgb_data: *const f32, 16 width: i32, 17 height: i32, 18 output: *mut f32, 19 ) -> i32; 20} 21 22pub struct CoreMLDepthEstimator { 23 model: *mut std::os::raw::c_void, 24} 25 26impl CoreMLDepthEstimator { 27 pub fn new(model_path: &str) -> SpatialResult<Self> { 28 let c_path = CString::new(model_path) 29 .map_err(|e| SpatialError::ModelError(format!("Invalid model path: {}", e)))?; 30 31 let model = unsafe { coreml_load_model(c_path.as_ptr()) }; 32 33 if model.is_null() { 34 return Err(SpatialError::ModelError(format!( 35 "Failed to load CoreML model: {}", 36 model_path 37 ))); 38 } 39 40 tracing::info!("CoreML model loaded: {}", model_path); 41 42 Ok(Self { model }) 43 } 44 45 pub fn estimate_raw(&self, image: &DynamicImage) -> SpatialResult<ImageBuffer<Luma<f32>, Vec<f32>>> { 46 let (orig_width, orig_height) = (image.width(), image.height()); 47 48 let resized = image.resize_exact( 49 INPUT_SIZE, 50 INPUT_SIZE, 51 image::imageops::FilterType::Lanczos3, 52 ); 53 54 let rgb = resized.to_rgb8(); 55 let mut input_data = Vec::with_capacity(3 * INPUT_SIZE as usize * INPUT_SIZE as usize); 56 57 for c in 0..3 { 58 for pixel in rgb.pixels() { 59 let normalized = (pixel[c] as f32 / 255.0 - IMAGENET_MEAN[c]) / IMAGENET_STD[c]; 60 input_data.push(normalized); 61 } 62 } 63 64 let output_size = (INPUT_SIZE * INPUT_SIZE) as usize; 65 let mut output_data = vec![0.0f32; output_size]; 66 67 let result = unsafe { 68 coreml_infer_depth( 69 self.model, 70 input_data.as_ptr(), 71 INPUT_SIZE as i32, 72 INPUT_SIZE as i32, 73 output_data.as_mut_ptr(), 74 ) 75 }; 76 77 if result != 0 { 78 return Err(SpatialError::ModelError(format!( 79 "CoreML inference failed with error code: {}", 80 result 81 ))); 82 } 83 84 let min_val = output_data.iter().copied().fold(f32::INFINITY, f32::min); 85 let max_val = output_data 86 .iter() 87 .copied() 88 .fold(f32::NEG_INFINITY, f32::max); 89 let range = max_val - min_val; 90 91 if range > 1e-6 { 92 for v in &mut output_data { 93 *v = (*v - min_val) / range; 94 } 95 } 96 97 let depth_image = ImageBuffer::from_fn(INPUT_SIZE, INPUT_SIZE, |x, y| { 98 let idx = (y * INPUT_SIZE + x) as usize; 99 Luma([output_data[idx]]) 100 }); 101 102 let resized_depth = image::imageops::resize( 103 &depth_image, 104 orig_width, 105 orig_height, 106 image::imageops::FilterType::Lanczos3, 107 ); 108 109 Ok(resized_depth) 110 } 111 112 pub fn estimate(&self, image: &DynamicImage) -> SpatialResult<Array2<f32>> { 113 let depth_image = self.estimate_raw(image)?; 114 let (width, height) = depth_image.dimensions(); 115 let data: Vec<f32> = depth_image.pixels().map(|p| p[0]).collect(); 116 let depth_2d = Array2::from_shape_vec((height as usize, width as usize), data) 117 .map_err(|e| SpatialError::TensorError(format!("Failed to reshape depth: {}", e)))?; 118 Ok(depth_2d) 119 } 120} 121 122impl Drop for CoreMLDepthEstimator { 123 fn drop(&mut self) { 124 unsafe { 125 coreml_unload_model(self.model); 126 } 127 } 128} 129 130unsafe impl Send for CoreMLDepthEstimator {} 131unsafe impl Sync for CoreMLDepthEstimator {}