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
//! Misc utility definitions
use std::fmt::{Display, Debug};
use num::{ToPrimitive, Unsigned, Integer};
use image::{Rgba, RgbaImage};
use failure::{err_msg, Error};
/// A list of supported formats
///
/// Information gathered from [https://openslide.org/formats/](https://openslide.org/formats/)
///
#[derive(Clone, Debug)]
pub enum Format {
/// Single-file pyramidal tiled TIFF, with non-standard metadata and compression.
///
/// File extensions:
/// .svs, .tif
Aperio,
/// Multi-file JPEG/NGR with proprietary metadata and index file formats, and single-file
/// TIFF-like format with proprietary metadata.
///
/// File extensions:
/// .vms, .vmu, .ndpi
Hamamatsu,
/// Single-file pyramidal tiled BigTIFF with non-standard metadata.
///
/// File extensions
/// .scn
Leica,
/// Multi-file with very complicated proprietary metadata and indexes.
///
/// File extensions
/// .mrxs
Mirax,
/// Single-file pyramidal tiled TIFF or BigTIFF with non-standard metadata.
///
/// File extensions
/// .tiff
Phillips,
/// SQLite database containing pyramid tiles and metadata.
///
/// File extensions
/// .svslide
Sakura,
/// Single-file pyramidal tiled TIFF, with non-standard metadata and overlaps. Additional files
/// contain more metadata and detailed overlap info.
///
/// File extensions
/// .tif
Trestle,
/// Single-file pyramidal tiled BigTIFF, with non-standard metadata and overlaps.
///
/// File extensions
/// .bif, .tif
Ventana,
/// Single-file pyramidal tiled TIFF.
///
/// File extensions
/// .tif
GenericTiledTiff,
}
/// The different ways the u8 color values are encoded into a u32 value.
///
/// A successfull reading from OpenSlide's `read_region()` will result in a buffer of `u32` with
/// `height * width` elements, where `height` and `width` is the shape (in pixels) of the read
/// region. This `u32` value consist of four `u8` values which are the red, green, blue, and alpha
/// value of a certain pixel. This enum determines in which order to arange these channels within
/// one element.
#[derive(Clone, Debug)]
pub enum WordRepresentation {
/// From most significant bit to least significant bit: `[alpha, red, green, blue]`
BigEndian,
/// From most significant bit to least significant bit: `[blue, green, red, alpha]`
LittleEndian,
}
/// This function takes a buffer, as the one obtained from openslide::read_region, and decodes into
/// an Rgba image buffer.
pub fn decode_buffer<T: Unsigned + Integer + ToPrimitive + Debug + Display + Clone + Copy>(
buffer: &Vec<u32>,
height: T,
width: T,
word_representation: WordRepresentation
) -> Result<RgbaImage, Error> {
let (a_pos, r_pos, g_pos, b_pos) = match word_representation {
WordRepresentation::BigEndian => (0, 1, 2, 3),
WordRepresentation::LittleEndian => (3, 2, 1, 0),
};
let mut rgba_image = RgbaImage::new(
width.to_u32().ok_or(err_msg("Conversion to primitive error"))?,
height.to_u32().ok_or(err_msg("Conversion to primitive error"))?);
for (col, row, pixel) in rgba_image.enumerate_pixels_mut() {
let curr_pos = row * width.to_u32().ok_or(err_msg("Conversion to primitive error"))? + col;
let values = buffer[curr_pos as usize];
// TODO: Iterate over chars() instead (?)
let bit_repr = format!("{:b}", values);
let alpha_bit_repr = String::from(&bit_repr[(8 * a_pos)..(8 * a_pos + 8)]);
let red_bit_repr = String::from(&bit_repr[(8 * r_pos)..(8 * r_pos + 8)]);
let green_bit_repr = String::from(&bit_repr[(8 * g_pos)..(8 * g_pos + 8)]);
let blue_bit_repr = String::from(&bit_repr[(8 * b_pos)..(8 * b_pos + 8)]);
let alpha = u8::from_str_radix(&alpha_bit_repr, 2)?;
let mut red = u8::from_str_radix(&red_bit_repr, 2)?;
let mut green = u8::from_str_radix(&green_bit_repr, 2)?;
let mut blue = u8::from_str_radix(&blue_bit_repr, 2)?;
if alpha != 0 && alpha != 255 {
red = (red as f32 * (255.0 / alpha as f32)).round().max(0.0).min(255.0) as u8;
green = (green as f32 * (255.0 / alpha as f32)).round().max(0.0).min(255.0) as u8;
blue = (blue as f32 * (255.0 / alpha as f32)).round().max(0.0).min(255.0) as u8;
}
*pixel = Rgba([red, green, blue, alpha]);
}
Ok(rgba_image)
}