use super::pixel::{get_pixel, PixelKind};
use super::prelude::{
CouldNotGetFrameSensorError, DepthError, DisparityError, FrameCategory, FrameConstructionError,
FrameEx, BITS_PER_BYTE,
};
use crate::{
check_rs2_error,
kind::{Rs2Extension, Rs2FrameMetadata, Rs2Option, Rs2StreamKind, Rs2TimestampDomain},
sensor::Sensor,
stream_profile::StreamProfile,
};
use anyhow::Result;
use num_traits::FromPrimitive;
use realsense_sys as sys;
use std::{
convert::{TryFrom, TryInto},
marker::PhantomData,
os::raw::c_int,
ptr::{self, NonNull},
};
#[derive(Debug)]
pub struct Depth;
#[derive(Debug)]
pub struct Disparity;
#[derive(Debug)]
pub struct Color;
#[derive(Debug)]
pub struct Infrared;
#[derive(Debug)]
pub struct Fisheye;
#[derive(Debug)]
pub struct Confidence;
#[derive(Debug)]
pub struct ImageFrame<Kind> {
frame_ptr: NonNull<sys::rs2_frame>,
width: usize,
height: usize,
stride: usize,
bits_per_pixel: usize,
timestamp: f64,
timestamp_domain: Rs2TimestampDomain,
frame_stream_profile: StreamProfile,
data_size_in_bytes: usize,
data: NonNull<std::os::raw::c_void>,
should_drop: bool,
_phantom: PhantomData<Kind>,
}
pub struct Iter<'a, K> {
pub(crate) frame: &'a ImageFrame<K>,
pub(crate) column: usize,
pub(crate) row: usize,
}
impl<'a, K> Iterator for Iter<'a, K> {
type Item = PixelKind<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.column >= self.frame.width() || self.row >= self.frame.height() {
return None;
}
let next = self.frame.get_unchecked(self.column, self.row);
self.column += 1;
if self.column >= self.frame.width() {
self.column = 0;
self.row += 1;
}
Some(next)
}
}
pub type DepthFrame = ImageFrame<Depth>;
pub type DisparityFrame = ImageFrame<Disparity>;
pub type ColorFrame = ImageFrame<Color>;
pub type InfraredFrame = ImageFrame<Infrared>;
pub type FisheyeFrame = ImageFrame<Fisheye>;
pub type ConfidenceFrame = ImageFrame<Confidence>;
impl<K> Drop for ImageFrame<K> {
fn drop(&mut self) {
unsafe {
if self.should_drop {
sys::rs2_release_frame(self.frame_ptr.as_ptr());
}
}
}
}
impl<'a, K> IntoIterator for &'a ImageFrame<K> {
type Item = <Iter<'a, K> as Iterator>::Item;
type IntoIter = Iter<'a, K>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
unsafe impl<K> Send for ImageFrame<K> {}
impl<K> TryFrom<NonNull<sys::rs2_frame>> for ImageFrame<K> {
type Error = anyhow::Error;
fn try_from(frame_ptr: NonNull<sys::rs2_frame>) -> Result<Self, Self::Error> {
unsafe {
let mut err = ptr::null_mut::<sys::rs2_error>();
let width = sys::rs2_get_frame_width(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetWidth)?;
let height = sys::rs2_get_frame_height(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetHeight)?;
let bits_per_pixel = sys::rs2_get_frame_bits_per_pixel(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetBitsPerPixel)?;
let stride = sys::rs2_get_frame_stride_in_bytes(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetStride)?;
let timestamp = sys::rs2_get_frame_timestamp(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetTimestamp)?;
let timestamp_domain =
sys::rs2_get_frame_timestamp_domain(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetTimestampDomain)?;
let profile_ptr = sys::rs2_get_frame_stream_profile(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetFrameStreamProfile)?;
let nonnull_profile_ptr =
NonNull::new(profile_ptr as *mut sys::rs2_stream_profile).unwrap();
let profile = StreamProfile::try_from(nonnull_profile_ptr)?;
let size = sys::rs2_get_frame_data_size(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetDataSize)?;
debug_assert_eq!(size, width * height * bits_per_pixel / BITS_PER_BYTE);
let data_ptr = sys::rs2_get_frame_data(frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, FrameConstructionError::CouldNotGetData)?;
let nonnull_data_ptr = NonNull::new(data_ptr as *mut std::os::raw::c_void).unwrap();
Ok(ImageFrame {
frame_ptr,
width: width as usize,
height: height as usize,
stride: stride as usize,
bits_per_pixel: bits_per_pixel as usize,
timestamp,
timestamp_domain: Rs2TimestampDomain::from_i32(timestamp_domain as i32).unwrap(),
frame_stream_profile: profile,
data_size_in_bytes: size as usize,
data: nonnull_data_ptr,
should_drop: true,
_phantom: PhantomData::<K> {},
})
}
}
}
impl FrameCategory for DepthFrame {
fn extension() -> Rs2Extension {
Rs2Extension::DepthFrame
}
fn kind() -> Rs2StreamKind {
Rs2StreamKind::Depth
}
fn has_correct_kind(&self) -> bool {
self.frame_stream_profile.kind() == Self::kind()
}
}
impl FrameCategory for DisparityFrame {
fn extension() -> Rs2Extension {
Rs2Extension::DisparityFrame
}
fn kind() -> Rs2StreamKind {
Rs2StreamKind::Any
}
fn has_correct_kind(&self) -> bool {
self.frame_stream_profile.kind() == Self::kind()
}
}
impl FrameCategory for ColorFrame {
fn extension() -> Rs2Extension {
Rs2Extension::VideoFrame
}
fn kind() -> Rs2StreamKind {
Rs2StreamKind::Color
}
fn has_correct_kind(&self) -> bool {
self.frame_stream_profile.kind() == Self::kind()
}
}
impl FrameCategory for InfraredFrame {
fn extension() -> Rs2Extension {
Rs2Extension::VideoFrame
}
fn kind() -> Rs2StreamKind {
Rs2StreamKind::Infrared
}
fn has_correct_kind(&self) -> bool {
self.frame_stream_profile.kind() == Self::kind()
}
}
impl FrameCategory for FisheyeFrame {
fn extension() -> Rs2Extension {
Rs2Extension::VideoFrame
}
fn kind() -> Rs2StreamKind {
Rs2StreamKind::Fisheye
}
fn has_correct_kind(&self) -> bool {
self.frame_stream_profile.kind() == Self::kind()
}
}
impl FrameCategory for ConfidenceFrame {
fn extension() -> Rs2Extension {
Rs2Extension::VideoFrame
}
fn kind() -> Rs2StreamKind {
Rs2StreamKind::Confidence
}
fn has_correct_kind(&self) -> bool {
self.frame_stream_profile.kind() == Self::kind()
}
}
impl<T> FrameEx for ImageFrame<T> {
fn stream_profile(&self) -> &StreamProfile {
&self.frame_stream_profile
}
fn sensor(&self) -> Result<Sensor> {
unsafe {
let mut err = std::ptr::null_mut::<sys::rs2_error>();
let sensor_ptr = sys::rs2_get_frame_sensor(self.frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, CouldNotGetFrameSensorError)?;
Ok(Sensor::try_from(NonNull::new(sensor_ptr).unwrap())?)
}
}
fn timestamp(&self) -> f64 {
self.timestamp
}
fn timestamp_domain(&self) -> Rs2TimestampDomain {
self.timestamp_domain
}
fn metadata(&self, metadata_kind: Rs2FrameMetadata) -> Option<std::os::raw::c_longlong> {
if !self.supports_metadata(metadata_kind) {
return None;
}
unsafe {
let mut err = std::ptr::null_mut::<sys::rs2_error>();
let val = sys::rs2_get_frame_metadata(
self.frame_ptr.as_ptr(),
#[allow(clippy::useless_conversion)]
(metadata_kind as i32).try_into().unwrap(),
&mut err,
);
if err.as_ref().is_none() {
Some(val)
} else {
sys::rs2_free_error(err);
None
}
}
}
fn supports_metadata(&self, metadata_kind: Rs2FrameMetadata) -> bool {
unsafe {
let mut err = std::ptr::null_mut::<sys::rs2_error>();
let supports_metadata = sys::rs2_supports_frame_metadata(
self.frame_ptr.as_ptr(),
#[allow(clippy::useless_conversion)]
(metadata_kind as i32).try_into().unwrap(),
&mut err,
);
if err.as_ref().is_none() {
supports_metadata != 0
} else {
sys::rs2_free_error(err);
false
}
}
}
unsafe fn get_owned_raw(mut self) -> NonNull<sys::rs2_frame> {
self.should_drop = false;
self.frame_ptr
}
}
impl DepthFrame {
pub fn distance(&self, col: usize, row: usize) -> Result<f32, DepthError> {
unsafe {
let mut err = ptr::null_mut::<sys::rs2_error>();
let distance = sys::rs2_depth_frame_get_distance(
self.frame_ptr.as_ptr(),
col as c_int,
row as c_int,
&mut err,
);
check_rs2_error!(err, DepthError::CouldNotGetDistance)?;
Ok(distance)
}
}
pub fn depth_units(&self) -> Result<f32> {
let sensor = self.sensor()?;
let depth_units = sensor.get_option(Rs2Option::DepthUnits).ok_or_else(|| {
anyhow::anyhow!("Option is not supported on the sensor for this frame type.")
})?;
Ok(depth_units)
}
}
impl DisparityFrame {
pub fn distance(&self, col: usize, row: usize) -> Result<f32, DepthError> {
unsafe {
let mut err = ptr::null_mut::<sys::rs2_error>();
let distance = sys::rs2_depth_frame_get_distance(
self.frame_ptr.as_ptr(),
col as c_int,
row as c_int,
&mut err,
);
check_rs2_error!(err, DepthError::CouldNotGetDistance)?;
Ok(distance)
}
}
pub fn depth_units(&self) -> Result<f32> {
let sensor = self.sensor()?;
let depth_units = sensor.get_option(Rs2Option::DepthUnits).ok_or_else(|| {
anyhow::anyhow!("Option is not supported on the sensor for this frame type.")
})?;
Ok(depth_units)
}
pub fn baseline(&self) -> Result<f32, DisparityError> {
unsafe {
let mut err = ptr::null_mut::<sys::rs2_error>();
let baseline =
sys::rs2_depth_stereo_frame_get_baseline(self.frame_ptr.as_ptr(), &mut err);
check_rs2_error!(err, DisparityError)?;
Ok(baseline)
}
}
}
impl<K> ImageFrame<K> {
pub fn iter(&self) -> Iter<'_, K> {
Iter {
frame: self,
column: 0,
row: 0,
}
}
#[inline(always)]
pub fn get_unchecked(&self, col: usize, row: usize) -> PixelKind<'_> {
unsafe {
get_pixel(
self.frame_stream_profile.format(),
self.data_size_in_bytes,
self.data.as_ptr(),
self.stride,
col,
row,
)
}
}
pub fn stride(&self) -> usize {
self.stride
}
pub fn bits_per_pixel(&self) -> usize {
self.bits_per_pixel
}
pub fn get_data_size(&self) -> usize {
self.data_size_in_bytes
}
pub unsafe fn get_data(&self) -> &std::os::raw::c_void {
self.data.as_ref()
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn get(&self, col: usize, row: usize) -> Option<PixelKind<'_>> {
if col >= self.width || row >= self.height {
None
} else {
Some(self.get_unchecked(col, row))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn frame_has_correct_kind() {
assert_eq!(ColorFrame::kind(), Rs2StreamKind::Color);
assert_eq!(DepthFrame::kind(), Rs2StreamKind::Depth);
assert_eq!(DisparityFrame::kind(), Rs2StreamKind::Any);
assert_eq!(InfraredFrame::kind(), Rs2StreamKind::Infrared);
assert_eq!(FisheyeFrame::kind(), Rs2StreamKind::Fisheye);
assert_eq!(ConfidenceFrame::kind(), Rs2StreamKind::Confidence);
}
}