- add colormap from string to rust code
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiffwrite"
|
name = "tiffwrite"
|
||||||
version = "2025.8.0"
|
version = "2025.8.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.85.1"
|
rust-version = "1.85.1"
|
||||||
authors = ["Wim Pomp <w.pomp@nki.nl>"]
|
authors = ["Wim Pomp <w.pomp@nki.nl>"]
|
||||||
@@ -18,6 +18,8 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
|
colorcet = "0.2.1"
|
||||||
|
colorgrad = "0.7.2"
|
||||||
chrono = "0.4.41"
|
chrono = "0.4.41"
|
||||||
css-color = "0.2.8"
|
css-color = "0.2.8"
|
||||||
flate2 = "1.1.1"
|
flate2 = "1.1.1"
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ from pathlib import Path
|
|||||||
from typing import Any, Callable, Sequence
|
from typing import Any, Callable, Sequence
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
import colorcet
|
|
||||||
import matplotlib
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import ArrayLike, DTypeLike
|
from numpy.typing import ArrayLike, DTypeLike
|
||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
@@ -77,6 +75,9 @@ class IJTiffFile(rs.IJTiffFile):
|
|||||||
else:
|
else:
|
||||||
return codecs.get(int(idx), 50000)
|
return codecs.get(int(idx), 50000)
|
||||||
|
|
||||||
|
if colors is not None and colormap is not None:
|
||||||
|
warn("Cannot have colors and colormap simultaneously.", TiffWriteWarning, stacklevel=2)
|
||||||
|
|
||||||
self.path = Path(path)
|
self.path = Path(path)
|
||||||
self.dtype = np.dtype(dtype)
|
self.dtype = np.dtype(dtype)
|
||||||
if compression is not None:
|
if compression is not None:
|
||||||
@@ -88,7 +89,7 @@ class IJTiffFile(rs.IJTiffFile):
|
|||||||
if colors is not None:
|
if colors is not None:
|
||||||
self.colors = [str(color) for color in colors]
|
self.colors = [str(color) for color in colors]
|
||||||
if colormap is not None:
|
if colormap is not None:
|
||||||
self.colormap = get_colormap(colormap)
|
self.colormap = str(colormap)
|
||||||
if pxsize is not None:
|
if pxsize is not None:
|
||||||
self.px_size = float(pxsize)
|
self.px_size = float(pxsize)
|
||||||
if deltaz is not None:
|
if deltaz is not None:
|
||||||
@@ -96,7 +97,7 @@ class IJTiffFile(rs.IJTiffFile):
|
|||||||
if timeinterval is not None:
|
if timeinterval is not None:
|
||||||
self.time_interval = float(timeinterval)
|
self.time_interval = float(timeinterval)
|
||||||
if comment is not None:
|
if comment is not None:
|
||||||
self.comment = comment
|
self.comment = str(comment)
|
||||||
if extratags is not None:
|
if extratags is not None:
|
||||||
for extra_tag in extratags:
|
for extra_tag in extratags:
|
||||||
self.append_extra_tag(extra_tag, None)
|
self.append_extra_tag(extra_tag, None)
|
||||||
@@ -106,8 +107,6 @@ class IJTiffFile(rs.IJTiffFile):
|
|||||||
TiffWriteWarning,
|
TiffWriteWarning,
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
if colors is not None and colormap is not None:
|
|
||||||
warn("Cannot have colors and colormap simultaneously.", TiffWriteWarning, stacklevel=2)
|
|
||||||
|
|
||||||
def __enter__(self) -> IJTiffFile:
|
def __enter__(self) -> IJTiffFile:
|
||||||
return self
|
return self
|
||||||
@@ -146,41 +145,6 @@ class IJTiffFile(rs.IJTiffFile):
|
|||||||
self.append_extra_tag(extra_tag, (c, z, t))
|
self.append_extra_tag(extra_tag, (c, z, t))
|
||||||
|
|
||||||
|
|
||||||
def get_colormap(colormap: str) -> np.ndarray:
|
|
||||||
if hasattr(colorcet, colormap.rstrip("_r")):
|
|
||||||
cm = np.array(
|
|
||||||
[[int("".join(i), 16) for i in zip(*[iter(s[1:])] * 2)] for s in getattr(colorcet, colormap.rstrip("_r"))]
|
|
||||||
).astype("uint8")
|
|
||||||
if colormap.endswith("_r"):
|
|
||||||
cm = cm[::-1]
|
|
||||||
if colormap.startswith("glasbey") or colormap.endswith("glasbey"):
|
|
||||||
cm[0] = 255, 255, 255
|
|
||||||
cm[-1] = 0, 0, 0
|
|
||||||
else:
|
|
||||||
cmap = matplotlib.colormaps.get_cmap(colormap)
|
|
||||||
if cmap.N < 256:
|
|
||||||
cm = (
|
|
||||||
255
|
|
||||||
* np.vstack(
|
|
||||||
(
|
|
||||||
(1, 1, 1),
|
|
||||||
matplotlib.cm.ScalarMappable(matplotlib.colors.Normalize(1, 254), cmap).to_rgba(
|
|
||||||
np.arange(1, 254)
|
|
||||||
)[:, :3],
|
|
||||||
(0, 0, 0),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).astype("uint8")
|
|
||||||
else:
|
|
||||||
cm = (
|
|
||||||
255
|
|
||||||
* matplotlib.cm.ScalarMappable(matplotlib.colors.Normalize(0, 255), cmap).to_rgba(np.arange(256))[
|
|
||||||
:, :3
|
|
||||||
]
|
|
||||||
).astype("uint8")
|
|
||||||
return cm
|
|
||||||
|
|
||||||
|
|
||||||
def tiffwrite(
|
def tiffwrite(
|
||||||
file: str | Path,
|
file: str | Path,
|
||||||
data: np.ndarray,
|
data: np.ndarray,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ classifiers = [
|
|||||||
"Programming Language :: Python :: 3.14",
|
"Programming Language :: Python :: 3.14",
|
||||||
]
|
]
|
||||||
|
|
||||||
dependencies = ["colorcet", "matplotlib", "numpy", "tqdm"]
|
dependencies = ["numpy", "tqdm"]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
test = ["pytest", "tifffile", "imagecodecs"]
|
test = ["pytest", "tifffile", "imagecodecs"]
|
||||||
|
|||||||
27
src/lib.rs
27
src/lib.rs
@@ -3,6 +3,8 @@ mod py;
|
|||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use colorcet::ColorMap;
|
||||||
|
use colorgrad::{Gradient, LinearGradient};
|
||||||
use css_color::Srgb;
|
use css_color::Srgb;
|
||||||
use flate2::write::ZlibEncoder;
|
use flate2::write::ZlibEncoder;
|
||||||
use ndarray::{ArcArray2, AsArray, Ix2, s};
|
use ndarray::{ArcArray2, AsArray, Ix2, s};
|
||||||
@@ -709,11 +711,12 @@ impl IJTiffFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// set colors from css color names and #C01085
|
/// set colors from css color names and #C01085
|
||||||
pub fn set_colors(&mut self, colors: &[String]) -> Result<()> {
|
pub fn set_colors<S: AsRef<str>>(&mut self, colors: &[S]) -> Result<()> {
|
||||||
self.colors = Colors::Colors(
|
self.colors = Colors::Colors(
|
||||||
colors
|
colors
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| {
|
.map(|c| {
|
||||||
|
let c = c.as_ref();
|
||||||
let lc = c.to_lowercase();
|
let lc = c.to_lowercase();
|
||||||
let c = match lc.as_str() {
|
let c = match lc.as_str() {
|
||||||
"r" => "#ff0000",
|
"r" => "#ff0000",
|
||||||
@@ -740,6 +743,28 @@ impl IJTiffFile {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_colormap<S: AsRef<str>>(&mut self, name: S) -> Result<()> {
|
||||||
|
let name = name.as_ref();
|
||||||
|
let colormap: LinearGradient = name.parse::<ColorMap>()?.try_into()?;
|
||||||
|
let mut colormap = colormap
|
||||||
|
.colors(256)
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| {
|
||||||
|
vec![
|
||||||
|
(c.r * 255.0).round() as u8,
|
||||||
|
(c.g * 255.0).round() as u8,
|
||||||
|
(c.b * 255.0).round() as u8,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if name.starts_with("glasbey") || name.ends_with("glasbey") {
|
||||||
|
colormap[0] = vec![255, 255, 255];
|
||||||
|
colormap[255] = vec![0, 0, 0];
|
||||||
|
}
|
||||||
|
self.colors = Colors::Colormap(colormap);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// to be saved in description tag (270)
|
/// to be saved in description tag (270)
|
||||||
pub fn description(&self, c_size: usize, z_size: usize, t_size: usize) -> String {
|
pub fn description(&self, c_size: usize, z_size: usize, t_size: usize) -> String {
|
||||||
let mut desc: String = String::from("ImageJ=1.11a");
|
let mut desc: String = String::from("ImageJ=1.11a");
|
||||||
|
|||||||
12
src/py.rs
12
src/py.rs
@@ -1,7 +1,6 @@
|
|||||||
use crate::{Colors, Compression, IJTiffFile, Tag};
|
use crate::{Colors, Compression, IJTiffFile, Tag};
|
||||||
use ndarray::s;
|
|
||||||
use num::{Complex, FromPrimitive, Rational32};
|
use num::{Complex, FromPrimitive, Rational32};
|
||||||
use numpy::{PyArrayMethods, PyReadonlyArray2};
|
use numpy::PyReadonlyArray2;
|
||||||
use pyo3::exceptions::PyValueError;
|
use pyo3::exceptions::PyValueError;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
@@ -221,14 +220,9 @@ impl PyIJTiffFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[setter]
|
#[setter]
|
||||||
fn set_colormap(&mut self, colormap: PyReadonlyArray2<u8>) -> PyResult<()> {
|
fn set_colormap(&mut self, colormap: &str) -> PyResult<()> {
|
||||||
if let Some(ijtifffile) = &mut self.ijtifffile {
|
if let Some(ijtifffile) = &mut self.ijtifffile {
|
||||||
let a = colormap.to_owned_array();
|
ijtifffile.set_colormap(colormap)?;
|
||||||
ijtifffile.colors = Colors::Colormap(
|
|
||||||
(0..a.shape()[0])
|
|
||||||
.map(|i| Vec::from(a.slice(s![i, ..]).as_slice().unwrap()))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user