| Name | Message | Date |
|---|---|---|
| 📄 lib.rs | 4 hours ago |
📄
kinect2-sys/src/lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
//! A small C ABI over the parts of libfreenect2's C++ API the capture //! pipeline needs (see `shim.cpp`). Every call catches C++ exceptions //! and reports through a thread-local message buffer instead. //! //! No bindgen: the surface is tiny and hand-declared in //! [`crate::ffi`], with layout `static_assert`s on the C++ side. /// Depth frames are 512x424 float millimetres (invalid: non-positive, /// NaN, infinity — libfreenect2's convention). pub const DEPTH_WIDTH: usize = 512; pub const DEPTH_HEIGHT: usize = 424; /// The format of a frame's bytes: libfreenect2 `Frame::Format`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum FrameFormat { /// 4 bytes: B, G, R, unused. Bgrx, /// 4 bytes: R, G, B, unused. Rgbx, /// 4-byte float per pixel. Float, /// Anything else. Other(i32), } impl FrameFormat { fn from_raw(raw: i32) -> Self { match raw { 4 => Self::Bgrx, 5 => Self::Rgbx, 2 => Self::Float, other => Self::Other(other), } } } mod ffi { use std::ffi::c_char; use std::ffi::c_void; #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct FrameView { /// Opaque `libfreenect2::Frame*`; owned by whoever allocated it. pub frame: *mut c_void, pub width: usize, pub height: usize, pub bytes_per_pixel: usize, pub data: *mut u8, pub format: i32, pub timestamp: u32, } #[repr(C)] #[derive(Debug, Clone, Copy, Default, PartialEq)] pub struct IrParams { pub fx: f32, pub fy: f32, pub cx: f32, pub cy: f32, pub k1: f32, pub k2: f32, pub k3: f32, pub p1: f32, pub p2: f32, } // Layout: keep in sync with shim.cpp's static_asserts. pub type Device = c_void; pub type Listener = c_void; pub type Registration = c_void; unsafe extern "C" { pub fn lf2_take_error() -> *const c_char; pub fn lf2_count_devices() -> i32; pub fn lf2_open_default() -> *mut Device; pub fn lf2_device_close(device: *mut Device); pub fn lf2_start(device: *mut Device) -> i32; pub fn lf2_stop(device: *mut Device) -> i32; pub fn lf2_get_ir_params(device: *mut Device, out: *mut IrParams); pub fn lf2_get_serial(device: *mut Device, buf: *mut c_char, len: usize); pub fn lf2_frame_new(width: i32, height: i32, bpp: i32) -> *mut FrameView; pub fn lf2_frame_free(frame: *mut FrameView); pub fn lf2_listener_new() -> *mut Listener; pub fn lf2_listener_free(listener: *mut Listener); pub fn lf2_listener_attach(device: *mut Device, listener: *mut Listener) -> i32; pub fn lf2_wait_for_frame( listener: *mut Listener, timeout_ms: i32, out_color: *mut FrameView, out_depth: *mut FrameView, ) -> i32; pub fn lf2_release(listener: *mut Listener); pub fn lf2_registration_new(device: *mut Device) -> *mut Registration; pub fn lf2_registration_free(registration: *mut Registration); pub fn lf2_register( registration: *mut Registration, color: *mut FrameView, depth: *mut FrameView, out_undistorted: *mut FrameView, out_registered: *mut FrameView, ) -> i32; } } /// The last error reported by a failing call on this thread. pub fn last_error() -> String { unsafe { let ptr = ffi::lf2_take_error(); if ptr.is_null() { "unknown libfreenect2 failure".into() } else { std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned() } } } macro_rules! call { ($expr:expr) => {{ let result = unsafe { $expr }; if result < 0 { anyhow::bail!("{}", last_error()); } result }}; } macro_rules! opt_call { ($expr:expr) => {{ let result = unsafe { $expr }; if result.is_null() { anyhow::bail!("{}", last_error()); } result }}; } /// A borrow of frame data — valid only until the listener releases the /// frame or the owning object is dropped. #[derive(Debug, Clone, Copy)] pub struct FrameRef { view: ffi::FrameView, } impl FrameRef { pub fn width(&self) -> usize { self.view.width } pub fn height(&self) -> usize { self.view.height } pub fn format(&self) -> FrameFormat { FrameFormat::from_raw(self.view.format) } /// The raw bytes of the frame. /// /// # Safety /// Valid for the borrow lifetime of the view; the caller must not /// use them after the source frame is released. pub unsafe fn bytes(&self) -> &[u8] { unsafe { std::slice::from_raw_parts( self.view.data, self.view.width * self.view.height * self.view.bytes_per_pixel, ) } } /// The floats of a `Float`-format frame. /// /// # Safety /// Valid for the borrow lifetime; `format` must be `Float`. pub unsafe fn floats(&self) -> &[f32] { unsafe { std::slice::from_raw_parts( self.view.data.cast::<f32>(), self.view.width * self.view.height, ) } } } /// A Kinect v2 device, via libfreenect2. Attach a [`Listener`] before /// [`Device::start`]. #[derive(Debug)] pub struct Device { ptr: *mut ffi::Device, } impl Device { /// How many Kinects are connected; `Err` on failure. pub fn count() -> anyhow::Result<usize> { Ok(call!(ffi::lf2_count_devices()) as usize) } /// Open the first connected device (CPU depth pipeline). pub fn open_default() -> anyhow::Result<Self> { Ok(Self { ptr: opt_call!(ffi::lf2_open_default()), }) } pub fn start(&self) -> anyhow::Result<()> { call!(ffi::lf2_start(self.ptr)); Ok(()) } pub fn stop(&self) -> anyhow::Result<()> { call!(ffi::lf2_stop(self.ptr)); Ok(()) } /// The depth (IR) camera's factory intrinsics, at 512x424. pub fn ir_params(&self) -> IrParams { let mut params = IrParams::default(); unsafe { ffi::lf2_get_ir_params(self.ptr, &mut params.0) }; params } /// The device serial number, for logging. pub fn serial(&self) -> String { let mut buf = [0 as std::ffi::c_char; 64]; unsafe { ffi::lf2_get_serial(self.ptr, buf.as_mut_ptr(), buf.len()) }; let cstr = unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }; cstr.to_string_lossy().into_owned() } fn close(&self) { unsafe { ffi::lf2_device_close(self.ptr) }; } } impl Drop for Device { fn drop(&mut self) { self.close(); } } /// Factory-calibrated depth intrinsics: focal length and principal /// point in pixels, plus the distortion model. #[derive(Debug, Clone, Copy, Default, PartialEq)] pub struct IrParams(ffi::IrParams); impl IrParams { pub fn fx(&self) -> f32 { self.0.fx } pub fn fy(&self) -> f32 { self.0.fy } pub fn cx(&self) -> f32 { self.0.cx } pub fn cy(&self) -> f32 { self.0.cy } } /// A synchronized color+depth listener attached to a [`Device`]. /// /// Frames are borrowed: read them inside /// [`Listener::with_frames`] and copy what you need; the frames are /// released when the closure returns. pub struct Listener { ptr: *mut ffi::Listener, } impl Listener { /// Create and attach a `Color | Depth` sync listener. pub fn attach(device: &Device) -> anyhow::Result<Self> { let listener = opt_call!(ffi::lf2_listener_new()); call!(ffi::lf2_listener_attach(device.ptr, listener)); Ok(Self { ptr: listener }) } /// Wait up to `timeout_ms` for a synchronized color+depth pair and /// run `f` with views into both, returning `f`'s value. /// Returns `Ok(None)` on timeout. pub fn with_frames<T>( &mut self, timeout_ms: i32, mut f: impl FnMut(&FrameRef, &FrameRef) -> T, ) -> anyhow::Result<Option<T>> { let mut color = ffi::FrameView { frame: std::ptr::null_mut(), width: 0, height: 0, bytes_per_pixel: 0, data: std::ptr::null_mut(), format: 0, timestamp: 0, }; let mut depth = color; let got = unsafe { ffi::lf2_wait_for_frame(self.ptr, timeout_ms, &mut color, &mut depth) }; match got { 1 => { let value = f(&FrameRef { view: color }, &FrameRef { view: depth }); unsafe { ffi::lf2_release(self.ptr) }; Ok(Some(value)) } 0 => Ok(None), _ => anyhow::bail!("{}", last_error()), } } } impl Drop for Listener { fn drop(&mut self) { unsafe { ffi::lf2_listener_free(self.ptr) }; } } /// Registration of color onto depth (libfreenect2's reverse-engineered /// factory-extrinsics mapping): produces the undistorted depth map /// (float millimetres) and the color image resampled onto the depth /// grid, both at 512x424. pub struct Registration { ptr: *mut ffi::Registration, undistorted: *mut ffi::FrameView, registered: *mut ffi::FrameView, } impl Registration { /// From the device's factory calibration. pub fn from_device(device: &Device) -> anyhow::Result<Self> { let registration = opt_call!(ffi::lf2_registration_new(device.ptr)); let undistorted = opt_call!(ffi::lf2_frame_new( DEPTH_WIDTH as i32, DEPTH_HEIGHT as i32, 4 )); let registered = opt_call!(ffi::lf2_frame_new( DEPTH_WIDTH as i32, DEPTH_HEIGHT as i32, 4 )); Ok(Self { ptr: registration, undistorted, registered, }) } /// Register one frame pair; the returned views are valid until the /// next call or drop. The registered image's channel order matches /// the color frame's (`FrameRef::format` of `color`). pub fn apply( &mut self, color: &FrameRef, depth: &FrameRef, ) -> anyhow::Result<(FrameRef, FrameRef)> { let mut color_view = color.view; let mut depth_view = depth.view; call!(ffi::lf2_register( self.ptr, &mut color_view, &mut depth_view, self.undistorted, self.registered )); // SAFETY: the out-frames live as long as `self`; the returned // views are plain copies pointing at their data, invalidated by // the next `apply` or by dropping `self`. unsafe { Ok(((*self.undistorted).borrow(), (*self.registered).borrow())) } } } // SAFETY: libfreenect2 device/listener/registration handles are // thread-safe across our usage (one worker thread drives them). unsafe impl Send for Device {} unsafe impl Send for Listener {} unsafe impl Send for Registration {} impl ffi::FrameView { /// Reinterpret as a borrow of the frame data. /// /// # Safety /// The view must be alive for `'self`'s lifetime; the caller /// guarantees it. unsafe fn borrow(&self) -> FrameRef { FrameRef { view: *self } } } impl Drop for Registration { fn drop(&mut self) { unsafe { ffi::lf2_frame_free(self.undistorted); ffi::lf2_frame_free(self.registered); ffi::lf2_registration_free(self.ptr); } } } #[cfg(test)] mod tests { use super::*; #[test] fn linkage_smoke_without_a_device() { // The library links and its context works even without USB. let count = Device::count().expect("enumerating devices works"); println!("{count} Kinect(s) connected"); } #[test] fn open_fails_clearly_without_a_device() { // The container has no USB: opening must fail (or succeed only // if a Kinect is actually attached, which it is not here). if Device::count().unwrap() == 0 { let err = Device::open_default().expect_err("no device"); assert!(!err.to_string().is_empty()); } } }