cv_convert_fork/
with_opencv_image_0_24.rs

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
7// ImageBuffer -> Mat
8impl<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
20// &ImageBuffer -> Mat
21impl<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
45// &DynamicImage -> Mat
46impl 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
69// DynamicImage -> Mat
70impl 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
77// &Mat -> DynamicImage
78impl 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
107// Mat -> DynamicImage
108impl 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
116// &Mat -> gray ImageBuffer
117impl<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
148// Mat -> gray ImageBuffer
149impl<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
161// &Mat -> rgb ImageBuffer
162impl<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
193// Mat -> rgb ImageBuffer
194impl<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
206// Utility functions
207
208fn 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        // gray
263        {
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        // rgb
278        {
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}