kinect_v2/
depth_capture.rs

1use std::sync::Arc;
2
3use kinect_v2_sys::{
4    DEFAULT_FRAME_WAIT_TIMEOUT_MS, WAITABLE_HANDLE,
5    depth::{DepthFrame, DepthFrameReader},
6    kinect::{self, KinectSensor},
7};
8use windows::Win32::Foundation::{E_FAIL, WAIT_OBJECT_0, WAIT_TIMEOUT};
9use windows::Win32::System::Threading::WaitForSingleObject;
10use windows::{Win32::Foundation::WAIT_EVENT, core::Error};
11
12/// Manages the Kinect sensor and provides access to depth frame data.
13///
14/// This struct is responsible for initializing and holding the necessary Kinect
15/// resources to capture depth frames.
16pub struct DepthFrameCapture {
17    kinect: KinectSensor, // keep the kinect sensor instance alive.
18}
19
20impl DepthFrameCapture {
21    /// Creates a new `DepthFrameCapture` instance.
22    ///
23    /// This function initializes the default Kinect sensor, opens it,
24    /// and sets up the depth frame source and reader.
25    ///
26    /// # Errors
27    ///
28    /// Returns an error if the Kinect sensor cannot be initialized,
29    /// opened, or if the depth frame source is not active.
30    pub fn new() -> Result<Self, Error> {
31        let kinect = kinect::get_default_kinect_sensor()?;
32        kinect.open()?;
33
34        Ok(DepthFrameCapture { kinect })
35    }
36    /// Returns an iterator over depth frames.
37    ///
38    /// The iterator will block waiting for new frames. Each item yielded by
39    /// the iterator is a `Result<DepthFrameData, Error>`, allowing for error
40    /// handling during frame acquisition.
41    ///
42    /// # Errors
43    ///
44    /// Returns an error if it fails to subscribe to the frame arrived event,
45    /// which is necessary for the iterator to function.
46    pub fn iter(&self) -> Result<DepthFrameCaptureIter, Error> {
47        let source = self.kinect.depth_frame_source()?;
48        // Open the reader to activate the source.
49        let reader = source.open_reader()?;
50        // Ensure the depth frame source is active.
51        // If not, event subscription and frame acquisition might fail.
52        if !source.get_is_active()? {
53            log::warn!(
54                "Depth frame source is not active, cannot subscribe to frame arrived event."
55            );
56            return Err(Error::from_hresult(E_FAIL));
57        }
58
59        let waitable_handle = reader.subscribe_frame_arrived()?;
60        Ok(DepthFrameCaptureIter {
61            reader,
62            waitable_handle,
63            timeout_ms: DEFAULT_FRAME_WAIT_TIMEOUT_MS,
64        })
65    }
66}
67
68/// An iterator that yields depth frames from a Kinect sensor.
69///
70/// This iterator blocks until a new frame is available or an error occurs.
71/// It is created by calling the `iter` method on `DepthFrameCapture`.
72pub struct DepthFrameCaptureIter {
73    reader: DepthFrameReader,
74    waitable_handle: WAITABLE_HANDLE,
75    timeout_ms: u32,
76}
77
78impl Drop for DepthFrameCaptureIter {
79    fn drop(&mut self) {
80        // Best effort to unsubscribe from the frame arrived event.
81        // Errors in `drop` are typically logged or ignored, as panicking in drop is problematic.
82        if let Err(e) = self.reader.unsubscribe_frame_arrived(self.waitable_handle) {
83            log::warn!("Failed to unsubscribe depth frame arrived event: {e:?}");
84        }
85    }
86}
87
88impl Iterator for DepthFrameCaptureIter {
89    type Item = Result<DepthFrameData, Error>;
90
91    fn next(&mut self) -> Option<Self::Item> {
92        loop {
93            let wait_status: WAIT_EVENT =
94                unsafe { WaitForSingleObject(self.waitable_handle, self.timeout_ms) };
95
96            if wait_status == WAIT_OBJECT_0 {
97                // Frame event was signaled.
98                // Use a closure and the `?` operator for cleaner error handling.
99                let result = (|| {
100                    let event_args = self
101                        .reader
102                        .get_frame_arrived_event_data(self.waitable_handle)?;
103                    let frame_reference = event_args.get_frame_reference()?;
104                    let depth_frame = frame_reference.acquire_frame()?;
105                    DepthFrameData::new(&depth_frame)
106                })(); // Immediately invoke the closure
107                return Some(result);
108            } else if wait_status == WAIT_TIMEOUT {
109                // No new frame arrived within the timeout period.
110                // Continue waiting as this is a blocking iterator.
111                continue;
112            } else {
113                return Some(Err(Error::from_hresult(E_FAIL)));
114            }
115        }
116    }
117}
118
119#[derive(Debug, Clone)]
120pub struct DepthFrameData {
121    pub width: u32,
122    pub height: u32,
123    pub timestamp: u64,
124    pub depth_min_reliable_distance: u16,
125    pub depth_max_reliable_distance: u16,
126    pub data: Arc<[u16]>, // Depth data is typically 16-bit unsigned integers
127}
128
129impl DepthFrameData {
130    pub fn new(depth_frame: &DepthFrame) -> Result<Self, Error> {
131        let frame_description = depth_frame.get_frame_description()?;
132        let width = frame_description.get_width()? as u32;
133        let height = frame_description.get_height()? as u32;
134        let timestamp = depth_frame.get_relative_time()? as u64;
135        let depth_min_reliable_distance = depth_frame.get_depth_min_reliable_distance()?;
136        let depth_max_reliable_distance = depth_frame.get_depth_max_reliable_distance()?;
137        let raw_buffer = depth_frame.access_underlying_buffer()?;
138
139        Ok(Self {
140            width,
141            height,
142            timestamp,
143            depth_min_reliable_distance,
144            depth_max_reliable_distance,
145            data: Arc::from(raw_buffer.to_vec()),
146        })
147    }
148
149    /// Converts depth values to meters.
150    ///
151    /// The depth values from the Kinect are in millimeters. This method
152    /// provides a convenient way to convert them to meters.
153    pub fn depth_to_meters(&self, depth_value: u16) -> f32 {
154        depth_value as f32 / 1000.0
155    }
156
157    /// Checks if a depth value is within the reliable range.
158    ///
159    /// The Kinect has minimum and maximum reliable distances. This method
160    /// helps determine if a depth reading is trustworthy.
161    pub fn is_depth_reliable(&self, depth_value: u16) -> bool {
162        depth_value >= self.depth_min_reliable_distance
163            && depth_value <= self.depth_max_reliable_distance
164    }
165
166    /// Gets the depth value at a specific pixel coordinate.
167    ///
168    /// Returns `None` if the coordinates are out of bounds.
169    pub fn get_depth_at(&self, x: u32, y: u32) -> Option<u16> {
170        if x >= self.width || y >= self.height {
171            return None;
172        }
173        let index = (y * self.width + x) as usize;
174        self.data.get(index).copied()
175    }
176}
177
178impl Default for DepthFrameData {
179    fn default() -> Self {
180        Self {
181            width: 0,
182            height: 0,
183            timestamp: 0,
184            depth_min_reliable_distance: 0,
185            depth_max_reliable_distance: 0,
186            data: Arc::from([]),
187        }
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194    use anyhow::anyhow;
195    use std::sync::mpsc;
196
197    #[test]
198    fn depth_capture_test() -> anyhow::Result<()> {
199        let (tx, rx) = mpsc::channel::<DepthFrameData>();
200        let max_frames_to_capture = 10;
201        let capture_thread = std::thread::spawn(move || -> anyhow::Result<()> {
202            let capture = DepthFrameCapture::new()?;
203            for (frame_count, frame) in capture.iter()?.enumerate() {
204                if frame_count >= max_frames_to_capture {
205                    break;
206                }
207                let data = frame.map_err(|e| anyhow!("Error capturing depth frame: {}", e))?;
208                if tx.send(data).is_err() {
209                    break;
210                }
211            }
212            Ok(())
213        });
214
215        let processing_thread = std::thread::spawn(move || -> anyhow::Result<()> {
216            for _ in 0..max_frames_to_capture {
217                let frame_data = match rx.recv() {
218                    Ok(data) => data,
219                    Err(_) => break,
220                };
221                println!(
222                    "Received depth frame: {}x{}, {} values, timestamp: {}, min: {}, max: {}",
223                    frame_data.width,
224                    frame_data.height,
225                    frame_data.data.len(),
226                    frame_data.timestamp,
227                    frame_data.depth_min_reliable_distance,
228                    frame_data.depth_max_reliable_distance
229                );
230                anyhow::ensure!(
231                    frame_data.width > 0,
232                    "Unexpected width: {}",
233                    frame_data.width
234                );
235                anyhow::ensure!(
236                    frame_data.height > 0,
237                    "Unexpected height: {}",
238                    frame_data.height
239                );
240                anyhow::ensure!(!frame_data.data.is_empty(), "Frame data is empty");
241                anyhow::ensure!(frame_data.timestamp > 0, "Timestamp is not positive");
242                anyhow::ensure!(
243                    frame_data.depth_max_reliable_distance > frame_data.depth_min_reliable_distance,
244                    "Depth max should be greater than min"
245                );
246            }
247            Ok(())
248        });
249
250        capture_thread
251            .join()
252            .map_err(|e| anyhow!("Depth capture thread join error: {:?}", e))??;
253        processing_thread
254            .join()
255            .map_err(|e| anyhow!("Processing thread join error: {:?}", e))??;
256        Ok(())
257    }
258}