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}