kinect_v2/
infrared_capture.rs1use std::sync::Arc;
2
3use kinect_v2_sys::{
4 DEFAULT_FRAME_WAIT_TIMEOUT_MS, KINECT_DEFAULT_CAPTURE_FPS, WAITABLE_HANDLE,
5 infrared::{InfraredFrame, InfraredFrameReader},
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
12pub struct InfraredFrameCapture {
17 kinect: KinectSensor, }
19
20impl InfraredFrameCapture {
21 pub fn new() -> Result<Self, Error> {
31 let kinect = kinect::get_default_kinect_sensor()?;
32 kinect.open()?;
33
34 Ok(InfraredFrameCapture { kinect })
35 }
36
37 pub fn iter(&self) -> Result<InfraredFrameCaptureIter, Error> {
48 let source = self.kinect.infrared_frame_source()?;
49 let reader = source.open_reader()?;
51 if !source.get_is_active()? {
54 log::warn!(
55 "Infrared frame source is not active, cannot subscribe to frame arrived event."
56 );
57 return Err(Error::from_hresult(E_FAIL));
58 }
59
60 let waitable_handle = reader.subscribe_frame_arrived()?;
61 Ok(InfraredFrameCaptureIter {
62 reader,
63 waitable_handle,
64 timeout_ms: DEFAULT_FRAME_WAIT_TIMEOUT_MS,
65 })
66 }
67}
68
69pub struct InfraredFrameCaptureIter {
74 reader: InfraredFrameReader,
75 waitable_handle: WAITABLE_HANDLE,
76 timeout_ms: u32,
77}
78
79impl Drop for InfraredFrameCaptureIter {
80 fn drop(&mut self) {
81 if let Err(e) = self.reader.unsubscribe_frame_arrived(self.waitable_handle) {
84 log::warn!("Failed to unsubscribe infrared frame arrived event: {e:?}");
85 }
86 }
87}
88
89impl Iterator for InfraredFrameCaptureIter {
90 type Item = Result<InfraredFrameData, Error>;
91
92 fn next(&mut self) -> Option<Self::Item> {
93 loop {
94 let wait_status: WAIT_EVENT =
95 unsafe { WaitForSingleObject(self.waitable_handle, self.timeout_ms) };
96
97 if wait_status == WAIT_OBJECT_0 {
98 let result = (|| {
101 let event_args = self
102 .reader
103 .get_frame_arrived_event_data(self.waitable_handle)?;
104 let frame_reference = event_args.get_frame_reference()?;
105 let infrared_frame = frame_reference.acquire_frame()?;
106 InfraredFrameData::new(&infrared_frame)
107 })(); return Some(result);
109 } else if wait_status == WAIT_TIMEOUT {
110 continue;
113 } else {
114 return Some(Err(Error::from_hresult(E_FAIL)));
115 }
116 }
117 }
118}
119
120#[derive(Debug, Clone)]
121pub struct InfraredFrameData {
122 pub width: u32,
123 pub height: u32,
124 pub fps: u32, pub timestamp: u64,
126 pub data: Arc<[u16]>, }
128
129impl InfraredFrameData {
130 pub fn new(infrared_frame: &InfraredFrame) -> Result<Self, Error> {
131 let frame_description = infrared_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 = infrared_frame.get_relative_time()? as u64;
135 let raw_buffer = infrared_frame.access_underlying_buffer()?;
136
137 Ok(Self {
138 width,
139 height,
140 fps: KINECT_DEFAULT_CAPTURE_FPS,
141 timestamp,
142 data: Arc::from(raw_buffer.to_vec()),
143 })
144 }
145
146 pub fn get_intensity_at(&self, x: u32, y: u32) -> Option<u16> {
150 if x >= self.width || y >= self.height {
151 return None;
152 }
153 let index = (y * self.width + x) as usize;
154 self.data.get(index).copied()
155 }
156
157 pub fn normalize_intensity(&self, intensity: u16) -> f32 {
162 intensity as f32 / 65535.0
164 }
165
166 pub fn to_grayscale_u8(&self) -> Vec<u8> {
171 self.data
172 .iter()
173 .map(|&intensity| (intensity >> 8) as u8) .collect()
175 }
176
177 pub fn get_intensity_range(&self) -> (u16, u16) {
181 let min_intensity = *self.data.iter().min().unwrap_or(&0);
182 let max_intensity = *self.data.iter().max().unwrap_or(&0);
183 (min_intensity, max_intensity)
184 }
185}
186
187impl Default for InfraredFrameData {
188 fn default() -> Self {
189 Self {
190 width: 0,
191 height: 0,
192 fps: KINECT_DEFAULT_CAPTURE_FPS,
193 timestamp: 0,
194 data: Arc::from([]),
195 }
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use anyhow::anyhow;
203 use std::sync::mpsc;
204
205 #[test]
206 fn infrared_capture_test() -> anyhow::Result<()> {
207 let (tx, rx) = mpsc::channel::<InfraredFrameData>();
208 let max_frames_to_capture = 10;
209 let capture_thread = std::thread::spawn(move || -> anyhow::Result<()> {
210 let capture = InfraredFrameCapture::new()?;
211 for (frame_count, frame) in capture.iter()?.enumerate() {
212 if frame_count >= max_frames_to_capture {
213 break;
214 }
215 let data = frame.map_err(|e| anyhow!("Error capturing infrared frame: {}", e))?;
216 if tx.send(data).is_err() {
217 break;
218 }
219 }
220 Ok(())
221 });
222
223 let processing_thread = std::thread::spawn(move || -> anyhow::Result<()> {
224 for _ in 0..max_frames_to_capture {
225 let frame_data = match rx.recv() {
226 Ok(data) => data,
227 Err(_) => break,
228 };
229 println!(
230 "Received infrared frame: {}x{}, {} values, timestamp: {}, fps: {}",
231 frame_data.width,
232 frame_data.height,
233 frame_data.data.len(),
234 frame_data.timestamp,
235 frame_data.fps
236 );
237 anyhow::ensure!(
238 frame_data.width > 0,
239 "Unexpected width: {}",
240 frame_data.width
241 );
242 anyhow::ensure!(
243 frame_data.height > 0,
244 "Unexpected height: {}",
245 frame_data.height
246 );
247 anyhow::ensure!(!frame_data.data.is_empty(), "Frame data is empty");
248 anyhow::ensure!(frame_data.timestamp > 0, "Timestamp is not positive");
249 anyhow::ensure!(frame_data.fps > 0, "FPS is not positive");
250 }
251 Ok(())
252 });
253
254 capture_thread
255 .join()
256 .map_err(|e| anyhow!("Infrared capture thread join error: {:?}", e))??;
257 processing_thread
258 .join()
259 .map_err(|e| anyhow!("Processing thread join error: {:?}", e))??;
260 Ok(())
261 }
262}