Skip to main content

libblackbody/
thermogram.rs

1use ndarray::*;
2use std::fs::File;
3use std::path::Path;
4use std::io::Read;
5
6use crate::*;
7
8/// The wrapper enum through which most processing of thermograms is recommend to
9/// happen. Use `Thermogram::from_file()` to read files.
10///
11/// The enum itself, and all thermogram formats it wraps, implement `ThermogramTrait`. Below
12/// several of its methods are listed. Consult the documentation of `ThermogramTrait` for
13/// more details.
14///
15/// ```rust
16/// pub trait ThermogramTrait {
17///     fn thermal(&self) -> &Array<f32, Ix2>;  // Extract the thermal data
18///     fn optical(&self) -> &Array<u8, Ix3>>;  // Extract embedded photos, if present
19///     fn identifier(&self) -> &str;  // A uniquely identifying string for this thermogram
20///     fn render(&self min_temp: f32, max_temp: f32, palette: [[f32; 3]; 256]) -> Array<u8, Ix3>;  // Thermal data render using the given palette
21///     fn render_defaults(&self) -> Array<u8, Ix3>;  // Thermal data rendered using the minimum and maximum thermal value and the `palette::TURBO` palette.
22///     fn thermal_shape(&self) -> [usize; 2];  // The [height, width] of the thermal data
23///     fn normalized_minmax(&self) -> Array<f32, Ix2>;  // Thermal data normalized to lie in the range 0.0..=1.0
24/// }
25/// ```
26#[derive(Clone, Debug)]
27pub enum Thermogram {
28    Flir(FlirThermogram),
29    Tiff(TiffThermogram),
30}
31
32impl Thermogram {
33    /// Tries to recognize the file type based on its magic number and return a `Thermogram`.
34    ///
35    /// # Arguments
36    /// * `path` - A path to a thermogram file.
37    ///
38    /// # Returns
39    /// In case of success an `Some<Thermogram>`, otherwise `None`. A `Thermogam` implements
40    /// `ThermogramTrait`, forwarding them to the wrapped struct.
41    ///
42    /// # Examples
43    /// ```rust
44    /// let file_path = "/home/user/FLIR0123.jpg";
45    /// let r_thermogram = Thermogram::from_file(&file_path);
46    /// match r_thermogram {
47    ///     None => println!("Failed opening thermogram {:?}", file_path),
48    ///     Some(thermogram) => {
49    ///         println!("Successfully opened thermogram {:?}", file_path);
50    ///         // Do something with `thermogram`
51    ///         // ...
52    ///     },
53    /// }
54    /// ```
55    pub fn from_file(path: &Path) -> Option<Self> {
56        match File::open(path) {
57            Ok(mut file) => {
58                let mut magic_numbers = [0u8; 4];
59                let read_success = file.read(&mut magic_numbers);
60
61                match read_success {
62                    Ok(count) => {
63                        if magic_numbers.len() != count {
64                            println!("Read insufficient bytes to determine type of {:?}", path);
65                            return None;
66                        }
67
68                        // TODO JPG: Other magic numbers
69                        if magic_numbers[..3] == [255, 216, 255] {
70                            match FlirThermogram::from_file(path) {
71                                Some(flir) => return Some(Thermogram::Flir(flir)),
72                                _ => return None,
73                            }
74                        }
75
76                        let tiff = &magic_numbers[..4];
77                        if tiff == [73, 73, 42, 0] || tiff == [77, 77, 0, 42] {
78                            match TiffThermogram::from_file(path) {
79                                Some(tiff) => return Some(Thermogram::Tiff(tiff)),
80                                _ => return None,
81                            }
82                        }
83
84                        println!("Thermogram format not recognized: {:x?}=={:?}", magic_numbers, magic_numbers);
85                        return None;
86                    }
87                    _ => {
88                        println!("Failed reading file {:?}", path);
89                        return None;
90                    }
91                }
92            }
93            _ => {
94                println!("Failed opening file {:?}", path);
95                return None;
96            }
97        }
98    }
99}
100
101/// The `ThermogramTrait` implemented for the `Thermogram` enum. Method calls are forwarded to the
102/// specific format wrapped by the enum. Consult the trait for documentation on the supported
103/// methods.
104impl ThermogramTrait for Thermogram {
105    fn thermal(&self) -> &Array<f32, Ix2> {
106        match self {
107            Thermogram::Flir(t) => t.thermal(),
108            Thermogram::Tiff(t) => t.thermal(),
109        }
110    }
111
112    fn optical(&self) -> Option<&Array<u8, Ix3>> {
113        match self {
114            Thermogram::Flir(t) => t.optical(),
115            Thermogram::Tiff(t) => t.optical(),
116        }
117    }
118
119    fn identifier(&self) -> &str {
120        match self {
121            Thermogram::Flir(t) => t.identifier(),
122            Thermogram::Tiff(t) => t.identifier(),
123        }
124    }
125
126    fn path(&self) -> Option<&str> {
127        match self {
128            Thermogram::Flir(t) => t.path(),
129            Thermogram::Tiff(t) => t.path(),
130        }
131    }
132}