Skip to main content

libblackbody/
tiff.rs

1use ndarray::*;
2use std::fs::File;
3use std::path::{Path, PathBuf};
4use tiff::decoder::DecodingResult;
5
6use crate::ThermogramTrait;
7
8/// This is the struct and `ThermogramTrait` implementation for TIFF thermograms, through the use
9/// `image-rs/tiff`.
10///
11/// A 'TIFF thermogram' is basically any TIFF file with a channel of data, assumed to be
12/// thermographic. Currently the only supported data types are u16 and u32, which are converted to
13/// floats. U16 data is assumed to be centikelvin and converted to centigrades by subtracting 27315.
14/// U32 is assumed to actually be a f32 and transmuted to that data type.
15///
16/// While a file can be directly read with `from_file`, it is recommended to instead use the
17/// `Thermogram::from_file` instead. The latter detects what kind of file (TIFF, FLIR) it is dealing
18/// with, subsequently choosing the right reader for it. This way your application support different
19/// thermogram formats.
20#[derive(Clone, Debug)]
21pub struct TiffThermogram {
22    thermal: Array<f32, Ix2>,
23    file_path: PathBuf,
24}
25
26#[allow(dead_code)]
27impl TiffThermogram {
28    /// Read a FLIR file referenced by a path.
29    ///
30    /// # Arguments
31    /// * `file_path` - The path to the FLIR file to read.
32    ///
33    /// # Returns
34    /// In case of success, `Some<FlirThermogram>` is returned, otherwise `None`. Values are in
35    /// centigrades, as specified by the `ThermogramTrait` contract.
36    pub fn from_file(file_path: &Path) -> Option<Self> {
37        match Self::_read_thermal(file_path) {
38            Some(thermal) => {
39                return Some(Self {
40                    thermal: thermal,
41                    file_path: (*file_path).to_path_buf(),
42                });
43            }
44            _ => return None
45        }
46    }
47
48    fn _read_thermal(file_path: &Path) -> Option<Array<f32, Ix2>> {
49        return Self::_read_thermal_libtiff(file_path);
50    }
51
52    fn _read_thermal_libtiff(file_path: &Path) -> Option<Array<f32, Ix2>> {
53        let file = File::open(file_path).unwrap();
54        let mut tiff = tiff::decoder::Decoder::new(file).unwrap();
55        let tiff_dims = tiff.dimensions().unwrap();
56        let arr_dims = Dim((tiff_dims.1 as usize, tiff_dims.0 as usize));
57        let vec_to_ndarray = |values| {
58            let thermal = ndarray::ArrayBase::from(values);
59            let thermal = thermal.into_shape(arr_dims).unwrap();
60            thermal
61        };
62
63        match tiff.read_image() {
64            Ok(image) => match image {
65                DecodingResult::U8(values) => {
66                    let f32_values: Vec<f32> =
67                        values.into_iter().map(|integer| integer as f32).collect();
68
69                    let thermal = vec_to_ndarray(f32_values);
70                    Some(thermal)
71                },
72                DecodingResult::U16(values) => {
73                    let f32_values: Vec<f32> =
74                        values.into_iter().map(|integer| integer as f32).collect();
75
76                    let thermal = vec_to_ndarray(f32_values);
77                    let thermal = thermal - 27315.0;
78                    Some(thermal / 100.0)
79                }
80                DecodingResult::U32(values) => {
81                    let f32_values: Vec<f32> =
82                        values.into_iter().map(|integer| integer as f32).collect();
83
84                    let thermal = vec_to_ndarray(f32_values);
85                    let thermal = thermal - 27315.0;
86                    Some(thermal / 100.0)
87                }
88                DecodingResult::U64(values) => {
89                    let f32_values: Vec<f32> =
90                        values.into_iter().map(|integer| integer as f32).collect();
91
92                    let thermal = vec_to_ndarray(f32_values);
93                    let thermal = thermal - 27315.0;
94                    Some(thermal / 100.0)
95                }
96                DecodingResult::F32(values) => {
97                    Some(vec_to_ndarray(values))
98                }
99                DecodingResult::F64(values) => {
100                    Some(vec_to_ndarray(values.iter().map(|v| *v as f32).collect()))
101                }
102            }
103            _ => None
104        }
105    }
106}
107
108impl ThermogramTrait for TiffThermogram {
109    fn thermal(&self) -> &Array<f32, Ix2> {
110        &self.thermal
111    }
112
113    fn optical(&self) -> Option<&Array<u8, Ix3>> {
114        None
115    }
116
117    fn identifier(&self) -> &str {
118        // FIXME unwraps
119        let file_name = self.file_path.file_name();
120        file_name.unwrap().to_str().unwrap()
121    }
122
123    fn path(&self) -> Option<&str> {
124        self.file_path.to_str()
125    }
126}