cv_bridge/
cv_image.rs

1//! cv_image module contains CvImage struct and its methods.
2//! CvImage wraps the image array and its metadata and acts as
3//! a bridge between the `sensor_msgs::Image` message and `cv::Mat`
4
5use opencv::prelude::*;
6use std::error::Error;
7
8use crate::msgs::{
9    std_msgs::Header,
10    sensor_msgs::Image,
11};
12use crate::utils::{
13    image_encodings,
14    image_byteorder_ops,
15};
16
17#[derive(Debug)]
18pub enum DataContainer {
19    VecU8(Vec<u8>),
20    VecI8(Vec<i8>),
21    VecU16(Vec<u16>),
22    VecI16(Vec<i16>),
23    VecI32(Vec<u32>),
24    VecF32(Vec<f32>),
25    VecF64(Vec<f64>),
26}
27
28#[derive(Debug)]
29pub struct CvImage {
30    header: Header,
31    height: usize,
32    width: usize,
33    encoding: String,
34    data: DataContainer,
35}
36
37impl CvImage {
38    /// Constructs a new `CvImage` from a `sensor_msgs::Image` message.
39    /// 
40    /// # Example
41    /// ```
42    /// let image = rosrust_msg::sensor_msgs::Image::default();
43    /// // set the image data
44    /// let cv_image = CvImage::from_imgmsg(image).unwrap();
45    /// ```
46    /// 
47    /// ## Arguments
48    /// * `image` - `rosrust_msg::sensor_msgs::Image` message 
49    /// 
50    /// ## Returns
51    /// * `CvImage` object
52    pub fn from_imgmsg(image: Image) -> Result<CvImage, Box<dyn Error>> {
53        let bit_depth = image_encodings::get_bit_depth(image.encoding.as_str());
54       
55        let data = match bit_depth {
56            8 => DataContainer::VecU8(image.data),
57            16 => DataContainer::VecU8(match image.is_bigendian {
58                0 => image.data,
59                1 => image_byteorder_ops::from_be_to_le(&image.data),
60                _ => Err(format!("Unsupported endianness [endianness: {}]", image.is_bigendian))?
61            }),
62            _ => Err(format!("Unsupported bit depth for container [bit depth: {}]", bit_depth))?
63        };
64
65        Ok(CvImage {
66            header: image.header,
67            height: image.height as usize,
68            width: image.width as usize,
69            encoding: image.encoding,
70            data: data
71        })
72    }
73
74    /// Constructs a new `CvImage` from a `cv::Mat` object.
75    /// 
76    /// # Example
77    /// ```
78    /// let mat = opencv::core::Mat::default();
79    /// // set the image data
80    /// let cv_image = CvImage::from_cvmat(mat, "bgr8").unwrap();
81    /// ```
82    /// 
83    /// ## Arguments
84    /// * `mat` - `opencv::core::Mat` object
85    /// * `encoding` - Encoding of the image. Note that `Mat` does not
86    ///                 contain any metadata about the image encoding, so it must be
87    ///                 tracked by the user.
88    /// 
89    /// ## Returns
90    /// * `CvImage` object
91    pub fn from_cvmat(mat: Mat, encoding: &str) -> Result<CvImage, Box<dyn Error>> {
92        let (width, height) = (mat.cols(), mat.rows());
93        let bit_depth = image_encodings::get_bit_depth(encoding);
94
95        let data = match mat.data_bytes() {
96            Ok(data) => data,
97            Err(_) => Err(format!("Could not get data from Mat"))?
98        };
99        
100        let data = match bit_depth {
101            8 => DataContainer::VecU8(data.to_vec()),
102            16 => DataContainer::VecU8(data.to_vec()),
103            _ => Err(format!("Unsupported bit depth for container [bit depth: {}]", bit_depth))?
104        };
105
106        Ok(CvImage {
107            header: Header::default(),
108            height: height as usize,
109            width: width as usize,
110            encoding: encoding.to_string(),
111            data: data,
112        })
113    }
114
115    /// Converts the `CvImage` to a `sensor_msgs::Image` message.
116    /// 
117    /// # Example:
118    /// ```
119    /// let mut cv_image = CvImage::from_imgmsg(image_msg).unwrap();
120    /// let image_msg = cv_image.to_imgmsg(0).unwrap();
121    /// ```
122    /// 
123    /// ## Arguments
124    /// * `is_bigendian` - Endianness of the image data. 0 for little-endian, 1 for big-endian.
125    /// 
126    /// ## Returns
127    /// * `sensor_msgs::Image` message
128    pub fn into_imgmsg(self, is_bigendian: u8) -> Result<Image, Box<dyn Error>> {
129        let step = self.width as u32 * image_encodings::get_num_channels(&self.encoding) as u32;
130        
131        let data = match self.data {
132            DataContainer::VecU8(data) => data,
133            DataContainer::VecU16(data) => image_byteorder_ops::from_u16_to_u8(&data, is_bigendian == 1),
134            _ => Err(format!("Unsupported container type"))?
135        };
136
137        Ok(Image {
138            header: self.header,
139            height: self.height as u32,
140            width: self.width as u32,
141            encoding: self.encoding,
142            is_bigendian: is_bigendian,
143            step: step,
144            data: data
145        })
146    }
147
148    /// Converts the `CvImage` to a `CvImage` with a different encoding. It will copy the data
149    /// into the new buffer.
150    /// 
151    /// # Example:
152    /// ```
153    /// let mut cv_image = CvImage::from_imgmsg(image_msg).unwrap();
154    /// let cv_image = cv_image.to_cvimage("mono8").unwrap();
155    /// ```
156    /// 
157    /// ## Arguments
158    /// * `desired_encoding` - Encoding of the new image. Check the supported encodings in the
159    ///                       `image_encodings` module.
160    /// 
161    /// ## Returns
162    /// * `CvImage` object
163    pub fn to_cvimage(&mut self, desired_encoding: &str) -> Result<CvImage, Box<dyn Error>> {
164        let src_enc = image_encodings::from_encstr_to_cvenc(&self.encoding)?;
165        let dst_enc = image_encodings::from_encstr_to_cvenc(desired_encoding)?;
166        let convertion_code = image_encodings::get_conversion_code(src_enc, dst_enc)?;
167
168        let src_mat = self.as_cvmat()?;
169        let mut dst_mat = Mat::default();
170
171        opencv::imgproc::cvt_color(&src_mat, &mut dst_mat, convertion_code, 0)?;
172
173        let cvtype = image_encodings::from_encstr_to_cvtype(desired_encoding)?;
174        let scaling = image_encodings::get_scaling_factor(&self.encoding, desired_encoding);
175
176        let mut dst2_mat = Mat::default();
177        
178        dst_mat.convert_to(&mut dst2_mat, cvtype, scaling, 0.0)?;
179
180        Ok(CvImage::from_cvmat(dst2_mat, desired_encoding)?)
181    }
182
183    /// Returns the image as a `cv::Mat` object. This is a cheap operation 
184    /// as the data is shared between the `CvImage` and the `Mat` object.
185    /// 
186    /// ## Returns
187    /// * `opencv::core::Mat` object
188    pub fn as_cvmat(&mut self) -> Result<Mat, Box<dyn Error>> {
189        let cvtype = image_encodings::from_encstr_to_cvtype(&self.encoding)?;
190        
191        let buffer_mut_ptr = match self.data {
192            DataContainer::VecU8(ref mut data) => data.as_mut_ptr() as *mut _,
193            DataContainer::VecU16(ref mut data) => data.as_mut_ptr() as *mut _,
194            _ => Err(format!("Unsupported container type"))?
195        };
196
197        let mat;
198        unsafe {
199            mat = Mat::new_rows_cols_with_data(
200                self.height as i32,
201                self.width as i32,
202                cvtype,
203                buffer_mut_ptr,
204                opencv::core::Mat_AUTO_STEP
205            )?;
206        }
207
208        Ok(mat)
209    }
210
211    /// Returns the immutable internal vector container containing the image data.
212    /// 
213    /// ## Returns
214    /// * `DataContainer` object
215    pub fn as_container(&self) -> &DataContainer {
216        &self.data
217    }
218
219    /// Returns the mutable internal vector container containing the image data.
220    /// 
221    /// ## Returns
222    /// * `DataContainer` object
223    pub fn as_mut_container(&mut self) -> &mut DataContainer {
224        &mut self.data
225    }
226
227    /// Returns the immutable header message used by ROS.
228    /// 
229    /// ## Returns
230    /// * `std_msgs::Header` message
231    pub fn header(&self) -> &Header {
232        &self.header
233    }
234
235    /// Returns the mutable header message used by ROS.
236    /// 
237    /// ## Returns
238    /// * `std_msgs::Header` message
239    pub fn header_mut(&mut self) -> &mut Header {
240        &mut self.header
241    }
242
243    /// Returns the immutable encoding string. Check the supported encodings in the
244    /// `image_encodings` module.
245    /// 
246    /// ## Returns
247    /// * `String` object
248    pub fn encoding(&self) -> &String {
249        &self.encoding
250    }
251
252    /// Returns the immutable width of the image.
253    /// 
254    /// ## Returns
255    /// * `usize` object containing the width
256    pub fn width(&self) -> usize {
257        self.width
258    }
259
260    /// Returns the immutable height of the image.
261    /// 
262    /// ## Returns
263    /// * `usize` object containing the height
264    pub fn height(&self) -> usize {
265        self.height
266    }
267}