machine_vision_formats/
pixel_format.rs

1//! Implementations of specific pixel formats
2
3// TODO: Check if we should use [PFNC (Pixel Format Naming
4// Convention)](https://www.emva.org/wp-content/uploads/GenICamPixelFormatValues.pdf)
5// names.
6
7// TODO: Check if names from ffmpeg (e.g. `AV_PIX_FMT_YUVA444P`) would be
8// better.
9
10// Also note the formats at
11// https://docs.microsoft.com/en-us/windows/win32/medfound/video-subtype-guids.
12
13#[cfg(not(feature = "std"))]
14extern crate core as std;
15
16use std::convert::TryFrom;
17
18/// This type allows runtime inspection of pixel format.
19#[derive(Debug, Clone, Copy, PartialEq)]
20#[non_exhaustive]
21pub enum PixFmt {
22    Mono8,
23    Mono32f,
24    RGB8,
25    RGBA8,
26    BayerRG8,
27    BayerRG32f,
28    BayerBG8,
29    BayerBG32f,
30    BayerGB8,
31    BayerGB32f,
32    BayerGR8,
33    BayerGR32f,
34    YUV444,
35    YUV422,
36    NV12,
37}
38
39impl PixFmt {
40    /// Convert a runtime variant into a static type.
41    pub fn to_static<FMT: PixelFormat>(&self) -> Option<std::marker::PhantomData<FMT>> {
42        let other = pixfmt::<FMT>();
43        if Ok(self) == other.as_ref() {
44            Some(std::marker::PhantomData)
45        } else {
46            None
47        }
48    }
49    /// The average number of bits per pixel.
50    pub const fn bits_per_pixel(&self) -> u8 {
51        use PixFmt::*;
52        match self {
53            Mono8 => 8,
54            Mono32f => 32,
55            RGB8 => 24,
56            RGBA8 => 32,
57            BayerRG8 => 8,
58            BayerRG32f => 32,
59            BayerBG8 => 8,
60            BayerBG32f => 32,
61            BayerGB8 => 8,
62            BayerGB32f => 32,
63            BayerGR8 => 8,
64            BayerGR32f => 32,
65            YUV444 => 24,
66            YUV422 => 16,
67            NV12 => 12,
68        }
69    }
70    /// The name of the pixel format.
71    pub const fn as_str(&self) -> &'static str {
72        use PixFmt::*;
73        match self {
74            Mono8 => "Mono8",
75            Mono32f => "Mono32f",
76            RGB8 => "RGB8",
77            RGBA8 => "RGBA8",
78            BayerRG8 => "BayerRG8",
79            BayerRG32f => "BayerRG32f",
80            BayerBG8 => "BayerBG8",
81            BayerBG32f => "BayerBG32f",
82            BayerGB8 => "BayerGB8",
83            BayerGB32f => "BayerGB32f",
84            BayerGR8 => "BayerGR8",
85            BayerGR32f => "BayerGR32f",
86            YUV444 => "YUV444",
87            YUV422 => "YUV422",
88            NV12 => "NV12",
89        }
90    }
91}
92
93impl std::fmt::Display for PixFmt {
94    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
95        write!(f, "{}", self.as_str())
96    }
97}
98
99impl std::str::FromStr for PixFmt {
100    type Err = &'static str;
101    fn from_str(instr: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
102        use PixFmt::*;
103        if instr == "Mono8" {
104            Ok(Mono8)
105        } else if instr == "Mono32f" {
106            Ok(Mono32f)
107        } else if instr == "RGB8" {
108            Ok(RGB8)
109        } else if instr == "RGBA8" {
110            Ok(RGBA8)
111        } else if instr == "BayerRG8" {
112            Ok(BayerRG8)
113        } else if instr == "BayerRG32f" {
114            Ok(BayerRG32f)
115        } else if instr == "BayerBG8" {
116            Ok(BayerBG8)
117        } else if instr == "BayerBG32f" {
118            Ok(BayerBG32f)
119        } else if instr == "BayerGB8" {
120            Ok(BayerGB8)
121        } else if instr == "BayerGB32f" {
122            Ok(BayerGB32f)
123        } else if instr == "BayerGR8" {
124            Ok(BayerGR8)
125        } else if instr == "BayerGR32f" {
126            Ok(BayerGR32f)
127        } else if instr == "YUV444" {
128            Ok(YUV444)
129        } else if instr == "YUV422" {
130            Ok(YUV422)
131        } else if instr == "NV12" {
132            Ok(NV12)
133        } else {
134            Err("Cannot parse string")
135        }
136    }
137}
138
139#[test]
140fn test_pixfmt_roundtrip() {
141    use PixFmt::*;
142    let fmts = [
143        Mono8, Mono32f, RGB8, RGBA8, BayerRG8, BayerRG32f, BayerBG8, BayerBG32f, BayerGB8,
144        BayerGB32f, BayerGR8, BayerGR32f, YUV444, YUV422, NV12,
145    ];
146    for fmt in &fmts {
147        let fmt_str = fmt.as_str();
148        dbg!(fmt_str);
149        let fmt2 = std::str::FromStr::from_str(fmt_str).unwrap();
150        assert_eq!(fmt, &fmt2);
151    }
152}
153
154macro_rules! try_downcast {
155    ($name:ident, $orig:expr) => {{
156        if let Some(_) = <dyn std::any::Any>::downcast_ref::<std::marker::PhantomData<$name>>($orig)
157        {
158            return Ok(PixFmt::$name);
159        }
160    }};
161}
162
163impl<FMT> TryFrom<std::marker::PhantomData<FMT>> for PixFmt
164where
165    FMT: PixelFormat,
166{
167    type Error = &'static str;
168
169    fn try_from(orig: std::marker::PhantomData<FMT>) -> Result<PixFmt, Self::Error> {
170        try_downcast!(Mono8, &orig);
171        try_downcast!(Mono32f, &orig);
172        try_downcast!(RGB8, &orig);
173        try_downcast!(RGBA8, &orig);
174        try_downcast!(BayerRG8, &orig);
175        try_downcast!(BayerRG32f, &orig);
176        try_downcast!(BayerBG8, &orig);
177        try_downcast!(BayerBG32f, &orig);
178        try_downcast!(BayerGB8, &orig);
179        try_downcast!(BayerGB32f, &orig);
180        try_downcast!(BayerGR8, &orig);
181        try_downcast!(BayerGR32f, &orig);
182        try_downcast!(YUV444, &orig);
183        try_downcast!(YUV422, &orig);
184        try_downcast!(NV12, &orig);
185        Err("unknown PixelFormat implementation could not be converted to PixFmt")
186    }
187}
188
189/// Convert a compile-time type FMT into a runtime type.
190#[inline]
191pub fn pixfmt<FMT: PixelFormat>() -> Result<PixFmt, &'static str> {
192    use std::convert::TryInto;
193    let concrete: std::marker::PhantomData<FMT> = std::marker::PhantomData;
194    concrete.try_into()
195}
196
197#[test]
198fn test_compile_runtime_roundtrip() {
199    macro_rules! gen_test {
200        ($name:ident) => {{
201            let x = PixFmt::$name;
202            let y = x.to_static::<$name>().unwrap();
203            let z = PixFmt::try_from(y).unwrap();
204            assert_eq!(x, z);
205        }};
206    }
207    gen_test!(Mono8);
208    gen_test!(Mono32f);
209    gen_test!(RGB8);
210    gen_test!(RGBA8);
211    gen_test!(BayerRG8);
212    gen_test!(BayerRG32f);
213    gen_test!(BayerBG8);
214    gen_test!(BayerBG32f);
215    gen_test!(BayerGB8);
216    gen_test!(BayerGB32f);
217    gen_test!(BayerGR8);
218    gen_test!(BayerGR32f);
219    gen_test!(YUV444);
220    gen_test!(YUV422);
221    gen_test!(NV12);
222}
223
224/// Implementations of this trait describe the format of raw image data.
225///
226/// Note that when [const generics for custom
227/// types](https://blog.rust-lang.org/2021/02/26/const-generics-mvp-beta.html#const-generics-for-custom-types)
228/// are introduced to the rust compiler, we intend to switch PixelFormat to use
229/// that feature.
230pub trait PixelFormat: std::any::Any + Clone {}
231
232macro_rules! define_pixel_format {
233    ($name:ident, $comment:literal) => {
234        #[doc = $comment]
235        #[derive(Clone, Debug)]
236        pub struct $name {}
237        impl PixelFormat for $name {}
238    };
239}
240
241define_pixel_format!(
242    Mono8,
243    "Luminance, 1 byte per pixel. Sometimes also called Gray8."
244);
245define_pixel_format!(
246    Mono32f,
247    "Luminance, 32 bytes per pixel, Little-Endian, IEEE-754"
248);
249
250define_pixel_format!(
251    RGB8,
252    "Red, Green, Blue, 1 byte each, total 3 bytes per pixel.
253
254Also sometimes called `RGB8packed`."
255);
256
257define_pixel_format!(
258    RGBA8,
259    "Red, Green, Blue, Alpha, 1 byte each, total 4 bytes per pixel."
260);
261
262define_pixel_format!(BayerRG8, "Bayer Red Green pattern, 1 byte per pixel.");
263define_pixel_format!(BayerRG32f, "Bayer Red Green pattern, 4 bytes per pixel.");
264define_pixel_format!(BayerBG8, "Bayer Blue Green pattern, 1 byte per pixel.");
265define_pixel_format!(BayerBG32f, "Bayer Blue Green pattern, 4 bytes per pixel.");
266define_pixel_format!(BayerGB8, "Bayer Green Blue pattern, 1 byte per pixel.");
267define_pixel_format!(BayerGB32f, "Bayer Green Blue pattern, 4 bytes per pixel.");
268define_pixel_format!(BayerGR8, "Bayer Green Red pattern, 1 byte per pixel.");
269define_pixel_format!(BayerGR32f, "Bayer Green Red pattern, 4 bytes per pixel.");
270define_pixel_format!(YUV444, "YUV 4:4:4 8-bit, total 3 bytes per pixel.");
271define_pixel_format!(YUV422, "YUV 4:2:2 8-bit, total 2 bytes per pixel.");
272define_pixel_format!(NV12, "NV12 format, average 12 bits per pixel");
273
274#[test]
275fn test_debug_types() {
276    let _ = format!("{:?}", BayerRG8 {});
277}