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}