1use image::{
2 Bgr, Bgra, ImageBuffer, ImageError, ImageResult, Luma, LumaA, Pixel, Primitive, Rgb, Rgba,
3};
4use ndarray::ShapeBuilder;
5use ndarray::{Array2, Array3, ArrayView, ArrayViewMut, Ix2, Ix3};
6use std::ops::Deref;
7use std::path::Path;
8
9pub struct NdImage<T>(pub T);
13
14pub type NdGray<'a, A = u8> = ArrayView<'a, A, Ix2>;
15pub type NdGrayMut<'a, A = u8> = ArrayViewMut<'a, A, Ix2>;
16pub type NdColor<'a, A = u8> = ArrayView<'a, A, Ix3>;
17pub type NdColorMut<'a, A = u8> = ArrayViewMut<'a, A, Ix3>;
18
19pub type ImgLuma<'a, A = u8> = ImageBuffer<Luma<A>, &'a [A]>;
20pub type ImgLumaA<'a, A = u8> = ImageBuffer<LumaA<A>, &'a [A]>;
21pub type ImgRgb<'a, A = u8> = ImageBuffer<Rgb<A>, &'a [A]>;
22pub type ImgRgba<'a, A = u8> = ImageBuffer<Rgba<A>, &'a [A]>;
23pub type ImgBgr<'a, A = u8> = ImageBuffer<Bgr<A>, &'a [A]>;
24pub type ImgBgra<'a, A = u8> = ImageBuffer<Bgra<A>, &'a [A]>;
25
26pub enum Colors {
27 Luma,
28 LumaA,
29 Rgb,
30 Rgba,
31 Bgr,
32 Bgra,
33}
34
35pub fn open_gray_image(path: impl AsRef<Path>) -> ImageResult<Array2<u8>> {
38 let image = image::open(path)?;
39 let image = image.to_luma();
40 let image: NdGray = NdImage(&image).into();
41 Ok(image.to_owned())
42}
43
44pub fn open_image(path: impl AsRef<Path>, colors: Colors) -> ImageResult<Array3<u8>> {
47 let image = image::open(path)?;
48 let image = match colors {
49 Colors::Luma => {
50 let image = image.to_luma();
51 let image: NdColor = NdImage(&image).into();
52 image.to_owned()
53 }
54 Colors::LumaA => {
55 let image = image.to_luma_alpha();
56 let image: NdColor = NdImage(&image).into();
57 image.to_owned()
58 }
59 Colors::Rgb => {
60 let image = image.to_rgb();
61 let image: NdColor = NdImage(&image).into();
62 image.to_owned()
63 }
64 Colors::Rgba => {
65 let image = image.to_rgba();
66 let image: NdColor = NdImage(&image).into();
67 image.to_owned()
68 }
69 Colors::Bgr => {
70 let image = image.to_bgr();
71 let image: NdColor = NdImage(&image).into();
72 image.to_owned()
73 }
74 Colors::Bgra => {
75 let image = image.to_bgra();
76 let image: NdColor = NdImage(&image).into();
77 image.to_owned()
78 }
79 };
80 Ok(image)
81}
82
83pub fn save_gray_image(path: impl AsRef<Path>, image: NdGray<'_, u8>) -> ImageResult<()> {
85 let image: Option<ImgLuma> = NdImage(image.view()).into();
86 let image = image.ok_or_else(|| {
87 ImageError::Decoding(image::error::DecodingError::new(
88 image::error::ImageFormatHint::Unknown,
89 "non-contiguous ndarray Array",
90 ))
91 })?;
92 image.save(path)?;
93 Ok(())
94}
95
96pub fn save_image(
98 path: impl AsRef<Path>,
99 image: NdColor<'_, u8>,
100 colors: Colors,
101) -> ImageResult<()> {
102 match colors {
103 Colors::Luma => {
104 let image: Option<ImgLuma> = NdImage(image.view()).into();
105 let image = image.ok_or_else(|| {
106 ImageError::Decoding(image::error::DecodingError::new(
107 image::error::ImageFormatHint::Unknown,
108 "non-contiguous ndarray Array",
109 ))
110 })?;
111 image.save(path)?;
112 }
113 Colors::LumaA => {
114 let image: Option<ImgLumaA> = NdImage(image.view()).into();
115 let image = image.ok_or_else(|| {
116 ImageError::Decoding(image::error::DecodingError::new(
117 image::error::ImageFormatHint::Unknown,
118 "non-contiguous ndarray Array",
119 ))
120 })?;
121 image.save(path)?;
122 }
123 Colors::Rgb => {
124 let image: Option<ImgRgb> = NdImage(image.view()).into();
125 let image = image.ok_or_else(|| {
126 ImageError::Decoding(image::error::DecodingError::new(
127 image::error::ImageFormatHint::Unknown,
128 "non-contiguous ndarray Array",
129 ))
130 })?;
131 image.save(path)?;
132 }
133 Colors::Rgba => {
134 let image: Option<ImgRgba> = NdImage(image.view()).into();
135 let image = image.ok_or_else(|| {
136 ImageError::Decoding(image::error::DecodingError::new(
137 image::error::ImageFormatHint::Unknown,
138 "non-contiguous ndarray Array",
139 ))
140 })?;
141 image.save(path)?;
142 }
143 Colors::Bgr => {
144 let image: Option<ImgBgr> = NdImage(image.view()).into();
145 let image = image.ok_or_else(|| {
146 ImageError::Decoding(image::error::DecodingError::new(
147 image::error::ImageFormatHint::Unknown,
148 "non-contiguous ndarray Array",
149 ))
150 })?;
151 image.save(path)?;
152 }
153 Colors::Bgra => {
154 let image: Option<ImgBgra> = NdImage(image.view()).into();
155 let image = image.ok_or_else(|| {
156 ImageError::Decoding(image::error::DecodingError::new(
157 image::error::ImageFormatHint::Unknown,
158 "non-contiguous ndarray Array",
159 ))
160 })?;
161 image.save(path)?;
162 }
163 }
164 Ok(())
165}
166
167impl<'a, C, A: 'static> Into<NdGray<'a, A>> for NdImage<&'a ImageBuffer<Luma<A>, C>>
169where
170 A: Primitive,
171 C: Deref<Target = [A]> + AsRef<[A]>,
172{
173 fn into(self) -> NdGray<'a, A> {
174 let NdImage(image) = self;
175 let (width, height) = image.dimensions();
176 let (width, height) = (width as usize, height as usize);
177 let slice: &'a [A] = unsafe { std::mem::transmute(image.as_flat_samples().as_slice()) };
178 ArrayView::from_shape((height, width).strides((width, 1)), slice).unwrap()
179 }
180}
181
182impl<'a, C, A: 'static> Into<NdGrayMut<'a, A>> for NdImage<&'a mut ImageBuffer<Luma<A>, C>>
184where
185 A: Primitive,
186 C: Deref<Target = [A]> + AsRef<[A]>,
187{
188 fn into(self) -> NdGrayMut<'a, A> {
189 let NdImage(image) = self;
190 let (width, height) = image.dimensions();
191 let (width, height) = (width as usize, height as usize);
192 #[allow(clippy::transmute_ptr_to_ref)]
193 let slice: &'a mut [A] =
194 unsafe { std::mem::transmute(image.as_flat_samples().as_slice() as *const [A]) };
195 ArrayViewMut::from_shape((height, width).strides((width, 1)), slice).unwrap()
196 }
197}
198
199impl<'a, C, P: 'static, A: 'static> Into<NdColor<'a, A>> for NdImage<&'a ImageBuffer<P, C>>
201where
202 A: Primitive,
203 P: Pixel<Subpixel = A>,
204 C: Deref<Target = [P::Subpixel]> + AsRef<[A]>,
205{
206 fn into(self) -> NdColor<'a, A> {
207 let NdImage(image) = self;
208 let (width, height) = image.dimensions();
209 let (width, height) = (width as usize, height as usize);
210 let channels = P::CHANNEL_COUNT as usize;
211 let slice: &'a [A] = unsafe { std::mem::transmute(image.as_flat_samples().as_slice()) };
212 ArrayView::from_shape(
213 (height, width, channels).strides((width * channels, channels, 1)),
214 slice,
215 )
216 .unwrap()
217 }
218}
219
220impl<'a, C, P: 'static, A: 'static> Into<NdColorMut<'a, A>> for NdImage<&'a mut ImageBuffer<P, C>>
222where
223 A: Primitive,
224 P: Pixel<Subpixel = A>,
225 C: Deref<Target = [P::Subpixel]> + AsRef<[A]>,
226{
227 fn into(self) -> NdColorMut<'a, A> {
228 let NdImage(image) = self;
229 let (width, height) = image.dimensions();
230 let (width, height) = (width as usize, height as usize);
231 let channels = P::CHANNEL_COUNT as usize;
232 #[allow(clippy::transmute_ptr_to_ref)]
233 let slice: &'a mut [A] =
234 unsafe { std::mem::transmute(image.as_flat_samples().as_slice() as *const [A]) };
235 ArrayViewMut::from_shape(
236 (height, width, channels).strides((width * channels, channels, 1)),
237 slice,
238 )
239 .unwrap()
240 }
241}
242
243impl<'a, A: 'static> Into<Option<ImgLuma<'a, A>>> for NdImage<NdGray<'a, A>>
247where
248 A: Primitive,
249{
250 fn into(self) -> Option<ImgLuma<'a, A>> {
251 let NdImage(image) = self;
252 if let [height, width] = *image.shape() {
253 image.to_slice().map(|slice| {
254 ImageBuffer::from_raw(width as u32, height as u32, slice)
255 .expect("failed to create image from slice")
256 })
257 } else {
258 unreachable!("the ndarray had more than 2 dimensions");
259 }
260 }
261}
262
263impl<'a, A: 'static> Into<Option<ImgLuma<'a, A>>> for NdImage<NdColor<'a, A>>
267where
268 A: Primitive,
269{
270 fn into(self) -> Option<ImgLuma<'a, A>> {
271 let NdImage(image) = self;
272 if let [height, width, 1] = *image.shape() {
273 image.to_slice().map(|slice| {
274 ImageBuffer::from_raw(width as u32, height as u32, slice)
275 .expect("failed to create image from raw vec")
276 })
277 } else {
278 None
279 }
280 }
281}
282
283impl<'a, A: 'static> Into<Option<ImgLumaA<'a, A>>> for NdImage<NdColor<'a, A>>
287where
288 A: Primitive,
289{
290 fn into(self) -> Option<ImgLumaA<'a, A>> {
291 let NdImage(image) = self;
292 if let [height, width, 2] = *image.shape() {
293 image.to_slice().map(|slice| {
294 ImageBuffer::from_raw(width as u32, height as u32, slice)
295 .expect("failed to create image from raw vec")
296 })
297 } else {
298 None
299 }
300 }
301}
302
303impl<'a, A: 'static> Into<Option<ImgRgb<'a, A>>> for NdImage<NdColor<'a, A>>
307where
308 A: Primitive,
309{
310 fn into(self) -> Option<ImgRgb<'a, A>> {
311 let NdImage(image) = self;
312 if let [height, width, 3] = *image.shape() {
313 image.to_slice().map(|slice| {
314 ImageBuffer::from_raw(width as u32, height as u32, slice)
315 .expect("failed to create image from raw vec")
316 })
317 } else {
318 None
319 }
320 }
321}
322
323impl<'a, A: 'static> Into<Option<ImgRgba<'a, A>>> for NdImage<NdColor<'a, A>>
327where
328 A: Primitive,
329{
330 fn into(self) -> Option<ImgRgba<'a, A>> {
331 let NdImage(image) = self;
332 if let [height, width, 4] = *image.shape() {
333 image.to_slice().map(|slice| {
334 ImageBuffer::from_raw(width as u32, height as u32, slice)
335 .expect("failed to create image from raw vec")
336 })
337 } else {
338 None
339 }
340 }
341}
342
343impl<'a, A: 'static> Into<Option<ImgBgr<'a, A>>> for NdImage<NdColor<'a, A>>
347where
348 A: Primitive,
349{
350 fn into(self) -> Option<ImgBgr<'a, A>> {
351 let NdImage(image) = self;
352 if let [height, width, 3] = *image.shape() {
353 image.to_slice().map(|slice| {
354 ImageBuffer::from_raw(width as u32, height as u32, slice)
355 .expect("failed to create image from raw vec")
356 })
357 } else {
358 None
359 }
360 }
361}
362
363impl<'a, A: 'static> Into<Option<ImgBgra<'a, A>>> for NdImage<NdColor<'a, A>>
367where
368 A: Primitive,
369{
370 fn into(self) -> Option<ImgBgra<'a, A>> {
371 let NdImage(image) = self;
372 if let [height, width, 4] = *image.shape() {
373 image.to_slice().map(|slice| {
374 ImageBuffer::from_raw(width as u32, height as u32, slice)
375 .expect("failed to create image from raw vec")
376 })
377 } else {
378 None
379 }
380 }
381}