1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use ndarray::*;
use std::fs::File;
use std::path::Path;
use std::io::Read;

use crate::*;

/// The wrapper enum through which most processing of thermograms is recommend to
/// happen. Use `Thermogram::from_file()` to read files.
///
/// The enum itself, and all thermogram formats it wraps, implement `ThermogramTrait`. Below
/// several of its methods are listed. Consult the documentation of `ThermogramTrait` for
/// more details.
///
/// ```rust
/// pub trait ThermogramTrait {
///     fn thermal(&self) -> &Array<f32, Ix2>;  // Extract the thermal data
///     fn optical(&self) -> &Array<u8, Ix3>>;  // Extract embedded photos, if present
///     fn identifier(&self) -> &str;  // A uniquely identifying string for this thermogram
///     fn render(&self min_temp: f32, max_temp: f32, palette: [[f32; 3]; 256]) -> Array<u8, Ix3>;  // Thermal data render using the given palette
///     fn render_defaults(&self) -> Array<u8, Ix3>;  // Thermal data rendered using the minimum and maximum thermal value and the `palette::TURBO` palette.
///     fn thermal_shape(&self) -> [usize; 2];  // The [height, width] of the thermal data
///     fn normalized_minmax(&self) -> Array<f32, Ix2>;  // Thermal data normalized to lie in the range 0.0..=1.0
/// }
/// ```
#[derive(Clone, Debug)]
pub enum Thermogram {
    Flir(FlirThermogram),
    Tiff(TiffThermogram),
}

impl Thermogram {
    /// Tries to recognize the file type based on its magic number and return a `Thermogram`.
    ///
    /// # Arguments
    /// * `path` - A path to a thermogram file.
    ///
    /// # Returns
    /// In case of success an `Some<Thermogram>`, otherwise `None`. A `Thermogam` implements
    /// `ThermogramTrait`, forwarding them to the wrapped struct.
    ///
    /// # Examples
    /// ```rust
    /// let file_path = "/home/user/FLIR0123.jpg";
    /// let r_thermogram = Thermogram::from_file(&file_path);
    /// match r_thermogram {
    ///     None => println!("Failed opening thermogram {:?}", file_path),
    ///     Some(thermogram) => {
    ///         println!("Successfully opened thermogram {:?}", file_path);
    ///         // Do something with `thermogram`
    ///         // ...
    ///     },
    /// }
    /// ```
    pub fn from_file(path: &Path) -> Option<Self> {
        match File::open(path) {
            Ok(mut file) => {
                let mut magic_numbers = [0u8; 4];
                let read_success = file.read(&mut magic_numbers);

                match read_success {
                    Ok(count) => {
                        if magic_numbers.len() != count {
                            println!("Read insufficient bytes to determine type of {:?}", path);
                            return None;
                        }

                        // TODO JPG: Other magic numbers
                        if magic_numbers[..3] == [255, 216, 255] {
                            match FlirThermogram::from_file(path) {
                                Some(flir) => return Some(Thermogram::Flir(flir)),
                                _ => return None,
                            }
                        }

                        let tiff = &magic_numbers[..4];
                        if tiff == [73, 73, 42, 0] || tiff == [77, 77, 0, 42] {
                            match TiffThermogram::from_file(path) {
                                Some(tiff) => return Some(Thermogram::Tiff(tiff)),
                                _ => return None,
                            }
                        }

                        println!("Thermogram format not recognized: {:x?}=={:?}", magic_numbers, magic_numbers);
                        return None;
                    }
                    _ => {
                        println!("Failed reading file {:?}", path);
                        return None;
                    }
                }
            }
            _ => {
                println!("Failed opening file {:?}", path);
                return None;
            }
        }
    }
}

/// The `ThermogramTrait` implemented for the `Thermogram` enum. Method calls are forwarded to the
/// specific format wrapped by the enum. Consult the trait for documentation on the supported
/// methods.
impl ThermogramTrait for Thermogram {
    fn thermal(&self) -> &Array<f32, Ix2> {
        match self {
            Thermogram::Flir(t) => t.thermal(),
            Thermogram::Tiff(t) => t.thermal(),
        }
    }

    fn optical(&self) -> Option<&Array<u8, Ix3>> {
        match self {
            Thermogram::Flir(t) => t.optical(),
            Thermogram::Tiff(t) => t.optical(),
        }
    }

    fn identifier(&self) -> &str {
        match self {
            Thermogram::Flir(t) => t.identifier(),
            Thermogram::Tiff(t) => t.identifier(),
        }
    }

    fn path(&self) -> Option<&str> {
        match self {
            Thermogram::Flir(t) => t.path(),
            Thermogram::Tiff(t) => t.path(),
        }
    }
}