1use crate::image;
2use crate::opencv::{core as cv, prelude::*};
3use crate::with_opencv::MatExt;
4use crate::{common::*, OpenCvElement, TryFromCv, TryIntoCv};
5use std::ops::Deref;
6
7impl<P, Container> TryFromCv<image::ImageBuffer<P, Container>> for cv::Mat
9where
10 P: image::Pixel,
11 P::Subpixel: OpenCvElement,
12 Container: Deref<Target = [P::Subpixel]> + Clone,
13{
14 type Error = Error;
15 fn try_from_cv(from: image::ImageBuffer<P, Container>) -> Result<Self, Self::Error> {
16 (&from).try_into_cv()
17 }
18}
19
20impl<P, Container> TryFromCv<&image::ImageBuffer<P, Container>> for cv::Mat
22where
23 P: image::Pixel,
24 P::Subpixel: OpenCvElement,
25 Container: Deref<Target = [P::Subpixel]> + Clone,
26{
27 type Error = Error;
28 fn try_from_cv(from: &image::ImageBuffer<P, Container>) -> Result<Self, Self::Error> {
29 let (width, height) = from.dimensions();
30 let cv_type = cv::CV_MAKETYPE(P::Subpixel::DEPTH, P::CHANNEL_COUNT as i32);
31 let mat = unsafe {
32 cv::Mat::new_rows_cols_with_data(
33 height as i32,
34 width as i32,
35 cv_type,
36 from.as_ptr() as *mut _,
37 cv::Mat_AUTO_STEP,
38 )?
39 .try_clone()?
40 };
41 Ok(mat)
42 }
43}
44
45impl TryFromCv<&image::DynamicImage> for cv::Mat {
47 type Error = Error;
48
49 fn try_from_cv(from: &image::DynamicImage) -> Result<Self, Self::Error> {
50 use image::DynamicImage as D;
51
52 let mat = match from {
53 D::ImageLuma8(image) => image.try_into_cv()?,
54 D::ImageLumaA8(image) => image.try_into_cv()?,
55 D::ImageRgb8(image) => image.try_into_cv()?,
56 D::ImageRgba8(image) => image.try_into_cv()?,
57 D::ImageLuma16(image) => image.try_into_cv()?,
58 D::ImageLumaA16(image) => image.try_into_cv()?,
59 D::ImageRgb16(image) => image.try_into_cv()?,
60 D::ImageRgba16(image) => image.try_into_cv()?,
61 D::ImageRgb32F(image) => image.try_into_cv()?,
62 D::ImageRgba32F(image) => image.try_into_cv()?,
63 image => bail!("the color type {:?} is not supported", image.color()),
64 };
65 Ok(mat)
66 }
67}
68
69impl TryFromCv<image::DynamicImage> for cv::Mat {
71 type Error = Error;
72 fn try_from_cv(from: image::DynamicImage) -> Result<Self, Self::Error> {
73 (&from).try_into_cv()
74 }
75}
76
77impl TryFromCv<&cv::Mat> for image::DynamicImage {
79 type Error = Error;
80
81 fn try_from_cv(from: &cv::Mat) -> Result<Self, Self::Error> {
82 let rows = from.rows();
83 let cols = from.cols();
84 ensure!(
85 rows != -1 && cols != -1,
86 "Mat with more than 2 dimensions is not supported."
87 );
88
89 let depth = from.depth();
90 let n_channels = from.channels();
91 let width = cols as u32;
92 let height = rows as u32;
93
94 let image: image::DynamicImage = match (depth, n_channels) {
95 (cv::CV_8U, 1) => mat_to_image_buffer_gray::<u8>(from, width, height).into(),
96 (cv::CV_16U, 1) => mat_to_image_buffer_gray::<u16>(from, width, height).into(),
97 (cv::CV_8U, 3) => mat_to_image_buffer_rgb::<u8>(from, width, height).into(),
98 (cv::CV_16U, 3) => mat_to_image_buffer_rgb::<u16>(from, width, height).into(),
99 (cv::CV_32F, 3) => mat_to_image_buffer_rgb::<f32>(from, width, height).into(),
100 _ => bail!("Mat of type {} is not supported", from.type_name()),
101 };
102
103 Ok(image)
104 }
105}
106
107impl TryFromCv<cv::Mat> for image::DynamicImage {
109 type Error = Error;
110
111 fn try_from_cv(from: cv::Mat) -> Result<Self, Self::Error> {
112 (&from).try_into_cv()
113 }
114}
115
116impl<T> TryFromCv<&cv::Mat> for image::ImageBuffer<image::Luma<T>, Vec<T>>
118where
119 image::Luma<T>: image::Pixel,
120 T: OpenCvElement + image::Primitive + DataType,
121{
122 type Error = Error;
123
124 fn try_from_cv(from: &cv::Mat) -> Result<Self, Self::Error> {
125 let rows = from.rows();
126 let cols = from.cols();
127 ensure!(
128 rows != -1 && cols != -1,
129 "Mat with more than 2 dimensions is not supported."
130 );
131
132 let depth = from.depth();
133 let n_channels = from.channels();
134 let width = cols as u32;
135 let height = rows as u32;
136
137 ensure!(
138 n_channels == 1,
139 "Unable to convert a multi-channel Mat to a gray image"
140 );
141 ensure!(depth == T::DEPTH, "Subpixel type is not supported");
142
143 let image = mat_to_image_buffer_gray::<T>(from, width, height);
144 Ok(image)
145 }
146}
147
148impl<T> TryFromCv<cv::Mat> for image::ImageBuffer<image::Luma<T>, Vec<T>>
150where
151 image::Luma<T>: image::Pixel,
152 T: OpenCvElement + image::Primitive + DataType,
153{
154 type Error = Error;
155
156 fn try_from_cv(from: cv::Mat) -> Result<Self, Self::Error> {
157 (&from).try_into_cv()
158 }
159}
160
161impl<T> TryFromCv<&cv::Mat> for image::ImageBuffer<image::Rgb<T>, Vec<T>>
163where
164 image::Rgb<T>: image::Pixel<Subpixel = T>,
165 T: OpenCvElement + image::Primitive + DataType,
166{
167 type Error = Error;
168
169 fn try_from_cv(from: &cv::Mat) -> Result<Self, Self::Error> {
170 let rows = from.rows();
171 let cols = from.cols();
172 ensure!(
173 rows != -1 && cols != -1,
174 "Mat with more than 2 dimensions is not supported."
175 );
176
177 let depth = from.depth();
178 let n_channels = from.channels();
179 let width = cols as u32;
180 let height = rows as u32;
181
182 ensure!(
183 n_channels == 3,
184 "Expect 3 channels, but get {n_channels} channels"
185 );
186 ensure!(depth == T::DEPTH, "Subpixel type is not supported");
187
188 let image = mat_to_image_buffer_rgb::<T>(from, width, height);
189 Ok(image)
190 }
191}
192
193impl<T> TryFromCv<cv::Mat> for image::ImageBuffer<image::Rgb<T>, Vec<T>>
195where
196 image::Rgb<T>: image::Pixel<Subpixel = T>,
197 T: OpenCvElement + image::Primitive + DataType,
198{
199 type Error = Error;
200
201 fn try_from_cv(from: cv::Mat) -> Result<Self, Self::Error> {
202 (&from).try_into_cv()
203 }
204}
205
206fn mat_to_image_buffer_gray<T>(
209 mat: &cv::Mat,
210 width: u32,
211 height: u32,
212) -> image::ImageBuffer<image::Luma<T>, Vec<T>>
213where
214 T: image::Primitive + OpenCvElement + DataType,
215{
216 type Image<T> = image::ImageBuffer<image::Luma<T>, Vec<T>>;
217
218 match mat.as_slice::<T>() {
219 Ok(slice) => Image::<T>::from_vec(width, height, slice.to_vec()).unwrap(),
220 Err(_) => Image::<T>::from_fn(width, height, |col, row| {
221 let pixel: T = *mat.at_2d(row as i32, col as i32).unwrap();
222 image::Luma([pixel])
223 }),
224 }
225}
226
227fn mat_to_image_buffer_rgb<T>(
228 mat: &cv::Mat,
229 width: u32,
230 height: u32,
231) -> image::ImageBuffer<image::Rgb<T>, Vec<T>>
232where
233 T: image::Primitive + OpenCvElement + DataType,
234 image::Rgb<T>: image::Pixel<Subpixel = T>,
235{
236 type Image<T> = image::ImageBuffer<image::Rgb<T>, Vec<T>>;
237
238 match mat.as_slice::<T>() {
239 Ok(slice) => Image::<T>::from_vec(width, height, slice.to_vec()).unwrap(),
240 Err(_) => Image::<T>::from_fn(width, height, |col, row| {
241 let cv::Point3_::<T> { x, y, z } = *mat.at_2d(row as i32, col as i32).unwrap();
242 image::Rgb([x, y, z])
243 }),
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use crate::image;
250 use crate::opencv::{core as cv, prelude::*};
251 use crate::with_opencv::MatExt;
252 use crate::TryIntoCv;
253 use anyhow::ensure;
254 use anyhow::Result;
255 use itertools::iproduct;
256
257 #[test]
258 fn convert_opencv_image() -> Result<()> {
259 const WIDTH: usize = 250;
260 const HEIGHT: usize = 100;
261
262 {
264 let mat = Mat::new_randn_2d(HEIGHT as i32, WIDTH as i32, cv::CV_8UC1)?;
265 let image: image::GrayImage = (&mat).try_into_cv()?;
266 let mat2: Mat = (&image).try_into_cv()?;
267
268 iproduct!(0..HEIGHT, 0..WIDTH).try_for_each(|(row, col)| {
269 let p1: u8 = *mat.at_2d(row as i32, col as i32)?;
270 let p2 = image[(col as u32, row as u32)].0[0];
271 let p3: u8 = *mat2.at_2d(row as i32, col as i32)?;
272 ensure!(p1 == p2 && p1 == p3);
273 anyhow::Ok(())
274 })?;
275 }
276
277 {
279 let mat = Mat::new_randn_2d(HEIGHT as i32, WIDTH as i32, cv::CV_8UC3)?;
280 let image: image::RgbImage = (&mat).try_into_cv()?;
281 let mat2: Mat = (&image).try_into_cv()?;
282
283 iproduct!(0..HEIGHT, 0..WIDTH).try_for_each(|(row, col)| {
284 let p1: cv::Point3_<u8> = *mat.at_2d(row as i32, col as i32)?;
285 let p2: image::Rgb<u8> = image[(col as u32, row as u32)];
286 let p3: cv::Point3_<u8> = *mat2.at_2d(row as i32, col as i32)?;
287 ensure!(p1 == p3);
288 ensure!({
289 let a1 = {
290 let cv::Point3_ { x, y, z } = p1;
291 [x, y, z]
292 };
293 let a2 = p2.0;
294 a1 == a2
295 });
296 anyhow::Ok(())
297 })?;
298 }
299
300 Ok(())
301 }
302}