225 lines
7.8 KiB
Rust
225 lines
7.8 KiB
Rust
use ndarray as nd;
|
|
use image;
|
|
|
|
pub enum ColorMaps{
|
|
Binary,
|
|
NipySpectral,
|
|
TraficLight,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ColorMap{
|
|
pub red: Vec<(f64, f64, f64)>,
|
|
pub green: Vec<(f64, f64, f64)>,
|
|
pub blue: Vec<(f64, f64, f64)>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Heatmap{
|
|
pub cm: ColorMap
|
|
}
|
|
|
|
|
|
impl Heatmap{
|
|
pub fn new(cm: ColorMaps) -> Self{
|
|
Self{
|
|
cm: ColorMap::new(cm)
|
|
}
|
|
}
|
|
|
|
pub fn convert_image(&self, img: image::DynamicImage) -> image::RgbImage {
|
|
let gray_img: image::GrayImage = match img {
|
|
image::DynamicImage::ImageLuma8(gray_image) => {
|
|
gray_image
|
|
}
|
|
_ => {
|
|
img.to_luma()
|
|
}
|
|
};
|
|
|
|
let mut heatmap_img = image::RgbImage::new(gray_img.width(), gray_img.height());
|
|
let lut_size = 256;// * 256 * 256;
|
|
let lut = self.cm.generate_lut(lut_size);
|
|
|
|
// info!("LUT: {:?}", lut);
|
|
|
|
for pixel in gray_img.enumerate_pixels() {
|
|
let l = pixel.2;
|
|
let p = image::Rgb(lut[l.0[0] as usize]);
|
|
heatmap_img.put_pixel(pixel.0, pixel.1, p);
|
|
}
|
|
|
|
return heatmap_img;
|
|
}
|
|
}
|
|
|
|
// impl From<nd::Array2<i32>> for Heatmap {
|
|
// fn from(array: nd::Array2<i32>) -> Self {
|
|
|
|
// }
|
|
// }
|
|
|
|
// impl From<image::DynamicImage> for Heatmap {
|
|
// fn from(image: image::DynamicImage) -> Self {
|
|
// Self{
|
|
// cm:
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
impl ColorMap{
|
|
pub fn new(m: ColorMaps) -> Self {
|
|
|
|
let cm = match m {
|
|
ColorMaps::Binary => {
|
|
Self{
|
|
red: vec![
|
|
(0., 0., 0.), (1., 1., 1.)
|
|
],
|
|
green: vec![
|
|
(0., 0., 0.), (1., 1., 1.)
|
|
],
|
|
blue: vec![
|
|
(0., 0., 0.), (1., 1., 1.)
|
|
],
|
|
}
|
|
}
|
|
ColorMaps::TraficLight => {
|
|
Self{
|
|
red: vec![
|
|
(0., 0., 0.), (0.5, 1., 1.), (1., 1., 1.)
|
|
],
|
|
green: vec![
|
|
(0., 0., 0.), (0.5, 1., 1.), (1., 0., 0.)
|
|
],
|
|
blue: vec![
|
|
(0., 0., 1.), (0.5, 0., 0.), (1., 0., 0.)
|
|
],
|
|
}
|
|
}
|
|
ColorMaps::NipySpectral => {
|
|
Self{
|
|
red: vec![(0.0, 0.0, 0.0), (0.05, 0.4667, 0.4667),
|
|
(0.10, 0.5333, 0.5333), (0.15, 0.0, 0.0),
|
|
(0.20, 0.0, 0.0), (0.25, 0.0, 0.0),
|
|
(0.30, 0.0, 0.0), (0.35, 0.0, 0.0),
|
|
(0.40, 0.0, 0.0), (0.45, 0.0, 0.0),
|
|
(0.50, 0.0, 0.0), (0.55, 0.0, 0.0),
|
|
(0.60, 0.0, 0.0), (0.65, 0.7333, 0.7333),
|
|
(0.70, 0.9333, 0.9333), (0.75, 1.0, 1.0),
|
|
(0.80, 1.0, 1.0), (0.85, 1.0, 1.0),
|
|
(0.90, 0.8667, 0.8667), (0.95, 0.80, 0.80),
|
|
(1.0, 0.80, 0.80)],
|
|
green: vec![(0.0, 0.0, 0.0), (0.05, 0.0, 0.0),
|
|
(0.10, 0.0, 0.0), (0.15, 0.0, 0.0),
|
|
(0.20, 0.0, 0.0), (0.25, 0.4667, 0.4667),
|
|
(0.30, 0.6000, 0.6000), (0.35, 0.6667, 0.6667),
|
|
(0.40, 0.6667, 0.6667), (0.45, 0.6000, 0.6000),
|
|
(0.50, 0.7333, 0.7333), (0.55, 0.8667, 0.8667),
|
|
(0.60, 1.0, 1.0), (0.65, 1.0, 1.0),
|
|
(0.70, 0.9333, 0.9333), (0.75, 0.8000, 0.8000),
|
|
(0.80, 0.6000, 0.6000), (0.85, 0.0, 0.0),
|
|
(0.90, 0.0, 0.0), (0.95, 0.0, 0.0),
|
|
(1.0, 0.80, 0.80)],
|
|
blue: vec![(0.0, 0.0, 0.0), (0.05, 0.5333, 0.5333),
|
|
(0.10, 0.6000, 0.6000), (0.15, 0.6667, 0.6667),
|
|
(0.20, 0.8667, 0.8667), (0.25, 0.8667, 0.8667),
|
|
(0.30, 0.8667, 0.8667), (0.35, 0.6667, 0.6667),
|
|
(0.40, 0.5333, 0.5333), (0.45, 0.0, 0.0),
|
|
(0.5, 0.0, 0.0), (0.55, 0.0, 0.0),
|
|
(0.60, 0.0, 0.0), (0.65, 0.0, 0.0),
|
|
(0.70, 0.0, 0.0), (0.75, 0.0, 0.0),
|
|
(0.80, 0.0, 0.0), (0.85, 0.0, 0.0),
|
|
(0.90, 0.0, 0.0), (0.95, 0.0, 0.0),
|
|
(1.0, 0.80, 0.80)],
|
|
}
|
|
}
|
|
};
|
|
|
|
return cm;
|
|
}
|
|
|
|
|
|
/// Similar to MatplotLib LinearSegmentedColormap
|
|
/// @see https://github.com/matplotlib/matplotlib/blob/13e3573b721210d84865d148aab7f63cc2fc95a6/lib/matplotlib/colors.py
|
|
/// """
|
|
/// Create color map from linear mapping segments
|
|
/// segmentdata argument is a dictionary with a red, green and blue
|
|
/// entries. Each entry should be a list of *x*, *y0*, *y1* tuples,
|
|
/// forming rows in a table. Entries for alpha are optional.
|
|
/// Example: suppose you want red to increase from 0 to 1 over
|
|
/// the bottom half, green to do the same over the middle half,
|
|
/// and blue over the top half. Then you would use::
|
|
/// cdict = {'red': [(0.0, 0.0, 0.0),
|
|
/// (0.5, 1.0, 1.0),
|
|
/// (1.0, 1.0, 1.0)],
|
|
/// 'green': [(0.0, 0.0, 0.0),
|
|
/// (0.25, 0.0, 0.0),
|
|
/// (0.75, 1.0, 1.0),
|
|
/// (1.0, 1.0, 1.0)],
|
|
/// 'blue': [(0.0, 0.0, 0.0),
|
|
/// (0.5, 0.0, 0.0),
|
|
/// (1.0, 1.0, 1.0)]}
|
|
/// Each row in the table for a given color is a sequence of
|
|
/// *x*, *y0*, *y1* tuples. In each sequence, *x* must increase
|
|
/// monotonically from 0 to 1. For any input value *z* falling
|
|
/// between *x[i]* and *x[i+1]*, the output value of a given color
|
|
/// will be linearly interpolated between *y1[i]* and *y0[i+1]*::
|
|
/// row i: x y0 y1
|
|
/// /
|
|
/// /
|
|
/// row i+1: x y0 y1
|
|
/// Hence y0 in the first row and y1 in the last row are never used.
|
|
/// See Also
|
|
/// --------
|
|
/// LinearSegmentedColormap.from_list
|
|
/// Static method; factory function for generating a smoothly-varying
|
|
/// LinearSegmentedColormap.
|
|
/// """
|
|
pub fn generate_lut(&self, N: usize) -> Vec<[u8; 3]> {
|
|
let r = Self::interpolate_color(&self.red, N);
|
|
let g = Self::interpolate_color(&self.green, N);
|
|
let b = Self::interpolate_color(&self.blue, N);
|
|
let mut lut = Vec::<[u8;3]>::new();
|
|
|
|
for i in 0..N {
|
|
lut.push([r[i], g[i], b[i]]);
|
|
}
|
|
|
|
lut
|
|
}
|
|
|
|
pub fn interpolate_color(colors: &Vec<(f64, f64, f64)>, N: usize) -> Vec<u8>{
|
|
let step_size = 1./N as f64;
|
|
|
|
let mut prev_color: Option<(f64, f64, f64)> = None;
|
|
|
|
let mut lut = Vec::<u8>::new();
|
|
|
|
for color in colors {
|
|
match prev_color {
|
|
Some(prev) => {
|
|
let steps = (color.0/step_size) as usize - lut.len();
|
|
for i in 0..steps {
|
|
let factor = (i + 1) as f64 / steps as f64;
|
|
let c = ((prev.2 * (1. - factor) + color.1 * factor) * (N as f64 - 1.)) as u8;
|
|
lut.push(c);
|
|
}
|
|
}
|
|
None => {
|
|
let steps = (color.0/step_size) as usize;
|
|
for i in 0..steps{
|
|
lut.push((color.2 * (N as f64 - 1.)) as u8);
|
|
}
|
|
}
|
|
}
|
|
prev_color = Some(color.clone());
|
|
}
|
|
// now fill the last bit of the lut with the last color
|
|
for pos in lut.len()..N {
|
|
lut.push(lut.last().unwrap().clone());
|
|
}
|
|
|
|
lut
|
|
}
|
|
} |