ndarray_vision/core/
colour_models.rs

1use crate::core::traits::*;
2use crate::core::*;
3use ndarray::{prelude::*, s, Data, Zip};
4use num_traits::cast::{FromPrimitive, NumCast};
5use num_traits::{Num, NumAssignOps};
6use std::convert::From;
7use std::fmt::Display;
8
9/// Grayscale image
10#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
11pub struct Gray;
12/// RGB colour as intended by sRGB and standardised in IEC 61966-2-1:1999
13#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
14pub struct RGB;
15/// RGB colour similar to `RGB` type but with an additional channel for alpha
16/// transparency
17#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
18pub struct RGBA;
19/// Hue Saturation Value image
20#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
21pub struct HSV;
22/// Hue Saturation Intensity image
23#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
24pub struct HSI;
25/// Hue Saturation Lightness image
26#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
27pub struct HSL;
28/// YCrCb represents an image as luma, red-difference chroma and blue-difference
29/// chroma.
30#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
31pub struct YCrCb;
32/// CIE XYZ standard - assuming a D50 reference white
33#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
34pub struct CIEXYZ;
35/// CIE LAB (also known as CIE L*a*b* or Lab) a colour model that represents
36/// colour as lightness, and a* and b* as the green-red and blue-yellow colour
37/// differences respectively. It is designed to be representative of human
38/// perception of colour
39#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
40pub struct CIELAB;
41/// Similar to `CIELAB` but has a different representation of colour.
42#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
43pub struct CIELUV;
44/// A single channel image with no colour model specified
45#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
46pub struct Generic1;
47/// A two channel image with no colour model specified
48#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
49pub struct Generic2;
50/// A three channel image with no colour model specified
51#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
52pub struct Generic3;
53/// A four channel image with no colour model specified
54#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
55pub struct Generic4;
56/// A five channel image with no colour model specified
57#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
58pub struct Generic5;
59
60/// ColourModel trait, this trait reports base parameters for different colour
61/// models
62pub trait ColourModel {
63    /// Number of colour channels for a type.
64    fn channels() -> usize {
65        3
66    }
67}
68
69impl RGB {
70    /// Remove the gamma from a normalised channel
71    pub fn remove_gamma(v: f64) -> f64 {
72        if v < 0.04045 {
73            v / 12.92
74        } else {
75            ((v + 0.055) / 1.055).powf(2.4)
76        }
77    }
78
79    /// Apply the gamma to a normalised channel
80    pub fn apply_gamma(v: f64) -> f64 {
81        if v < 0.0031308 {
82            v * 12.92
83        } else {
84            1.055 * v.powf(1.0 / 2.4) - 0.055
85        }
86    }
87}
88
89fn rescale_pixel<T>(x: f64) -> T
90where
91    T: FromPrimitive + Num + NumCast + PixelBound + Display,
92{
93    let tmax = T::max_pixel().to_f64().unwrap_or(0.0f64);
94    let tmin = T::min_pixel().to_f64().unwrap_or(0.0f64);
95
96    let x = x * (tmax - tmin) + tmin;
97
98    T::from_f64(x).unwrap_or_else(T::zero)
99}
100
101/// Converts an RGB pixel to a HSV pixel
102pub fn rgb_to_hsv<T>(r: T, g: T, b: T) -> (T, T, T)
103where
104    T: Copy
105        + Clone
106        + FromPrimitive
107        + Num
108        + NumAssignOps
109        + NumCast
110        + PartialOrd
111        + Display
112        + PixelBound,
113{
114    let r_norm = normalise_pixel_value(r);
115    let g_norm = normalise_pixel_value(g);
116    let b_norm = normalise_pixel_value(b);
117    let cmax = r_norm.max(g_norm.max(b_norm));
118    let cmin = r_norm.min(g_norm.min(b_norm));
119    let delta = cmax - cmin;
120
121    let sat = if cmax > 0.0f64 { delta / cmax } else { 0.0f64 };
122
123    let hue = if delta < std::f64::EPSILON {
124        0.0 // hue is undefined for full black full white
125    } else if cmax <= r_norm {
126        60.0 * (((g_norm - b_norm) / delta) % 6.0)
127    } else if cmax <= g_norm {
128        60.0 * ((b_norm - r_norm) / delta + 2.0)
129    } else {
130        60.0 * ((r_norm - g_norm) / delta + 4.0)
131    };
132    let hue = hue / 360.0f64;
133
134    let hue = rescale_pixel(hue);
135    let sat = rescale_pixel(sat);
136    let val = rescale_pixel(cmax);
137
138    (hue, sat, val)
139}
140
141/// Converts a HSV pixel to a RGB pixel
142pub fn hsv_to_rgb<T>(h: T, s: T, v: T) -> (T, T, T)
143where
144    T: Copy
145        + Clone
146        + FromPrimitive
147        + Num
148        + NumAssignOps
149        + NumCast
150        + PartialOrd
151        + Display
152        + PixelBound,
153{
154    let h_deg = normalise_pixel_value(h) * 360.0f64;
155    let s_norm = normalise_pixel_value(s);
156    let v_norm = normalise_pixel_value(v);
157
158    let c = v_norm * s_norm;
159    let x = c * (1.0f64 - ((h_deg / 60.0f64) % 2.0f64 - 1.0f64).abs());
160    let m = v_norm - c;
161
162    let rgb = if (0.0f64..60.0f64).contains(&h_deg) {
163        (c, x, 0.0f64)
164    } else if (60.0f64..120.0f64).contains(&h_deg) {
165        (x, c, 0.0f64)
166    } else if (120.0f64..180.0f64).contains(&h_deg) {
167        (0.0f64, c, x)
168    } else if (180.0f64..240.0f64).contains(&h_deg) {
169        (0.0f64, x, c)
170    } else if (240.0f64..300.0f64).contains(&h_deg) {
171        (x, 0.0f64, c)
172    } else if (300.0f64..360.0f64).contains(&h_deg) {
173        (c, 0.0f64, x)
174    } else {
175        (0.0f64, 0.0f64, 0.0f64)
176    };
177
178    let red = rescale_pixel(rgb.0 + m);
179    let green = rescale_pixel(rgb.1 + m);
180    let blue = rescale_pixel(rgb.2 + m);
181
182    (red, green, blue)
183}
184
185impl<U, T> From<ImageBase<U, RGB>> for Image<T, HSV>
186where
187    U: Data<Elem = T>,
188    T: Copy
189        + Clone
190        + FromPrimitive
191        + Num
192        + NumAssignOps
193        + NumCast
194        + PartialOrd
195        + Display
196        + PixelBound,
197{
198    fn from(image: ImageBase<U, RGB>) -> Self {
199        let mut res = Array3::<_>::zeros((image.rows(), image.cols(), HSV::channels()));
200        let window = image.data.windows((1, 1, image.channels()));
201
202        Zip::indexed(window).for_each(|(i, j, _), pix| {
203            let red = pix[[0, 0, 0]];
204            let green = pix[[0, 0, 1]];
205            let blue = pix[[0, 0, 2]];
206
207            let (hue, sat, val) = rgb_to_hsv(red, green, blue);
208            res.slice_mut(s![i, j, ..]).assign(&arr1(&[hue, sat, val]));
209        });
210        Self::from_data(res)
211    }
212}
213
214impl<T, U> From<ImageBase<U, HSV>> for Image<T, RGB>
215where
216    U: Data<Elem = T>,
217    T: Copy
218        + Clone
219        + FromPrimitive
220        + Num
221        + NumAssignOps
222        + NumCast
223        + PartialOrd
224        + Display
225        + PixelBound,
226{
227    fn from(image: ImageBase<U, HSV>) -> Self {
228        let mut res = Array3::<T>::zeros((image.rows(), image.cols(), RGB::channels()));
229        let window = image.data.windows((1, 1, image.channels()));
230
231        Zip::indexed(window).for_each(|(i, j, _), pix| {
232            let h = pix[[0, 0, 0]];
233            let s = pix[[0, 0, 1]];
234            let v = pix[[0, 0, 2]];
235
236            let (r, g, b) = hsv_to_rgb(h, s, v);
237            res.slice_mut(s![i, j, ..]).assign(&arr1(&[r, g, b]));
238        });
239        Self::from_data(res)
240    }
241}
242
243impl<T, U> From<ImageBase<U, RGB>> for Image<T, Gray>
244where
245    U: Data<Elem = T>,
246    T: Copy
247        + Clone
248        + FromPrimitive
249        + Num
250        + NumAssignOps
251        + NumCast
252        + PartialOrd
253        + Display
254        + PixelBound,
255{
256    fn from(image: ImageBase<U, RGB>) -> Self {
257        let mut res = Array3::<T>::zeros((image.rows(), image.cols(), Gray::channels()));
258        let window = image.data.windows((1, 1, image.channels()));
259
260        Zip::indexed(window).for_each(|(i, j, _), pix| {
261            let r = normalise_pixel_value(pix[[0, 0, 0]]);
262            let g = normalise_pixel_value(pix[[0, 0, 1]]);
263            let b = normalise_pixel_value(pix[[0, 0, 2]]);
264
265            let gray = (0.3 * r) + (0.59 * g) + (0.11 * b);
266            let gray = rescale_pixel(gray);
267
268            res.slice_mut(s![i, j, ..]).assign(&arr1(&[gray]));
269        });
270        Self::from_data(res)
271    }
272}
273
274impl<T, U> From<ImageBase<U, Gray>> for Image<T, RGB>
275where
276    U: Data<Elem = T>,
277    T: Copy
278        + Clone
279        + FromPrimitive
280        + Num
281        + NumAssignOps
282        + NumCast
283        + PartialOrd
284        + Display
285        + PixelBound,
286{
287    fn from(image: ImageBase<U, Gray>) -> Self {
288        let mut res = Array3::<T>::zeros((image.rows(), image.cols(), RGB::channels()));
289        let window = image.data.windows((1, 1, image.channels()));
290
291        Zip::indexed(window).for_each(|(i, j, _), pix| {
292            let gray = pix[[0, 0, 0]];
293
294            res.slice_mut(s![i, j, ..])
295                .assign(&arr1(&[gray, gray, gray]));
296        });
297        Self::from_data(res)
298    }
299}
300
301impl<T, U> From<ImageBase<U, RGB>> for Image<T, CIEXYZ>
302where
303    U: Data<Elem = T>,
304    T: Copy
305        + Clone
306        + FromPrimitive
307        + Num
308        + NumAssignOps
309        + NumCast
310        + PartialOrd
311        + Display
312        + PixelBound,
313{
314    fn from(image: ImageBase<U, RGB>) -> Self {
315        let mut res = Array3::<T>::zeros((image.rows(), image.cols(), CIEXYZ::channels()));
316        let window = image.data.windows((1, 1, image.channels()));
317
318        let m = arr2(&[
319            [0.4360747, 0.3850649, 0.1430804],
320            [0.2225045, 0.7168786, 0.0606169],
321            [0.0139322, 0.0971045, 0.7141733],
322        ]);
323
324        Zip::indexed(window).for_each(|(i, j, _), pix| {
325            let pixel = pix
326                .index_axis(Axis(0), 0)
327                .index_axis(Axis(0), 0)
328                .mapv(normalise_pixel_value)
329                .mapv(RGB::remove_gamma);
330
331            let pixel = m.dot(&pixel);
332            let pixel = pixel.mapv(rescale_pixel);
333
334            res.slice_mut(s![i, j, ..]).assign(&pixel);
335        });
336        Self::from_data(res)
337    }
338}
339
340impl<T, U> From<ImageBase<U, CIEXYZ>> for Image<T, RGB>
341where
342    U: Data<Elem = T>,
343    T: Copy
344        + Clone
345        + FromPrimitive
346        + Num
347        + NumAssignOps
348        + NumCast
349        + PartialOrd
350        + Display
351        + PixelBound,
352{
353    fn from(image: ImageBase<U, CIEXYZ>) -> Self {
354        let mut res = Array3::<T>::zeros((image.rows(), image.cols(), RGB::channels()));
355        let window = image.data.windows((1, 1, image.channels()));
356
357        let m = arr2(&[
358            [3.1338561, -1.6168667, -0.4906146],
359            [-0.9787684, 1.9161415, 0.0334540],
360            [0.0719453, -0.2289914, 1.4052427],
361        ]);
362
363        Zip::indexed(window).for_each(|(i, j, _), pix| {
364            let pixel = pix
365                .index_axis(Axis(0), 0)
366                .index_axis(Axis(0), 0)
367                .mapv(normalise_pixel_value);
368
369            let pixel = m.dot(&pixel);
370
371            let pixel = pixel.mapv(RGB::apply_gamma).mapv(rescale_pixel);
372
373            res.slice_mut(s![i, j, ..]).assign(&pixel);
374        });
375        Self::from_data(res)
376    }
377}
378
379impl<T> From<ImageBase<T, Generic3>> for ImageBase<T, RGB>
380where
381    T: Data,
382{
383    fn from(image: ImageBase<T, Generic3>) -> Self {
384        image.into_type_raw()
385    }
386}
387
388impl<T> From<ImageBase<T, Generic3>> for ImageBase<T, HSV>
389where
390    T: Data,
391{
392    fn from(image: ImageBase<T, Generic3>) -> Self {
393        Self::from_data(image.data)
394    }
395}
396
397impl<T> From<ImageBase<T, Generic3>> for ImageBase<T, HSI>
398where
399    T: Data,
400{
401    fn from(image: ImageBase<T, Generic3>) -> Self {
402        Self::from_data(image.data)
403    }
404}
405
406impl<T> From<ImageBase<T, Generic3>> for ImageBase<T, HSL>
407where
408    T: Data,
409{
410    fn from(image: ImageBase<T, Generic3>) -> Self {
411        Self::from_data(image.data)
412    }
413}
414
415impl<T> From<ImageBase<T, Generic3>> for ImageBase<T, YCrCb>
416where
417    T: Data,
418{
419    fn from(image: ImageBase<T, Generic3>) -> Self {
420        Self::from_data(image.data)
421    }
422}
423
424impl<T> From<ImageBase<T, Generic3>> for ImageBase<T, CIEXYZ>
425where
426    T: Data,
427{
428    fn from(image: ImageBase<T, Generic3>) -> Self {
429        Self::from_data(image.data)
430    }
431}
432
433impl<T> From<ImageBase<T, Generic3>> for ImageBase<T, CIELAB>
434where
435    T: Data,
436{
437    fn from(image: ImageBase<T, Generic3>) -> Self {
438        Self::from_data(image.data)
439    }
440}
441
442impl<T> From<ImageBase<T, Generic3>> for ImageBase<T, CIELUV>
443where
444    T: Data,
445{
446    fn from(image: ImageBase<T, Generic3>) -> Self {
447        Self::from_data(image.data)
448    }
449}
450
451impl<T> From<ImageBase<T, Generic1>> for ImageBase<T, Gray>
452where
453    T: Data,
454{
455    fn from(image: ImageBase<T, Generic1>) -> Self {
456        Self::from_data(image.data)
457    }
458}
459
460impl<T> From<ImageBase<T, Generic4>> for ImageBase<T, RGBA>
461where
462    T: Data,
463{
464    fn from(image: ImageBase<T, Generic4>) -> Self {
465        Self::from_data(image.data)
466    }
467}
468
469impl<T> From<ImageBase<T, RGB>> for ImageBase<T, Generic3>
470where
471    T: Data,
472{
473    fn from(image: ImageBase<T, RGB>) -> Self {
474        Self::from_data(image.data)
475    }
476}
477
478impl<T> From<ImageBase<T, HSV>> for ImageBase<T, Generic3>
479where
480    T: Data,
481{
482    fn from(image: ImageBase<T, HSV>) -> Self {
483        Self::from_data(image.data)
484    }
485}
486
487impl<T> From<ImageBase<T, HSI>> for ImageBase<T, Generic3>
488where
489    T: Data,
490{
491    fn from(image: ImageBase<T, HSI>) -> Self {
492        Self::from_data(image.data)
493    }
494}
495
496impl<T> From<ImageBase<T, HSL>> for ImageBase<T, Generic3>
497where
498    T: Data,
499{
500    fn from(image: ImageBase<T, HSL>) -> Self {
501        Self::from_data(image.data)
502    }
503}
504
505impl<T> From<ImageBase<T, YCrCb>> for ImageBase<T, Generic3>
506where
507    T: Data,
508{
509    fn from(image: ImageBase<T, YCrCb>) -> Self {
510        Self::from_data(image.data)
511    }
512}
513
514impl<T> From<ImageBase<T, CIEXYZ>> for ImageBase<T, Generic3>
515where
516    T: Data,
517{
518    fn from(image: ImageBase<T, CIEXYZ>) -> Self {
519        Self::from_data(image.data)
520    }
521}
522
523impl<T> From<ImageBase<T, CIELAB>> for ImageBase<T, Generic3>
524where
525    T: Data,
526{
527    fn from(image: ImageBase<T, CIELAB>) -> Self {
528        Self::from_data(image.data)
529    }
530}
531
532impl<T> From<ImageBase<T, CIELUV>> for ImageBase<T, Generic3>
533where
534    T: Data,
535{
536    fn from(image: ImageBase<T, CIELUV>) -> Self {
537        Self::from_data(image.data)
538    }
539}
540
541impl<T> From<ImageBase<T, RGBA>> for ImageBase<T, Generic4>
542where
543    T: Data,
544{
545    fn from(image: ImageBase<T, RGBA>) -> Self {
546        Self::from_data(image.data)
547    }
548}
549
550impl<T> From<ImageBase<T, Gray>> for ImageBase<T, Generic1>
551where
552    T: Data,
553{
554    fn from(image: ImageBase<T, Gray>) -> Self {
555        Self::from_data(image.data)
556    }
557}
558
559impl<T, U> From<ImageBase<U, Generic5>> for Image<T, Generic4>
560where
561    U: Data<Elem = T>,
562    T: Copy,
563{
564    fn from(image: ImageBase<U, Generic5>) -> Self {
565        let shape = (image.rows(), image.cols(), Generic4::channels());
566        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
567        Self::from_data(data)
568    }
569}
570
571impl<T, U> From<ImageBase<U, Generic5>> for Image<T, Generic3>
572where
573    U: Data<Elem = T>,
574    T: Copy,
575{
576    fn from(image: ImageBase<U, Generic5>) -> Self {
577        let shape = (image.rows(), image.cols(), Generic3::channels());
578        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
579        Self::from_data(data)
580    }
581}
582
583impl<T, U> From<ImageBase<U, Generic5>> for Image<T, Generic2>
584where
585    U: Data<Elem = T>,
586    T: Copy,
587{
588    fn from(image: ImageBase<U, Generic5>) -> Self {
589        let shape = (image.rows(), image.cols(), Generic2::channels());
590        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
591        Self::from_data(data)
592    }
593}
594
595impl<T, U> From<ImageBase<U, Generic5>> for Image<T, Generic1>
596where
597    U: Data<Elem = T>,
598    T: Copy,
599{
600    fn from(image: ImageBase<U, Generic5>) -> Self {
601        let shape = (image.rows(), image.cols(), Generic1::channels());
602        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
603        Self::from_data(data)
604    }
605}
606
607impl<T, U> From<ImageBase<U, Generic4>> for Image<T, Generic3>
608where
609    U: Data<Elem = T>,
610    T: Copy,
611{
612    fn from(image: ImageBase<U, Generic4>) -> Self {
613        let shape = (image.rows(), image.cols(), Generic3::channels());
614        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
615        Self::from_data(data)
616    }
617}
618
619impl<T, U> From<ImageBase<U, Generic4>> for Image<T, Generic2>
620where
621    U: Data<Elem = T>,
622    T: Copy,
623{
624    fn from(image: ImageBase<U, Generic4>) -> Self {
625        let shape = (image.rows(), image.cols(), Generic2::channels());
626        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
627        Self::from_data(data)
628    }
629}
630
631impl<T, U> From<ImageBase<U, Generic4>> for Image<T, Generic1>
632where
633    U: Data<Elem = T>,
634    T: Copy,
635{
636    fn from(image: ImageBase<U, Generic4>) -> Self {
637        let shape = (image.rows(), image.cols(), Generic1::channels());
638        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
639        Self::from_data(data)
640    }
641}
642
643impl<T, U> From<ImageBase<U, Generic3>> for Image<T, Generic2>
644where
645    U: Data<Elem = T>,
646    T: Copy,
647{
648    fn from(image: ImageBase<U, Generic3>) -> Self {
649        let shape = (image.rows(), image.cols(), Generic2::channels());
650        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
651        Self::from_data(data)
652    }
653}
654
655impl<T, U> From<ImageBase<U, Generic3>> for Image<T, Generic1>
656where
657    U: Data<Elem = T>,
658    T: Copy,
659{
660    fn from(image: ImageBase<U, Generic3>) -> Self {
661        let shape = (image.rows(), image.cols(), Generic1::channels());
662        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
663        Self::from_data(data)
664    }
665}
666
667impl<T, U> From<ImageBase<U, Generic2>> for Image<T, Generic1>
668where
669    U: Data<Elem = T>,
670    T: Copy,
671{
672    fn from(image: ImageBase<U, Generic2>) -> Self {
673        let shape = (image.rows(), image.cols(), Generic1::channels());
674        let data = Array3::from_shape_fn(shape, |(i, j, k)| image.data[[i, j, k]]);
675        Self::from_data(data)
676    }
677}
678
679impl<T, U> From<ImageBase<U, Generic1>> for Image<T, Generic5>
680where
681    U: Data<Elem = T>,
682    T: Copy + Num,
683{
684    fn from(image: ImageBase<U, Generic1>) -> Self {
685        let shape = (image.rows(), image.cols(), Generic5::channels());
686        let mut data = Array3::zeros(shape);
687        data.slice_mut(s![.., .., 0..Generic1::channels()])
688            .assign(&image.data);
689        Self::from_data(data)
690    }
691}
692
693impl<T, U> From<ImageBase<U, Generic2>> for Image<T, Generic5>
694where
695    U: Data<Elem = T>,
696    T: Copy + Num,
697{
698    fn from(image: ImageBase<U, Generic2>) -> Self {
699        let shape = (image.rows(), image.cols(), Generic5::channels());
700        let mut data = Array3::zeros(shape);
701        data.slice_mut(s![.., .., 0..Generic2::channels()])
702            .assign(&image.data);
703        Self::from_data(data)
704    }
705}
706
707impl<T, U> From<ImageBase<U, Generic3>> for Image<T, Generic5>
708where
709    U: Data<Elem = T>,
710    T: Copy + Num,
711{
712    fn from(image: ImageBase<U, Generic3>) -> Self {
713        let shape = (image.rows(), image.cols(), Generic5::channels());
714        let mut data = Array3::zeros(shape);
715        data.slice_mut(s![.., .., 0..Generic3::channels()])
716            .assign(&image.data);
717        Self::from_data(data)
718    }
719}
720
721impl<T, U> From<ImageBase<U, Generic4>> for Image<T, Generic5>
722where
723    U: Data<Elem = T>,
724    T: Copy + Num,
725{
726    fn from(image: ImageBase<U, Generic4>) -> Self {
727        let shape = (image.rows(), image.cols(), Generic5::channels());
728        let mut data = Array3::zeros(shape);
729        data.slice_mut(s![.., .., 0..Generic4::channels()])
730            .assign(&image.data);
731        Self::from_data(data)
732    }
733}
734
735impl<T, U> From<ImageBase<U, Generic1>> for Image<T, Generic4>
736where
737    U: Data<Elem = T>,
738    T: Copy + Num,
739{
740    fn from(image: ImageBase<U, Generic1>) -> Self {
741        let shape = (image.rows(), image.cols(), Generic4::channels());
742        let mut data = Array3::zeros(shape);
743        data.slice_mut(s![.., .., 0..Generic1::channels()])
744            .assign(&image.data);
745        Self::from_data(data)
746    }
747}
748
749impl<T, U> From<ImageBase<U, Generic2>> for Image<T, Generic4>
750where
751    U: Data<Elem = T>,
752    T: Copy + Num,
753{
754    fn from(image: ImageBase<U, Generic2>) -> Self {
755        let shape = (image.rows(), image.cols(), Generic4::channels());
756        let mut data = Array3::zeros(shape);
757        data.slice_mut(s![.., .., 0..Generic2::channels()])
758            .assign(&image.data);
759        Self::from_data(data)
760    }
761}
762
763impl<T, U> From<ImageBase<U, Generic3>> for Image<T, Generic4>
764where
765    U: Data<Elem = T>,
766    T: Copy + Num,
767{
768    fn from(image: ImageBase<U, Generic3>) -> Self {
769        let shape = (image.rows(), image.cols(), Generic4::channels());
770        let mut data = Array3::zeros(shape);
771        data.slice_mut(s![.., .., 0..Generic3::channels()])
772            .assign(&image.data);
773        Self::from_data(data)
774    }
775}
776
777impl<T, U> From<ImageBase<U, Generic1>> for Image<T, Generic3>
778where
779    U: Data<Elem = T>,
780    T: Copy + Num,
781{
782    fn from(image: ImageBase<U, Generic1>) -> Self {
783        let shape = (image.rows(), image.cols(), Generic3::channels());
784        let mut data = Array3::zeros(shape);
785        data.slice_mut(s![.., .., 0..Generic1::channels()])
786            .assign(&image.data);
787        Self::from_data(data)
788    }
789}
790
791impl<T, U> From<ImageBase<U, Generic2>> for Image<T, Generic3>
792where
793    U: Data<Elem = T>,
794    T: Copy + Num,
795{
796    fn from(image: ImageBase<U, Generic2>) -> Self {
797        let shape = (image.rows(), image.cols(), Generic3::channels());
798        let mut data = Array3::zeros(shape);
799        data.slice_mut(s![.., .., 0..Generic2::channels()])
800            .assign(&image.data);
801        Self::from_data(data)
802    }
803}
804
805impl<T, U> From<ImageBase<U, Generic1>> for Image<T, Generic2>
806where
807    U: Data<Elem = T>,
808    T: Copy + Num,
809{
810    fn from(image: ImageBase<U, Generic1>) -> Self {
811        let shape = (image.rows(), image.cols(), Generic2::channels());
812        let mut data = Array3::zeros(shape);
813        data.slice_mut(s![.., .., 0..Generic1::channels()])
814            .assign(&image.data);
815        Self::from_data(data)
816    }
817}
818
819impl ColourModel for RGB {}
820impl ColourModel for HSV {}
821impl ColourModel for HSI {}
822impl ColourModel for HSL {}
823impl ColourModel for YCrCb {}
824impl ColourModel for CIEXYZ {}
825impl ColourModel for CIELAB {}
826impl ColourModel for CIELUV {}
827
828impl ColourModel for Gray {
829    fn channels() -> usize {
830        1
831    }
832}
833
834impl ColourModel for Generic1 {
835    fn channels() -> usize {
836        1
837    }
838}
839impl ColourModel for Generic2 {
840    fn channels() -> usize {
841        2
842    }
843}
844impl ColourModel for Generic3 {
845    fn channels() -> usize {
846        3
847    }
848}
849impl ColourModel for Generic4 {
850    fn channels() -> usize {
851        4
852    }
853}
854impl ColourModel for Generic5 {
855    fn channels() -> usize {
856        5
857    }
858}
859
860impl ColourModel for RGBA {
861    fn channels() -> usize {
862        4
863    }
864}
865
866#[cfg(test)]
867mod tests {
868    use super::*;
869    use ndarray::s;
870    use ndarray_rand::RandomExt;
871    use ndarray_stats::QuantileExt;
872    use rand::distributions::Uniform;
873
874    #[test]
875    fn basic_rgb_hsv_check() {
876        let mut i = Image::<u8, RGB>::new(1, 2);
877        i.pixel_mut(0, 0).assign(&arr1(&[0, 0, 0]));
878        i.pixel_mut(0, 1).assign(&arr1(&[255, 255, 255]));
879
880        let hsv = Image::<u8, HSV>::from(i.clone());
881
882        assert_eq!(hsv.pixel(0, 0)[[2]], 0);
883        assert_eq!(hsv.pixel(0, 1)[[2]], 255);
884
885        let rgb = Image::<u8, RGB>::from(hsv);
886        assert_eq!(i, rgb);
887    }
888
889    #[test]
890    fn gray_to_rgb_test() {
891        let mut image = Image::<u8, Gray>::new(480, 640);
892        let new_data = Array3::<u8>::random(image.data.dim(), Uniform::new(0, 255));
893        image.data = new_data;
894
895        let rgb = Image::<u8, RGB>::from(image.clone());
896        let slice_2d = image.data.slice(s![.., .., 0]);
897
898        assert_eq!(slice_2d, rgb.data.slice(s![.., .., 0]));
899        assert_eq!(slice_2d, rgb.data.slice(s![.., .., 1]));
900        assert_eq!(slice_2d, rgb.data.slice(s![.., .., 2]));
901    }
902
903    #[test]
904    fn rgb_to_gray_basic() {
905        // Check white, black, red, green, blue
906        let data = vec![255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255];
907        let image = Image::<u8, RGB>::from_shape_data(1, 5, data);
908
909        let gray = Image::<u8, Gray>::from(image);
910
911        // take standard 0.3 0.59 0.11 values and assume truncation
912        let expected = vec![255, 0, 77, 150, 28];
913
914        for (act, exp) in gray.data.iter().zip(expected.iter()) {
915            let delta = (*act as i16 - *exp as i16).abs();
916            assert!(delta < 2);
917        }
918    }
919
920    #[test]
921    fn basic_xyz_rgb_checks() {
922        let mut image = Image::<f32, RGB>::new(100, 100);
923        let new_data = Array3::<f32>::random(image.data.dim(), Uniform::new(0.0, 1.0));
924        image.data = new_data;
925
926        let xyz = Image::<f32, CIEXYZ>::from(image.clone());
927
928        let rgb_restored = Image::<f32, RGB>::from(xyz);
929
930        let mut delta = image.data - rgb_restored.data;
931        delta.mapv_inplace(|x| x.abs());
932
933        // 0.5% error in RGB -> XYZ -> RGB
934        assert!(*delta.max().unwrap() * 100.0 < 0.5);
935    }
936
937    #[test]
938    fn generic3_checks() {
939        let mut image = Image::<f32, RGB>::new(100, 100);
940        let new_data = Array3::<f32>::random(image.data.dim(), Uniform::new(0.0, 1.0));
941        image.data = new_data;
942        let gen = Image::<_, Generic3>::from(image.clone());
943        // Normally don't check floats with equality but data shouldn't have
944        // changed
945        assert_eq!(gen.data, image.data);
946
947        let hsv = Image::<_, HSV>::from(gen);
948        assert_eq!(image.data, hsv.data);
949
950        let gen = Image::<_, Generic3>::from(hsv);
951        assert_eq!(image.data, gen.data);
952
953        let hsi = Image::<_, HSI>::from(gen);
954        assert_eq!(image.data, hsi.data);
955
956        let gen = Image::<_, Generic3>::from(hsi);
957        assert_eq!(image.data, gen.data);
958
959        let hsl = Image::<_, HSL>::from(gen);
960        assert_eq!(image.data, hsl.data);
961
962        let gen = Image::<_, Generic3>::from(hsl);
963        assert_eq!(image.data, gen.data);
964
965        let ycrcb = Image::<_, YCrCb>::from(gen);
966        assert_eq!(image.data, ycrcb.data);
967
968        let gen = Image::<_, Generic3>::from(ycrcb);
969        assert_eq!(image.data, gen.data);
970
971        let ciexyz = Image::<_, CIEXYZ>::from(gen);
972        assert_eq!(image.data, ciexyz.data);
973
974        let gen = Image::<_, Generic3>::from(ciexyz);
975        assert_eq!(image.data, gen.data);
976
977        let cieluv = Image::<_, CIELUV>::from(gen);
978        assert_eq!(image.data, cieluv.data);
979
980        let gen = Image::<_, Generic3>::from(cieluv);
981        assert_eq!(image.data, gen.data);
982
983        let cielab = Image::<_, CIELAB>::from(gen);
984        assert_eq!(image.data, cielab.data);
985    }
986
987    #[test]
988    fn generic_model_expand() {
989        let mut image = Image::<f32, Generic1>::new(100, 100);
990        let new_data = Array3::<f32>::random(image.data.dim(), Uniform::new(0.0, 1.0));
991        image.data = new_data;
992
993        let large = Image::<_, Generic5>::from(image.clone());
994
995        assert_eq!(large.channels(), Generic5::channels());
996
997        assert_eq!(
998            large.data.slice(s![.., .., 0]),
999            image.data.slice(s![.., .., 0])
1000        );
1001        let zeros = Array2::<f32>::zeros((100, 100));
1002        for i in 1..Generic5::channels() {
1003            assert_eq!(large.data.slice(s![.., .., i]), zeros);
1004        }
1005    }
1006}