openslide/utils.rs
1//! Misc utility definitions
2
3use std::fmt::{Display, Debug};
4
5use num::{ToPrimitive, Unsigned, Integer};
6use image::{Rgba, RgbaImage};
7use failure::{err_msg, Error};
8
9
10/// A list of supported formats
11///
12/// Information gathered from [https://openslide.org/formats/](https://openslide.org/formats/)
13///
14#[derive(Clone, Debug)]
15pub enum Format {
16 /// Single-file pyramidal tiled TIFF, with non-standard metadata and compression.
17 ///
18 /// File extensions:
19 /// .svs, .tif
20 Aperio,
21 /// Multi-file JPEG/NGR with proprietary metadata and index file formats, and single-file
22 /// TIFF-like format with proprietary metadata.
23 ///
24 /// File extensions:
25 /// .vms, .vmu, .ndpi
26 Hamamatsu,
27 /// Single-file pyramidal tiled BigTIFF with non-standard metadata.
28 ///
29 /// File extensions
30 /// .scn
31 Leica,
32 /// Multi-file with very complicated proprietary metadata and indexes.
33 ///
34 /// File extensions
35 /// .mrxs
36 Mirax,
37 /// Single-file pyramidal tiled TIFF or BigTIFF with non-standard metadata.
38 ///
39 /// File extensions
40 /// .tiff
41 Phillips,
42 /// SQLite database containing pyramid tiles and metadata.
43 ///
44 /// File extensions
45 /// .svslide
46 Sakura,
47 /// Single-file pyramidal tiled TIFF, with non-standard metadata and overlaps. Additional files
48 /// contain more metadata and detailed overlap info.
49 ///
50 /// File extensions
51 /// .tif
52 Trestle,
53 /// Single-file pyramidal tiled BigTIFF, with non-standard metadata and overlaps.
54 ///
55 /// File extensions
56 /// .bif, .tif
57 Ventana,
58 /// Single-file pyramidal tiled TIFF.
59 ///
60 /// File extensions
61 /// .tif
62 GenericTiledTiff,
63}
64
65/// The different ways the u8 color values are encoded into a u32 value.
66///
67/// A successfull reading from OpenSlide's `read_region()` will result in a buffer of `u32` with
68/// `height * width` elements, where `height` and `width` is the shape (in pixels) of the read
69/// region. This `u32` value consist of four `u8` values which are the red, green, blue, and alpha
70/// value of a certain pixel. This enum determines in which order to arange these channels within
71/// one element.
72#[derive(Clone, Debug)]
73pub enum WordRepresentation {
74 /// From most significant bit to least significant bit: `[alpha, red, green, blue]`
75 BigEndian,
76 /// From most significant bit to least significant bit: `[blue, green, red, alpha]`
77 LittleEndian,
78}
79
80/// This function takes a buffer, as the one obtained from openslide::read_region, and decodes into
81/// an Rgba image buffer.
82pub fn decode_buffer<T: Unsigned + Integer + ToPrimitive + Debug + Display + Clone + Copy>(
83 buffer: &Vec<u32>,
84 height: T,
85 width: T,
86 word_representation: WordRepresentation
87) -> Result<RgbaImage, Error> {
88 let (a_pos, r_pos, g_pos, b_pos) = match word_representation {
89 WordRepresentation::BigEndian => (0, 1, 2, 3),
90 WordRepresentation::LittleEndian => (3, 2, 1, 0),
91 };
92
93 let mut rgba_image = RgbaImage::new(
94 width.to_u32().ok_or(err_msg("Conversion to primitive error"))?,
95 height.to_u32().ok_or(err_msg("Conversion to primitive error"))?);
96
97 for (col, row, pixel) in rgba_image.enumerate_pixels_mut() {
98 let curr_pos = row * width.to_u32().ok_or(err_msg("Conversion to primitive error"))? + col;
99 let values = buffer[curr_pos as usize];
100 // TODO: Iterate over chars() instead (?)
101 let bit_repr = format!("{:b}", values);
102 let alpha_bit_repr = String::from(&bit_repr[(8 * a_pos)..(8 * a_pos + 8)]);
103 let red_bit_repr = String::from(&bit_repr[(8 * r_pos)..(8 * r_pos + 8)]);
104 let green_bit_repr = String::from(&bit_repr[(8 * g_pos)..(8 * g_pos + 8)]);
105 let blue_bit_repr = String::from(&bit_repr[(8 * b_pos)..(8 * b_pos + 8)]);
106
107 let alpha = u8::from_str_radix(&alpha_bit_repr, 2)?;
108 let mut red = u8::from_str_radix(&red_bit_repr, 2)?;
109 let mut green = u8::from_str_radix(&green_bit_repr, 2)?;
110 let mut blue = u8::from_str_radix(&blue_bit_repr, 2)?;
111
112
113 if alpha != 0 && alpha != 255 {
114 red = (red as f32 * (255.0 / alpha as f32)).round().max(0.0).min(255.0) as u8;
115 green = (green as f32 * (255.0 / alpha as f32)).round().max(0.0).min(255.0) as u8;
116 blue = (blue as f32 * (255.0 / alpha as f32)).round().max(0.0).min(255.0) as u8;
117 }
118
119 *pixel = Rgba([red, green, blue, alpha]);
120 }
121
122 Ok(rgba_image)
123}