diff --git a/Cargo.toml b/Cargo.toml index 2b82e05..f5c0bab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["libodm", "leapdebug"] +members = ["libodm", "pylibodm"] diff --git a/README.md b/README.md index 7b6513d..02bec57 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # OpenDepthMap Point cloud streams from Leap Motion cameras ``` -clang libclang-dev libopencv-dev +clang libclang-dev libopencv-dev python3-dev python-dev ``` diff --git a/leapdebug/src/cli/logging.rs b/leapdebug/src/cli/logging.rs deleted file mode 100644 index 38f8712..0000000 --- a/leapdebug/src/cli/logging.rs +++ /dev/null @@ -1,21 +0,0 @@ - -pub fn start_fern() { - fern::Dispatch::new() - // Perform allocation-free log formatting - .format(|out, message, record| { - out.finish(format_args!( - "{}[{}][{}] {}", - chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), - record.target(), - record.level(), - message - )) - }) - // Add blanket level filter - - .level(log::LevelFilter::Debug) - - // Output to stdout, files, and other Dispatch configurations - .chain(std::io::stdout()) - // Apply globally - .apply().unwrap(); -} \ No newline at end of file diff --git a/leapdebug/src/cli/mod.rs b/leapdebug/src/cli/mod.rs deleted file mode 100644 index cdb11a3..0000000 --- a/leapdebug/src/cli/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod logging; \ No newline at end of file diff --git a/leapdebug/src/main.rs b/leapdebug/src/main.rs deleted file mode 100644 index 47393b7..0000000 --- a/leapdebug/src/main.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::time::Duration; - -use cli::logging::start_fern; -use libodm::leapmotion::device::LeapMotionDevice; - -mod cli; - -fn main() { - start_fern(); - - // Init a device - let mut device = LeapMotionDevice::create_device(Duration::from_secs(4)).unwrap(); - - log::info!("Got data"); - - log::info!("Press CTRL+C to exit"); - loop { - // Read a single frame - let frame = device.get_frame().unwrap(); - - // Show data - frame.debug_show(); - } -} diff --git a/libodm/Cargo.toml b/libodm/Cargo.toml index 5f079b6..49f26fa 100644 --- a/libodm/Cargo.toml +++ b/libodm/Cargo.toml @@ -10,9 +10,8 @@ build = "build.rs" [dependencies] libc = "0.2.95" log = "0.4.14" -glam = "0.15.2" -opencv = "0.53.0" +# glam = "0.15.2" +# opencv = "0.53.0" [build-dependencies] -cc = "1.0" -pkg-config = "0.3.19" \ No newline at end of file +cc = "1.0" \ No newline at end of file diff --git a/libodm/src/image.rs b/libodm/src/image.rs index 0fca6d2..3a047ef 100644 --- a/libodm/src/image.rs +++ b/libodm/src/image.rs @@ -1,56 +1,32 @@ -use glam::UVec2; -use opencv::{core::{Image2D, Mat, Mat_AUTO_STEP, Size, CV_8U}, highgui::{imshow, named_window, wait_key}}; -pub struct Image { - size: UVec2, - raw_data: *const u8, - img: Mat, +pub struct Image<'a> { + width: u32, + height: u32, + buffer: &'a [u8], } -impl Image { - +impl Image<'_> { /// Create a new image from raw data - pub fn new_raw(width: i32, height: i32, raw_data: *const u8) -> Result { - // Build a CV size - let size = Size::new(width, height); - - let image; - unsafe { - image = - Mat::new_size_with_data(size, CV_8U, raw_data as *mut libc::c_void, Mat_AUTO_STEP)?; + pub fn new(width: i32, height: i32, raw_data: *const u8) -> Self { + Self { + width: width as u32, + height: height as u32, + buffer: unsafe { std::slice::from_raw_parts(raw_data, (width * height) as usize) }, } - - Ok(Self { - size: UVec2::new(width as u32, height as u32), - raw_data, - img: image, - }) } - /// Get the image size - pub fn get_size(&self) -> &UVec2 { - &self.size + /// Get the image width + pub fn get_width(&self) -> u32 { + self.width + } + + /// Get the image height + pub fn get_height(&self) -> u32 { + self.height } /// Get the internal OpenCV image - pub fn get_image(&self) -> &Mat { - &self.img - } - - /// Show this image in a debug window - pub fn debug_show(&self){ - - // Create a debug window - imshow("Debug", &self.img).unwrap(); - wait_key(0).unwrap(); - + pub fn get_image(&self) -> &[u8] { + &self.buffer } } - -// impl Drop for Image { -// fn drop(&mut self) { -// unsafe { -// libc::free(self.raw_data as *mut libc::c_void); -// } -// } -// } diff --git a/libodm/src/leapmotion/device.rs b/libodm/src/leapmotion/device.rs index b56b297..383bd62 100644 --- a/libodm/src/leapmotion/device.rs +++ b/libodm/src/leapmotion/device.rs @@ -2,8 +2,6 @@ use std::time::{Duration, SystemTime}; -use opencv::highgui::{imshow, wait_key}; - use crate::image::Image; use super::ffi; @@ -25,23 +23,15 @@ pub enum DeviceError { } /// Represents a single data frame generated by the LeapMotion device -pub struct DeviceFrame { +pub struct DeviceFrame<'a> { /// Number of bytes per pixel pub bytes_per_pixel: u8, /// Image from the left camera - pub left_camera: Image, + pub left_camera: Image<'a>, /// Image from the right camera - pub right_camera: Image, -} - -impl DeviceFrame { - pub fn debug_show(&self){ - imshow("Left Camera", self.left_camera.get_image()).unwrap(); - imshow("Right Camera", self.right_camera.get_image()).unwrap(); - wait_key(25).unwrap(); - } + pub right_camera: Image<'a>, } /// Represents a LeapMotion device, and provides safe wrappers around its FFI @@ -114,8 +104,8 @@ impl LeapMotionDevice { /// Get the latest frame from the device pub fn get_frame(&mut self) -> Result { // Update the frame data from the controller - unsafe{ - if ffi::isControllerCreated(){ + unsafe { + if ffi::isControllerCreated() { ffi::updateFrame(); } } @@ -128,28 +118,30 @@ impl LeapMotionDevice { let image_width; let image_height; let bytes_per_pixel; - let mut left_image_raw; - let mut right_image_raw; + let left_image_raw; + let right_image_raw; unsafe { image_width = ffi::getImageWidth(); image_height = ffi::getImageHeight(); bytes_per_pixel = ffi::getImageBPP(); - left_image_raw = ffi::getImageLeft(); - right_image_raw = ffi::getImageRight(); + + if self.cameras_flipped { + left_image_raw = ffi::getImageRight(); + right_image_raw = ffi::getImageLeft(); + } else { + left_image_raw = ffi::getImageLeft(); + right_image_raw = ffi::getImageRight(); + } } // Build two images - let left_image = Image::new_raw(image_width, image_height, left_image_raw); - let right_image = Image::new_raw(image_width, image_height, right_image_raw); - - if left_image.is_err() || right_image.is_err() { - return Err(DeviceError::DecodeError); - } + let left_image = Image::new(image_width, image_height, left_image_raw); + let right_image = Image::new(image_width, image_height, right_image_raw); Ok(DeviceFrame { bytes_per_pixel: bytes_per_pixel as u8, - left_camera: left_image.unwrap(), - right_camera: right_image.unwrap(), + left_camera: left_image, + right_camera: right_image, }) } } diff --git a/leapdebug/Cargo.toml b/pylibodm/Cargo.toml similarity index 60% rename from leapdebug/Cargo.toml rename to pylibodm/Cargo.toml index af15793..f27b289 100644 --- a/leapdebug/Cargo.toml +++ b/pylibodm/Cargo.toml @@ -1,13 +1,17 @@ [package] -name = "leapdebug" +name = "pylibodm" version = "0.1.0" -authors = ["Evan Pratten "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "pylibodm" +crate-type = ["cdylib"] + [dependencies] libodm = { version = "0.1.0", path = "../libodm"} -fern = "0.6.0" -log = "0.4.14" -chrono = "0.4.19" \ No newline at end of file + +[dependencies.pyo3] +version = "0.13.2" +features = ["extension-module"] \ No newline at end of file diff --git a/pylibodm/src/lib.rs b/pylibodm/src/lib.rs new file mode 100644 index 0000000..6a9f1fd --- /dev/null +++ b/pylibodm/src/lib.rs @@ -0,0 +1,114 @@ +use libodm::leapmotion::device::LeapMotionDevice; + +pub(crate) static mut DEVICE_INSTANCE: Option = None; + +mod errors { + use libodm::leapmotion::device::DeviceError; + use pyo3::{exceptions::PyOSError, prelude::*}; + + #[derive(Debug)] + pub struct PyDeviceError(DeviceError); + + impl From for PyDeviceError { + fn from(e: DeviceError) -> Self { + Self { 0: e } + } + } + + impl std::convert::From for PyErr { + fn from(err: PyDeviceError) -> PyErr { + PyOSError::new_err(format!("{:?}", err.0)) + } + } +} + +mod device { + use std::time::Duration; + + use libodm::{ + image::Image, + leapmotion::device::{DeviceFrame, LeapMotionDevice}, + }; + use pyo3::prelude::*; + + use crate::{errors::PyDeviceError, DEVICE_INSTANCE}; + + #[pyclass] + struct PyImage { + pub width: u32, + pub height: u32, + pub buffer: Vec, + } + + impl IntoPy for Image<'_> { + fn into_py(self, py: Python) -> PyImage { + PyImage { + width: self.get_width(), + height: self.get_height(), + buffer: self.get_image().into_iter().map(|e| *e).collect(), + } + } + } + + #[pyclass] + struct PyDeviceFrame { + pub bytes_per_pixel: u8, + pub left_camera: PyImage, + pub right_camera: PyImage, + } + + impl IntoPy for DeviceFrame<'_> { + fn into_py(self, py: Python) -> PyDeviceFrame { + PyDeviceFrame { + bytes_per_pixel: self.bytes_per_pixel, + left_camera: self.left_camera.into_py(py), + right_camera: self.right_camera.into_py(py), + } + } + } + + #[pymodule] + pub fn leap_device(py: Python, m: &PyModule) -> PyResult<()> { + #[pyfn(m, "connect")] + fn connect(timeout_secs: u64) -> Result<(), PyDeviceError> { + // Create a leap device + let device = LeapMotionDevice::create_device(Duration::from_secs(timeout_secs))?; + + // Override the instance + unsafe { + DEVICE_INSTANCE = Some(device); + } + + Ok(()) + } + + #[pyfn(m, "set_cameras_flipped")] + fn set_cameras_flipped(flipped: bool) -> PyResult<()> { + unsafe { + DEVICE_INSTANCE + .as_mut() + .unwrap() + .set_cameras_flipped(flipped); + } + + Ok(()) + } + + #[pyfn(m, "get_cameras_flipped")] + fn get_cameras_flipped() -> PyResult { + Ok(unsafe { DEVICE_INSTANCE.as_ref().unwrap().cameras_flipped() }) + } + + #[pyfn(m, "has_frame")] + fn has_frame() -> PyResult { + Ok(unsafe { DEVICE_INSTANCE.as_mut().unwrap().has_frame() }) + } + + #[pyfn(m, "get_frame")] + fn get_frame(py: Python) -> Result { + Ok(unsafe { DEVICE_INSTANCE.as_mut().unwrap().get_frame()?.into_py(py) }) + } + + Ok(()) + } +}