- start some color(map) support in python
- compress_frame function in python for backwards compatibility - save all extra tags in a single hashmap - construct tags from references - store frames by c, z, t - save px_size in tiff - some getters and setters in py.rs
This commit is contained in:
@@ -4,7 +4,9 @@ from itertools import product
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Sequence
|
from typing import Any, Sequence
|
||||||
|
|
||||||
|
import colorcet
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from matplotlib import colors as mpl_colors
|
||||||
from numpy.typing import ArrayLike, DTypeLike
|
from numpy.typing import ArrayLike, DTypeLike
|
||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
|
|
||||||
@@ -27,29 +29,29 @@ class Tag(rs.Tag):
|
|||||||
|
|
||||||
Strip = tuple[list[int], list[int]]
|
Strip = tuple[list[int], list[int]]
|
||||||
CZT = tuple[int, int, int]
|
CZT = tuple[int, int, int]
|
||||||
FrameInfo = tuple[IFD, Strip, CZT]
|
FrameInfo = tuple[np.ndarray, None, CZT]
|
||||||
|
|
||||||
|
|
||||||
class IJTiffFile(rs.IJTiffFile):
|
class IJTiffFile(rs.IJTiffFile):
|
||||||
def __new__(cls, path: str | Path, shape: tuple[int, int, int], dtype: DTypeLike = 'uint16',
|
def __new__(cls, path: str | Path, shape: tuple[int, int, int], dtype: DTypeLike = 'uint16',
|
||||||
colors: Sequence[str] = None, colormap: str = None, pxsize: float = None,
|
colors: Sequence[str] = None, colormap: str = None, pxsize: float = None,
|
||||||
deltaz: float = None, timeinterval: float = None, comment: str = None,
|
deltaz: float = None, timeinterval: float = None, comment: str = None,
|
||||||
**extratags: Tag.Value | Tag) -> IJTiffFile:
|
**extratags: Tag) -> IJTiffFile:
|
||||||
new = super().__new__(cls, str(path), shape)
|
new = super().__new__(cls, str(path), shape)
|
||||||
if colors is not None:
|
if colors is not None:
|
||||||
new = new.with_colors(colors)
|
new.colors = np.array([get_color(color) for color in colors])
|
||||||
if colormap is not None:
|
if colormap is not None:
|
||||||
new = new.with_colormap(colormap)
|
new.colormap = get_colormap(colormap)
|
||||||
if pxsize is not None:
|
if pxsize is not None:
|
||||||
new = new.with_pxsize(pxsize)
|
new.px_size = float(pxsize)
|
||||||
if deltaz is not None:
|
if deltaz is not None:
|
||||||
new = new.with_deltaz(deltaz)
|
new.delta_z = float(deltaz)
|
||||||
if timeinterval is not None:
|
if timeinterval is not None:
|
||||||
new = new.with_timeinterval(timeinterval)
|
new.time_interval = float(timeinterval)
|
||||||
if comment is not None:
|
if comment is not None:
|
||||||
new = new.with_comment(comment)
|
new.comment = comment
|
||||||
if extratags:
|
for extra_tag in extratags:
|
||||||
new = new.extend_extratags(extratags)
|
new.append_extra_tag(extra_tag, None)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def __init__(self, path: str | Path, shape: tuple[int, int, int], dtype: DTypeLike = 'uint16', # noqa
|
def __init__(self, path: str | Path, shape: tuple[int, int, int], dtype: DTypeLike = 'uint16', # noqa
|
||||||
@@ -66,30 +68,43 @@ class IJTiffFile(rs.IJTiffFile):
|
|||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def save(self, frame: ArrayLike, c: int, z: int, t: int) -> None:
|
def save(self, frame: ArrayLike, c: int, z: int, t: int) -> None:
|
||||||
frame = np.asarray(frame).astype(self.dtype)
|
for frame, _, (cn, zn, tn) in self.compress_frame(frame):
|
||||||
match self.dtype:
|
frame = np.asarray(frame).astype(self.dtype)
|
||||||
case np.uint8:
|
match self.dtype:
|
||||||
self.save_u8(frame, c, z, t)
|
case np.uint8:
|
||||||
case np.uint16:
|
self.save_u8(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_u16(frame, c, z, t)
|
case np.uint16:
|
||||||
case np.uint32:
|
self.save_u16(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_u32(frame, c, z, t)
|
case np.uint32:
|
||||||
case np.uint64:
|
self.save_u32(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_u64(frame, c, z, t)
|
case np.uint64:
|
||||||
case np.int8:
|
self.save_u64(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_i8(frame, c, z, t)
|
case np.int8:
|
||||||
case np.int16:
|
self.save_i8(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_i16(frame, c, z, t)
|
case np.int16:
|
||||||
case np.int32:
|
self.save_i16(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_i32(frame, c, z, t)
|
case np.int32:
|
||||||
case np.int64:
|
self.save_i32(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_i64(frame, c, z, t)
|
case np.int64:
|
||||||
case np.float32:
|
self.save_i64(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_f32(frame, c, z, t)
|
case np.float32:
|
||||||
case np.float64:
|
self.save_f32(frame, c + cn, z + zn, t + tn)
|
||||||
self.save_f64(frame, c, z, t)
|
case np.float64:
|
||||||
case _:
|
self.save_f64(frame, c + cn, z + zn, t + tn)
|
||||||
raise TypeError(f'Cannot save type {self.dtype}')
|
case _:
|
||||||
|
raise TypeError(f'Cannot save type {self.dtype}')
|
||||||
|
|
||||||
|
def compress_frame(self, frame: ArrayLike) -> tuple[FrameInfo]: # noqa
|
||||||
|
return (frame, None, (0, 0, 0)),
|
||||||
|
|
||||||
|
def get_colormap(colormap: str) -> np.ndarray:
|
||||||
|
colormap = getattr(colorcet, colormap)
|
||||||
|
colormap[0] = '#ffffff'
|
||||||
|
colormap[-1] = '#000000'
|
||||||
|
return np.array([[int(''.join(i), 16) for i in zip(*[iter(s[1:])] * 2)] for s in colormap]).astype('uint8')
|
||||||
|
|
||||||
|
def get_color(color: str) -> np.ndarray:
|
||||||
|
return np.array([int(''.join(i), 16) for i in zip(*[iter(mpl_colors.to_hex(color)[1:])] * 2)]).astype('uint8')
|
||||||
|
|
||||||
|
|
||||||
def tiffwrite(file: str | Path, data: np.ndarray, axes: str = 'TZCXY', dtype: DTypeLike = None, bar: bool = False,
|
def tiffwrite(file: str | Path, data: np.ndarray, axes: str = 'TZCXY', dtype: DTypeLike = None, bar: bool = False,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ classifiers = [
|
|||||||
"Programming Language :: Python :: Implementation :: CPython",
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
]
|
]
|
||||||
dependencies = ["numpy", "tqdm"]
|
dependencies = ["colorcet", "matplotlib", "numpy", "tqdm"]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
|
|||||||
616
src/lib.rs
616
src/lib.rs
@@ -1,31 +1,29 @@
|
|||||||
// #[cfg(not(feature = "nopython"))]
|
// #[cfg(not(feature = "nopython"))]
|
||||||
mod py;
|
mod py;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use chrono::Utc;
|
||||||
|
use ndarray::{s, Array2};
|
||||||
|
use num::traits::ToBytes;
|
||||||
|
use num::{Complex, FromPrimitive, Rational32, Zero};
|
||||||
|
use rayon::prelude::*;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
|
||||||
use anyhow::Result;
|
|
||||||
use num::{Complex, Rational32, Zero};
|
|
||||||
use ndarray::{s, Array2};
|
|
||||||
use num::traits::ToBytes;
|
|
||||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||||
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
use chrono::Utc;
|
|
||||||
use zstd::stream::encode_all;
|
use zstd::stream::encode_all;
|
||||||
use rayon::prelude::*;
|
|
||||||
|
|
||||||
|
|
||||||
const TAG_SIZE: usize = 20;
|
const TAG_SIZE: usize = 20;
|
||||||
const OFFSET_SIZE: usize = 8;
|
const OFFSET_SIZE: usize = 8;
|
||||||
const OFFSET: u64 = 16;
|
const OFFSET: u64 = 16;
|
||||||
const COMPRESSION: u16 = 50000;
|
const COMPRESSION: u16 = 50000;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct IFD {
|
struct IFD {
|
||||||
tags: Vec<Tag>
|
tags: Vec<Tag>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IFD {
|
impl IFD {
|
||||||
@@ -39,12 +37,6 @@ impl IFD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_tags(&mut self, tags: Vec<Tag>) {
|
|
||||||
for tag in tags {
|
|
||||||
self.push_tag(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, ijtifffile: &mut IJTiffFile, where_to_write_offset: u64) -> Result<u64> {
|
fn write(&mut self, ijtifffile: &mut IJTiffFile, where_to_write_offset: u64) -> Result<u64> {
|
||||||
self.tags.sort();
|
self.tags.sort();
|
||||||
ijtifffile.file.seek(SeekFrom::End(0))?;
|
ijtifffile.file.seek(SeekFrom::End(0))?;
|
||||||
@@ -52,7 +44,9 @@ impl IFD {
|
|||||||
ijtifffile.file.write(&[0])?;
|
ijtifffile.file.write(&[0])?;
|
||||||
}
|
}
|
||||||
let offset = ijtifffile.file.stream_position()?;
|
let offset = ijtifffile.file.stream_position()?;
|
||||||
ijtifffile.file.write(&(self.tags.len() as u64).to_le_bytes())?;
|
ijtifffile
|
||||||
|
.file
|
||||||
|
.write(&(self.tags.len() as u64).to_le_bytes())?;
|
||||||
|
|
||||||
for tag in self.tags.iter_mut() {
|
for tag in self.tags.iter_mut() {
|
||||||
tag.write_tag(ijtifffile)?;
|
tag.write_tag(ijtifffile)?;
|
||||||
@@ -62,19 +56,20 @@ impl IFD {
|
|||||||
for tag in self.tags.iter() {
|
for tag in self.tags.iter() {
|
||||||
tag.write_data(ijtifffile)?;
|
tag.write_data(ijtifffile)?;
|
||||||
}
|
}
|
||||||
ijtifffile.file.seek(SeekFrom::Start(where_to_write_offset))?;
|
ijtifffile
|
||||||
|
.file
|
||||||
|
.seek(SeekFrom::Start(where_to_write_offset))?;
|
||||||
ijtifffile.file.write(&offset.to_le_bytes())?;
|
ijtifffile.file.write(&offset.to_le_bytes())?;
|
||||||
Ok(where_to_write_next_ifd_offset)
|
Ok(where_to_write_next_ifd_offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq)]
|
#[derive(Clone, Debug, Eq)]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
code: u16,
|
code: u16,
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
ttype: u16,
|
ttype: u16,
|
||||||
offset: u64
|
offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd<Self> for Tag {
|
impl PartialOrd<Self> for Tag {
|
||||||
@@ -97,11 +92,16 @@ impl PartialEq for Tag {
|
|||||||
|
|
||||||
impl Tag {
|
impl Tag {
|
||||||
pub fn new(code: u16, bytes: Vec<u8>, ttype: u16) -> Self {
|
pub fn new(code: u16, bytes: Vec<u8>, ttype: u16) -> Self {
|
||||||
Tag { code, bytes, ttype, offset: 0 }
|
Tag {
|
||||||
|
code,
|
||||||
|
bytes,
|
||||||
|
ttype,
|
||||||
|
offset: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn byte(code: u16, byte: Vec<u8>) -> Self {
|
pub fn byte(code: u16, byte: &Vec<u8>) -> Self {
|
||||||
Tag::new(code, byte, 1)
|
Tag::new(code, byte.to_owned(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ascii(code: u16, ascii: &str) -> Self {
|
pub fn ascii(code: u16, ascii: &str) -> Self {
|
||||||
@@ -110,96 +110,213 @@ impl Tag {
|
|||||||
Tag::new(code, bytes, 2)
|
Tag::new(code, bytes, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn short(code: u16, short: Vec<u16>) -> Self {
|
pub fn short(code: u16, short: &Vec<u16>) -> Self {
|
||||||
Tag::new(code, short.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 3)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
short
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
3,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn long(code: u16, long: Vec<u32>) -> Self {
|
pub fn long(code: u16, long: &Vec<u32>) -> Self {
|
||||||
Tag::new(code, long.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 4)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
long.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
4,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rational(code: u16, rational: Vec<Rational32>) -> Self {
|
pub fn rational(code: u16, rational: &Vec<Rational32>) -> Self {
|
||||||
Tag::new(code, rational.into_iter().map(|x|
|
Tag::new(
|
||||||
u32::try_from(*x.denom()).unwrap().to_le_bytes().into_iter().chain(
|
code,
|
||||||
u32::try_from(*x.numer()).unwrap().to_le_bytes()).collect::<Vec<_>>()
|
rational
|
||||||
).flatten().collect(), 5)
|
.into_iter()
|
||||||
|
.map(|x| {
|
||||||
|
u32::try_from(*x.denom())
|
||||||
|
.unwrap()
|
||||||
|
.to_le_bytes()
|
||||||
|
.into_iter()
|
||||||
|
.chain(u32::try_from(*x.numer()).unwrap().to_le_bytes())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
5,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sbyte(code: u16, sbyte: Vec<i8>) -> Self {
|
pub fn sbyte(code: u16, sbyte: &Vec<i8>) -> Self {
|
||||||
Tag::new(code, sbyte.iter().map(|x| x.to_le_bytes()).flatten().collect(), 6)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
sbyte.iter().map(|x| x.to_le_bytes()).flatten().collect(),
|
||||||
|
6,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sshort(code: u16, sshort: Vec<i16>) -> Self {
|
pub fn sshort(code: u16, sshort: &Vec<i16>) -> Self {
|
||||||
Tag::new(code, sshort.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 8)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
sshort
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
8,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slong(code: u16, slong: Vec<i32>) -> Self {
|
pub fn slong(code: u16, slong: &Vec<i32>) -> Self {
|
||||||
Tag::new(code, slong.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 9)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
slong
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
9,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn srational(code: u16, srational: Vec<Rational32>) -> Self {
|
pub fn srational(code: u16, srational: &Vec<Rational32>) -> Self {
|
||||||
Tag::new(code, srational.into_iter().map(|x|
|
Tag::new(
|
||||||
i32::try_from(*x.denom()).unwrap().to_le_bytes().into_iter().chain(
|
code,
|
||||||
i32::try_from(*x.numer()).unwrap().to_le_bytes()).collect::<Vec<_>>()
|
srational
|
||||||
).flatten().collect(), 10)
|
.into_iter()
|
||||||
|
.map(|x| {
|
||||||
|
i32::try_from(*x.denom())
|
||||||
|
.unwrap()
|
||||||
|
.to_le_bytes()
|
||||||
|
.into_iter()
|
||||||
|
.chain(i32::try_from(*x.numer()).unwrap().to_le_bytes())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
10,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn float(code: u16, float: Vec<f32>) -> Self {
|
pub fn float(code: u16, float: &Vec<f32>) -> Self {
|
||||||
Tag::new(code, float.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 11)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
float
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
11,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn double(code: u16, double: Vec<f64>) -> Self {
|
pub fn double(code: u16, double: &Vec<f64>) -> Self {
|
||||||
Tag::new(code, double.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 12)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
double
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
12,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ifd(code: u16, ifd: Vec<u32>) -> Self {
|
pub fn ifd(code: u16, ifd: &Vec<u32>) -> Self {
|
||||||
Tag::new(code, ifd.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 13)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
ifd.into_iter().map(|x| x.to_le_bytes()).flatten().collect(),
|
||||||
|
13,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unicode(code: u16, unicode: &str) -> Self {
|
pub fn unicode(code: u16, unicode: &str) -> Self {
|
||||||
let mut bytes: Vec<u8> = unicode.encode_utf16().map(|x| x.to_le_bytes()).flatten().collect();
|
let mut bytes: Vec<u8> = unicode
|
||||||
|
.encode_utf16()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
bytes.push(0);
|
bytes.push(0);
|
||||||
Tag::new(code, bytes, 14)
|
Tag::new(code, bytes, 14)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complex(code: u16, complex: Vec<Complex<f32>>) -> Self {
|
pub fn complex(code: u16, complex: &Vec<Complex<f32>>) -> Self {
|
||||||
Tag::new(code, complex.into_iter().map(|x|
|
Tag::new(
|
||||||
x.re.to_le_bytes().into_iter().chain(x.im.to_le_bytes()).collect::<Vec<_>>()
|
code,
|
||||||
).flatten().collect(), 15)
|
complex
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| {
|
||||||
|
x.re.to_le_bytes()
|
||||||
|
.into_iter()
|
||||||
|
.chain(x.im.to_le_bytes())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
15,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn long8(code: u16, long8: Vec<u64>) -> Self {
|
pub fn long8(code: u16, long8: &Vec<u64>) -> Self {
|
||||||
Tag::new(code, long8.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 16)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
long8
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
16,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slong8(code: u16, slong8: Vec<i64>) -> Self {
|
pub fn slong8(code: u16, slong8: &Vec<i64>) -> Self {
|
||||||
Tag::new(code, slong8.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 17)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
slong8
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
17,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ifd8(code: u16, ifd8: Vec<u64>) -> Self {
|
pub fn ifd8(code: u16, ifd8: &Vec<u64>) -> Self {
|
||||||
Tag::new(code, ifd8.into_iter().map(|x| x.to_le_bytes()).flatten().collect(), 18)
|
Tag::new(
|
||||||
|
code,
|
||||||
|
ifd8.into_iter()
|
||||||
|
.map(|x| x.to_le_bytes())
|
||||||
|
.flatten()
|
||||||
|
.collect(),
|
||||||
|
18,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> u64 {
|
pub fn count(&self) -> u64 {
|
||||||
let c = match self.ttype {
|
let c = match self.ttype {
|
||||||
1 => self.bytes.len(), // BYTE
|
1 => self.bytes.len(), // BYTE
|
||||||
2 => self.bytes.len(), // ASCII
|
2 => self.bytes.len(), // ASCII
|
||||||
3 => self.bytes.len() / 2, // SHORT
|
3 => self.bytes.len() / 2, // SHORT
|
||||||
4 => self.bytes.len() / 4, // LONG
|
4 => self.bytes.len() / 4, // LONG
|
||||||
5 => self.bytes.len() / 8, // RATIONAL
|
5 => self.bytes.len() / 8, // RATIONAL
|
||||||
6 => self.bytes.len(), // SBYTE
|
6 => self.bytes.len(), // SBYTE
|
||||||
7 => self.bytes.len(), // UNDEFINED
|
7 => self.bytes.len(), // UNDEFINED
|
||||||
8 => self.bytes.len() / 2, // SSHORT
|
8 => self.bytes.len() / 2, // SSHORT
|
||||||
9 => self.bytes.len() / 4, // SLONG
|
9 => self.bytes.len() / 4, // SLONG
|
||||||
10 => self.bytes.len() / 8, // SRATIONAL
|
10 => self.bytes.len() / 8, // SRATIONAL
|
||||||
11 => self.bytes.len() / 4, // FLOAT
|
11 => self.bytes.len() / 4, // FLOAT
|
||||||
12 => self.bytes.len() / 8, // DOUBLE
|
12 => self.bytes.len() / 8, // DOUBLE
|
||||||
13 => self.bytes.len() / 4, // IFD
|
13 => self.bytes.len() / 4, // IFD
|
||||||
14 => self.bytes.len() / 2, // UNICODE
|
14 => self.bytes.len() / 2, // UNICODE
|
||||||
15 => self.bytes.len() / 8, // COMPLEX
|
15 => self.bytes.len() / 8, // COMPLEX
|
||||||
16 => self.bytes.len() / 8, // LONG8
|
16 => self.bytes.len() / 8, // LONG8
|
||||||
17 => self.bytes.len() / 8, // SLONG8
|
17 => self.bytes.len() / 8, // SLONG8
|
||||||
18 => self.bytes.len() / 8, // IFD8
|
18 => self.bytes.len() / 8, // IFD8
|
||||||
_ => self.bytes.len(),
|
_ => self.bytes.len(),
|
||||||
};
|
};
|
||||||
c as u64
|
c as u64
|
||||||
@@ -226,7 +343,8 @@ impl Tag {
|
|||||||
ijtifffile.file.seek(SeekFrom::End(0))?;
|
ijtifffile.file.seek(SeekFrom::End(0))?;
|
||||||
let offset = ijtifffile.write(&self.bytes)?;
|
let offset = ijtifffile.write(&self.bytes)?;
|
||||||
ijtifffile.file.seek(SeekFrom::Start(
|
ijtifffile.file.seek(SeekFrom::Start(
|
||||||
self.offset + (TAG_SIZE - OFFSET_SIZE) as u64))?;
|
self.offset + (TAG_SIZE - OFFSET_SIZE) as u64,
|
||||||
|
))?;
|
||||||
ijtifffile.file.write(&offset.to_le_bytes())?;
|
ijtifffile.file.write(&offset.to_le_bytes())?;
|
||||||
if ijtifffile.file.stream_position()? % 2 == 1 {
|
if ijtifffile.file.stream_position()? % 2 == 1 {
|
||||||
ijtifffile.file.write(&[0u8])?;
|
ijtifffile.file.write(&[0u8])?;
|
||||||
@@ -236,17 +354,16 @@ impl Tag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct CompressedFrame {
|
struct CompressedFrame {
|
||||||
bytes: Vec<Vec<u8>>,
|
bytes: Vec<Vec<u8>>,
|
||||||
image_width: u32,
|
image_width: u32,
|
||||||
image_length: u32,
|
image_length: u32,
|
||||||
tile_size: usize,
|
tile_size: usize,
|
||||||
bits_per_sample: u16,
|
bits_per_sample: u16,
|
||||||
sample_format: u16
|
sample_format: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Frame {
|
struct Frame {
|
||||||
tileoffsets: Vec<u64>,
|
tileoffsets: Vec<u64>,
|
||||||
@@ -261,17 +378,28 @@ struct Frame {
|
|||||||
|
|
||||||
impl Frame {
|
impl Frame {
|
||||||
fn new(
|
fn new(
|
||||||
tileoffsets: Vec<u64>, tilebytecounts: Vec<u64>, image_width: u32, image_length: u32,
|
tileoffsets: Vec<u64>,
|
||||||
bits_per_sample: u16, sample_format: u16, tile_width: u16, tile_length: u16
|
tilebytecounts: Vec<u64>,
|
||||||
|
image_width: u32,
|
||||||
|
image_length: u32,
|
||||||
|
bits_per_sample: u16,
|
||||||
|
sample_format: u16,
|
||||||
|
tile_width: u16,
|
||||||
|
tile_length: u16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Frame {
|
Frame {
|
||||||
tileoffsets, tilebytecounts, image_width, image_length, bits_per_sample,
|
tileoffsets,
|
||||||
sample_format, tile_width, tile_length
|
tilebytecounts,
|
||||||
|
image_width,
|
||||||
|
image_length,
|
||||||
|
bits_per_sample,
|
||||||
|
sample_format,
|
||||||
|
tile_width,
|
||||||
|
tile_length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub trait Bytes {
|
pub trait Bytes {
|
||||||
const BITS_PER_SAMPLE: u16;
|
const BITS_PER_SAMPLE: u16;
|
||||||
const SAMPLE_FORMAT: u16;
|
const SAMPLE_FORMAT: u16;
|
||||||
@@ -279,7 +407,6 @@ pub trait Bytes {
|
|||||||
fn bytes(&self) -> Vec<u8>;
|
fn bytes(&self) -> Vec<u8>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
macro_rules! bytes_impl {
|
macro_rules! bytes_impl {
|
||||||
($T:ty, $bits_per_sample:expr, $sample_format:expr) => {
|
($T:ty, $bits_per_sample:expr, $sample_format:expr) => {
|
||||||
impl Bytes for $T {
|
impl Bytes for $T {
|
||||||
@@ -315,23 +442,26 @@ bytes_impl!(isize, 32, 2);
|
|||||||
bytes_impl!(f32, 32, 3);
|
bytes_impl!(f32, 32, 3);
|
||||||
bytes_impl!(f64, 64, 3);
|
bytes_impl!(f64, 64, 3);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Colors {
|
||||||
|
None,
|
||||||
|
Colors(Vec<Vec<u8>>),
|
||||||
|
Colormap(Vec<Vec<u8>>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IJTiffFile {
|
pub struct IJTiffFile {
|
||||||
file: File,
|
file: File,
|
||||||
frames: HashMap<(usize, u8), Frame>,
|
frames: HashMap<(usize, usize, usize), Frame>,
|
||||||
hashes: HashMap<u64, u64>,
|
hashes: HashMap<u64, u64>,
|
||||||
threads: HashMap<(usize, u8), JoinHandle<CompressedFrame>>,
|
threads: HashMap<(usize, usize, usize), JoinHandle<CompressedFrame>>,
|
||||||
pub shape: (usize, usize, usize),
|
pub shape: (usize, usize, usize),
|
||||||
pub n_frames: usize,
|
pub colors: Colors,
|
||||||
pub samples_per_pixel: u8,
|
|
||||||
pub colormap: Option<Vec<u16>>,
|
|
||||||
pub colors: Option<Vec<(u8, u8, u8)>>,
|
|
||||||
pub comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
|
pub px_size: Option<f64>,
|
||||||
pub delta_z: Option<f64>,
|
pub delta_z: Option<f64>,
|
||||||
pub timeinterval: Option<f64>,
|
pub time_interval: Option<f64>,
|
||||||
pub extra_tags: Vec<Tag>,
|
pub extra_tags: HashMap<Option<(usize, usize, usize)>, Vec<Tag>>,
|
||||||
pub extra_tags_frame: HashMap<usize, Vec<Tag>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for IJTiffFile {
|
impl Drop for IJTiffFile {
|
||||||
@@ -344,28 +474,35 @@ impl Drop for IJTiffFile {
|
|||||||
|
|
||||||
impl IJTiffFile {
|
impl IJTiffFile {
|
||||||
pub fn new(path: &str, shape: (usize, usize, usize)) -> Result<Self> {
|
pub fn new(path: &str, shape: (usize, usize, usize)) -> Result<Self> {
|
||||||
let mut file = OpenOptions::new().create(true).truncate(true)
|
let mut file = OpenOptions::new()
|
||||||
.write(true).read(true).open(path)?;
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.write(true)
|
||||||
|
.read(true)
|
||||||
|
.open(path)?;
|
||||||
file.write(b"II")?;
|
file.write(b"II")?;
|
||||||
file.write(&43u16.to_le_bytes())?;
|
file.write(&43u16.to_le_bytes())?;
|
||||||
file.write(&8u16.to_le_bytes())?;
|
file.write(&8u16.to_le_bytes())?;
|
||||||
file.write(&0u16.to_le_bytes())?;
|
file.write(&0u16.to_le_bytes())?;
|
||||||
file.write(&OFFSET.to_le_bytes())?;
|
file.write(&OFFSET.to_le_bytes())?;
|
||||||
let colormap: Option<Vec<(u8, u8, u8)>> = None;
|
Ok(IJTiffFile {
|
||||||
let (spp, n_frames) = if let None = &colormap {
|
file,
|
||||||
(shape.0 as u8, shape.1 * shape.2)
|
frames: HashMap::new(),
|
||||||
} else {
|
hashes: HashMap::new(),
|
||||||
(1, shape.0 * shape.1 * shape.2)
|
threads: HashMap::new(),
|
||||||
};
|
shape,
|
||||||
Ok(IJTiffFile { file, frames: HashMap::new(), hashes: HashMap::new(),
|
colors: Colors::None,
|
||||||
threads: HashMap::new(), shape, n_frames,
|
comment: None,
|
||||||
samples_per_pixel: spp, colormap: None, colors: None, comment: None, delta_z: None,
|
px_size: None,
|
||||||
timeinterval: None, extra_tags: Vec::new(), extra_tags_frame: HashMap::new() } )
|
delta_z: None,
|
||||||
|
time_interval: None,
|
||||||
|
extra_tags: HashMap::new(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn description(&self) -> String {
|
pub fn description(&self) -> String {
|
||||||
let mut desc: String = String::from("ImageJ=1.11a");
|
let mut desc: String = String::from("ImageJ=1.11a");
|
||||||
if let (None, None) = (self.colormap.as_ref(), self.colors.as_ref()) {
|
if let Colors::None = self.colors {
|
||||||
desc += &format!("\nimages={}", self.shape.0);
|
desc += &format!("\nimages={}", self.shape.0);
|
||||||
desc += &format!("\nslices={}", self.shape.1);
|
desc += &format!("\nslices={}", self.shape.1);
|
||||||
desc += &format!("\nframes={}", self.shape.2);
|
desc += &format!("\nframes={}", self.shape.2);
|
||||||
@@ -384,7 +521,7 @@ impl IJTiffFile {
|
|||||||
if let Some(delta_z) = self.delta_z {
|
if let Some(delta_z) = self.delta_z {
|
||||||
desc += &format!("\nspacing={}", delta_z);
|
desc += &format!("\nspacing={}", delta_z);
|
||||||
}
|
}
|
||||||
if let Some(timeinterval) = self.timeinterval {
|
if let Some(timeinterval) = self.time_interval {
|
||||||
desc += &format!("\ninterval={}", timeinterval);
|
desc += &format!("\ninterval={}", timeinterval);
|
||||||
}
|
}
|
||||||
if let Some(comment) = &self.comment {
|
if let Some(comment) = &self.comment {
|
||||||
@@ -393,11 +530,27 @@ impl IJTiffFile {
|
|||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_frame_number(&self, c: usize, z: usize, t: usize) -> (usize, u8) {
|
fn get_czt(&self, frame_number: usize, channel: u8) -> (usize, usize, usize) {
|
||||||
if let (None, None) = (self.colormap.as_ref(), self.colors.as_ref()) {
|
if let Colors::None = self.colors {
|
||||||
(z + t * self.shape.1, c as u8)
|
(
|
||||||
|
channel as usize,
|
||||||
|
frame_number % self.shape.1,
|
||||||
|
frame_number / self.shape.1,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
(c + z * self.shape.0 + t * self.shape.0 * self.shape.1, 0)
|
(
|
||||||
|
frame_number % self.shape.0,
|
||||||
|
frame_number / self.shape.0 % self.shape.1,
|
||||||
|
frame_number / self.shape.0 / self.shape.1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spp_and_n_frames(&self) -> (u8, usize) {
|
||||||
|
if let Colors::None = &self.colors {
|
||||||
|
(self.shape.0 as u8, self.shape.1 * self.shape.2)
|
||||||
|
} else {
|
||||||
|
(1, self.shape.0 * self.shape.1 * self.shape.2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,7 +572,9 @@ impl IJTiffFile {
|
|||||||
|
|
||||||
fn write(&mut self, bytes: &Vec<u8>) -> Result<u64> {
|
fn write(&mut self, bytes: &Vec<u8>) -> Result<u64> {
|
||||||
let hash = IJTiffFile::hash(&bytes);
|
let hash = IJTiffFile::hash(&bytes);
|
||||||
if self.hashes.contains_key(&hash) && self.hash_check(&bytes, *self.hashes.get(&hash).unwrap())? {
|
if self.hashes.contains_key(&hash)
|
||||||
|
&& self.hash_check(&bytes, *self.hashes.get(&hash).unwrap())?
|
||||||
|
{
|
||||||
Ok(*self.hashes.get(&hash).unwrap())
|
Ok(*self.hashes.get(&hash).unwrap())
|
||||||
} else {
|
} else {
|
||||||
if self.file.stream_position()? % 2 == 1 {
|
if self.file.stream_position()? % 2 == 1 {
|
||||||
@@ -432,64 +587,88 @@ impl IJTiffFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save<T>(&mut self, frame: Array2<T>, c: usize, z: usize, t: usize,
|
pub fn save<T>(&mut self, frame: Array2<T>, c: usize, z: usize, t: usize) -> Result<()>
|
||||||
extra_tags: Option<Vec<Tag>>) -> Result<()>
|
where
|
||||||
where T: Bytes + Clone + Send + Sync + Zero + 'static {
|
T: Bytes + Clone + Send + Sync + Zero + 'static,
|
||||||
let key = self.get_frame_number(c, z, t);
|
{
|
||||||
if let Some(extra_tags) = extra_tags {
|
self.compress_frame(frame.reversed_axes(), c, z, t)?;
|
||||||
if let Some(extra_tags_frame) = self.extra_tags_frame.get_mut(&key.0) {
|
|
||||||
extra_tags_frame.extend(extra_tags);
|
|
||||||
} else {
|
|
||||||
self.extra_tags_frame.insert(key.0, extra_tags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.compress_frame(frame.reversed_axes(), key)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compress_frame<T>(&mut self, frame: Array2<T>, key: (usize, u8)) -> Result<()>
|
fn compress_frame<T>(&mut self, frame: Array2<T>, c: usize, z: usize, t: usize) -> Result<()>
|
||||||
where T: Bytes + Clone + Zero + Send + 'static {
|
where
|
||||||
|
T: Bytes + Clone + Zero + Send + 'static,
|
||||||
|
{
|
||||||
fn compress<T>(frame: Array2<T>) -> CompressedFrame
|
fn compress<T>(frame: Array2<T>) -> CompressedFrame
|
||||||
where T: Bytes + Clone + Zero {
|
where
|
||||||
|
T: Bytes + Clone + Zero,
|
||||||
|
{
|
||||||
let image_width = frame.shape()[0] as u32;
|
let image_width = frame.shape()[0] as u32;
|
||||||
let image_length = frame.shape()[1] as u32;
|
let image_length = frame.shape()[1] as u32;
|
||||||
let tile_size = 2usize.pow(((image_width as f64 * image_length as f64 / 64f64
|
let tile_size = 2usize
|
||||||
).log2() / 2f64).round() as u32).max(16).min(1024);
|
.pow(
|
||||||
|
((image_width as f64 * image_length as f64 / 64f64).log2() / 2f64).round()
|
||||||
|
as u32,
|
||||||
|
)
|
||||||
|
.max(16)
|
||||||
|
.min(1024);
|
||||||
let tiles = IJTiffFile::tile(frame.reversed_axes(), tile_size);
|
let tiles = IJTiffFile::tile(frame.reversed_axes(), tile_size);
|
||||||
let byte_tiles: Vec<Vec<u8>> = tiles.into_iter().map(
|
let byte_tiles: Vec<Vec<u8>> = tiles
|
||||||
|tile| tile.map(|x| x.bytes()).into_iter().flatten().collect()
|
.into_iter()
|
||||||
).collect();
|
.map(|tile| tile.map(|x| x.bytes()).into_iter().flatten().collect())
|
||||||
let bytes = byte_tiles.into_par_iter().map(|x| encode_all(&*x, 3).unwrap()).collect::<Vec<_>>();
|
.collect();
|
||||||
CompressedFrame { bytes, image_width, image_length, tile_size,
|
let bytes = byte_tiles
|
||||||
bits_per_sample: T::BITS_PER_SAMPLE, sample_format: T::SAMPLE_FORMAT }
|
.into_par_iter()
|
||||||
}
|
.map(|x| encode_all(&*x, 3).unwrap())
|
||||||
self.threads.insert(key, thread::spawn(move || compress(frame)));
|
.collect::<Vec<_>>();
|
||||||
for key in self.threads.keys().cloned().collect::<Vec<(usize, u8)>>() {
|
CompressedFrame {
|
||||||
if self.threads[&key].is_finished() {
|
bytes,
|
||||||
|
image_width,
|
||||||
|
image_length,
|
||||||
|
tile_size,
|
||||||
|
bits_per_sample: T::BITS_PER_SAMPLE,
|
||||||
|
sample_format: T::SAMPLE_FORMAT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.threads
|
||||||
|
.insert((c, z, t), thread::spawn(move || compress(frame)));
|
||||||
|
for key in self
|
||||||
|
.threads
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<(usize, usize, usize)>>()
|
||||||
|
{
|
||||||
|
if self.threads[&key].is_finished() {}
|
||||||
|
}
|
||||||
|
|
||||||
for key in self.threads.keys().cloned().collect::<Vec<(usize, u8)>>() {
|
for (c, z, t) in self.threads.keys().cloned().collect::<Vec<_>>() {
|
||||||
if self.threads[&key].is_finished() {
|
if self.threads[&(c, z, t)].is_finished() {
|
||||||
if let Some(thread) = self.threads.remove(&key) {
|
if let Some(thread) = self.threads.remove(&(c, z, t)) {
|
||||||
self.write_frame(thread.join().unwrap(), key)?;
|
self.write_frame(thread.join().unwrap(), c, z, t)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_frame(&mut self, frame: CompressedFrame, key: (usize, u8)) -> Result<()> {
|
fn write_frame(&mut self, frame: CompressedFrame, c: usize, z: usize, t: usize) -> Result<()> {
|
||||||
let mut tileoffsets = Vec::new();
|
let mut tileoffsets = Vec::new();
|
||||||
let mut tilebytecounts = Vec::new();
|
let mut tilebytecounts = Vec::new();
|
||||||
for tile in frame.bytes {
|
for tile in frame.bytes {
|
||||||
tilebytecounts.push(tile.len() as u64);
|
tilebytecounts.push(tile.len() as u64);
|
||||||
tileoffsets.push(self.write(&tile)?);
|
tileoffsets.push(self.write(&tile)?);
|
||||||
}
|
}
|
||||||
let frame = Frame::new(tileoffsets, tilebytecounts, frame.image_width, frame.image_length,
|
let frame = Frame::new(
|
||||||
frame.bits_per_sample, frame.sample_format, frame.tile_size as u16, frame.tile_size as u16);
|
tileoffsets,
|
||||||
self.frames.insert(key, frame);
|
tilebytecounts,
|
||||||
|
frame.image_width,
|
||||||
|
frame.image_length,
|
||||||
|
frame.bits_per_sample,
|
||||||
|
frame.sample_format,
|
||||||
|
frame.tile_size as u16,
|
||||||
|
frame.tile_size as u16,
|
||||||
|
);
|
||||||
|
self.frames.insert((c, z, t), frame);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,59 +678,74 @@ impl IJTiffFile {
|
|||||||
let (n, m) = (shape[0] / size, shape[1] / size);
|
let (n, m) = (shape[0] / size, shape[1] / size);
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
for j in 0..m {
|
for j in 0..m {
|
||||||
tiles.push(frame.slice(
|
tiles.push(
|
||||||
s![i * size..(i + 1) * size, j * size..(j + 1) * size]).to_owned());
|
frame
|
||||||
|
.slice(s![i * size..(i + 1) * size, j * size..(j + 1) * size])
|
||||||
|
.to_owned(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if shape[1] % size != 0 {
|
if shape[1] % size != 0 {
|
||||||
let mut tile = Array2::<T>::zeros((size, size));
|
let mut tile = Array2::<T>::zeros((size, size));
|
||||||
tile.slice_mut(
|
tile.slice_mut(s![.., ..shape[1] - m * size])
|
||||||
s![.., ..shape[1] - m * size]
|
.assign(&frame.slice(s![i * size..(i + 1) * size, m * size..]));
|
||||||
).assign(&frame.slice(s![i * size..(i + 1) * size, m * size..]));
|
|
||||||
tiles.push(tile);
|
tiles.push(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if shape[0] % size != 0 {
|
if shape[0] % size != 0 {
|
||||||
for j in 0..m {
|
for j in 0..m {
|
||||||
let mut tile = Array2::<T>::zeros((size, size));
|
let mut tile = Array2::<T>::zeros((size, size));
|
||||||
tile.slice_mut(
|
tile.slice_mut(s![..shape[0] - n * size, ..])
|
||||||
s![..shape[0] - n * size, ..]
|
.assign(&frame.slice(s![n * size.., j * size..(j + 1) * size]));
|
||||||
).assign(&frame.slice(s![n * size.., j * size..(j + 1) * size]));
|
|
||||||
tiles.push(tile);
|
tiles.push(tile);
|
||||||
}
|
}
|
||||||
if shape[1] % size != 0 {
|
if shape[1] % size != 0 {
|
||||||
let mut tile = Array2::<T>::zeros((size, size));
|
let mut tile = Array2::<T>::zeros((size, size));
|
||||||
tile.slice_mut(
|
tile.slice_mut(s![..shape[0] - n * size, ..shape[1] - m * size])
|
||||||
s![..shape[0] - n * size, ..shape[1] - m * size]
|
.assign(&frame.slice(s![n * size.., m * size..]));
|
||||||
).assign(&frame.slice(s![n * size.., m * size..]));
|
|
||||||
tiles.push(tile);
|
tiles.push(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tiles
|
tiles
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_colormap(&self, _colormap: &Vec<u16>) -> Result<Vec<u16>> {
|
fn get_colormap(&self, colormap: &Vec<Vec<u8>>, bits_per_sample: u16) -> Vec<u16> {
|
||||||
todo!();
|
if bits_per_sample == 8 {
|
||||||
|
colormap
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.map(|x| (*x as u16) * 256)
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
colormap
|
||||||
|
.iter()
|
||||||
|
.map(|x| vec![x; 256])
|
||||||
|
.flatten()
|
||||||
|
.flatten()
|
||||||
|
.map(|x| (*x as u16) * 256)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_color(&self, _colors: (u8, u8, u8)) -> Result<Vec<u16>> {
|
fn get_color(&self, _colors: &Vec<u8>, _bits_per_sample: u16) -> Result<Vec<u16>> {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&mut self) -> Result<()> {
|
fn close(&mut self) -> Result<()> {
|
||||||
for key in self.threads.keys().cloned().collect::<Vec<(usize, u8)>>() {
|
for (c, z, t) in self.threads.keys().cloned().collect::<Vec<_>>() {
|
||||||
if let Some(thread) = self.threads.remove(&key) {
|
if let Some(thread) = self.threads.remove(&(c, z, t)) {
|
||||||
self.write_frame(thread.join().unwrap(), key)?;
|
self.write_frame(thread.join().unwrap(), c, z, t)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut where_to_write_next_ifd_offset = OFFSET - OFFSET_SIZE as u64;
|
let mut where_to_write_next_ifd_offset = OFFSET - OFFSET_SIZE as u64;
|
||||||
let mut warn = false;
|
let mut warn = false;
|
||||||
for frame_number in 0..self.n_frames {
|
let (samples_per_pixel, n_frames) = self.spp_and_n_frames();
|
||||||
if let Some(frame) = self.frames.get(&(frame_number, 0)) {
|
for frame_number in 0..n_frames {
|
||||||
|
if let Some(frame) = self.frames.get(&self.get_czt(frame_number, 0)) {
|
||||||
let mut tileoffsets = Vec::new();
|
let mut tileoffsets = Vec::new();
|
||||||
let mut tilebytecounts = Vec::new();
|
let mut tilebytecounts = Vec::new();
|
||||||
let mut frame_count = 0;
|
let mut frame_count = 0;
|
||||||
for channel in 0..self.samples_per_pixel {
|
for channel in 0..samples_per_pixel {
|
||||||
if let Some(frame_n) = self.frames.get(&(frame_number, channel)) {
|
if let Some(frame_n) = self.frames.get(&self.get_czt(frame_number, channel)) {
|
||||||
tileoffsets.extend(frame_n.tileoffsets.iter());
|
tileoffsets.extend(frame_n.tileoffsets.iter());
|
||||||
tilebytecounts.extend(frame_n.tilebytecounts.iter());
|
tilebytecounts.extend(frame_n.tilebytecounts.iter());
|
||||||
frame_count += 1;
|
frame_count += 1;
|
||||||
@@ -560,52 +754,80 @@ impl IJTiffFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut ifd = IFD::new();
|
let mut ifd = IFD::new();
|
||||||
ifd.push_tag(Tag::long(256, vec![frame.image_width]));
|
ifd.push_tag(Tag::long(256, &vec![frame.image_width]));
|
||||||
ifd.push_tag(Tag::long(257, vec![frame.image_length]));
|
ifd.push_tag(Tag::long(257, &vec![frame.image_length]));
|
||||||
ifd.push_tag(Tag::short(258, vec![frame.bits_per_sample; frame_count]));
|
ifd.push_tag(Tag::short(258, &vec![frame.bits_per_sample; frame_count]));
|
||||||
ifd.push_tag(Tag::short(259, vec![COMPRESSION]));
|
ifd.push_tag(Tag::short(259, &vec![COMPRESSION]));
|
||||||
ifd.push_tag(Tag::ascii(270, &self.description()));
|
ifd.push_tag(Tag::ascii(270, &self.description()));
|
||||||
ifd.push_tag(Tag::short(277, vec![frame_count as u16]));
|
ifd.push_tag(Tag::short(277, &vec![frame_count as u16]));
|
||||||
ifd.push_tag(Tag::ascii(305, "tiffwrite_rs"));
|
ifd.push_tag(Tag::ascii(305, "tiffwrite_rs"));
|
||||||
ifd.push_tag(Tag::short(322, vec![frame.tile_width]));
|
ifd.push_tag(Tag::short(322, &vec![frame.tile_width]));
|
||||||
ifd.push_tag(Tag::short(323, vec![frame.tile_length]));
|
ifd.push_tag(Tag::short(323, &vec![frame.tile_length]));
|
||||||
ifd.push_tag(Tag::long8(324, tileoffsets));
|
ifd.push_tag(Tag::long8(324, &tileoffsets));
|
||||||
ifd.push_tag(Tag::long8(325, tilebytecounts));
|
ifd.push_tag(Tag::long8(325, &tilebytecounts));
|
||||||
ifd.push_tag(Tag::short(339, vec![frame.sample_format]));
|
ifd.push_tag(Tag::short(339, &vec![frame.sample_format]));
|
||||||
|
if let Some(px_size) = self.px_size {
|
||||||
|
let r = vec![Rational32::from_f64(px_size).unwrap()];
|
||||||
|
ifd.push_tag(Tag::rational(282, &r));
|
||||||
|
ifd.push_tag(Tag::rational(283, &r));
|
||||||
|
ifd.push_tag(Tag::short(296, &vec![1]));
|
||||||
|
}
|
||||||
|
|
||||||
if frame_number == 0 {
|
if frame_number == 0 {
|
||||||
if let Some(colormap) = &self.colormap {
|
if let Colors::Colormap(colormap) = &self.colors {
|
||||||
ifd.push_tag(Tag::short(320, self.get_colormap(colormap)?));
|
ifd.push_tag(Tag::short(
|
||||||
ifd.push_tag(Tag::short(262, vec![3])); // PhotometricInterpretation PHOTOMETRIC_PALETTE
|
320,
|
||||||
} else if let None = self.colors {
|
&self.get_colormap(colormap, frame.bits_per_sample),
|
||||||
ifd.push_tag(Tag::short(262, vec![1])); // PhotometricInterpretation PHOTOMETRIC_PALETTE
|
));
|
||||||
|
ifd.push_tag(Tag::short(262, &vec![3]));
|
||||||
|
} else if let Colors::None = self.colors {
|
||||||
|
ifd.push_tag(Tag::short(262, &vec![1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if frame_number < self.samples_per_pixel as usize {
|
if frame_number < samples_per_pixel as usize {
|
||||||
if let Some(color) = &self.colors {
|
if let Colors::Colors(colors) = &self.colors {
|
||||||
ifd.push_tag(Tag::short(320, self.get_color(color[frame_number])?));
|
ifd.push_tag(Tag::short(
|
||||||
ifd.push_tag(Tag::short(262, vec![3])); // PhotometricInterpretation PHOTOMETRIC_PALETTE
|
320,
|
||||||
|
&self.get_color(&colors[frame_number], frame.bits_per_sample)?,
|
||||||
|
));
|
||||||
|
ifd.push_tag(Tag::short(262, &vec![3]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let (None, None) = (&self.colormap, &self.colors) {
|
if let Colors::None = &self.colors {
|
||||||
if self.shape.0 > 1 {
|
if self.shape.0 > 1 {
|
||||||
ifd.push_tag(Tag::short(284, vec![2]))
|
ifd.push_tag(Tag::short(284, &vec![2]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.extra_tags_frame.contains_key(&frame_number) {
|
for channel in 0..samples_per_pixel {
|
||||||
ifd.extend_tags(self.extra_tags_frame[&frame_number].to_owned());
|
let czt = self.get_czt(frame_number, channel);
|
||||||
|
if let Some(extra_tags) = self.extra_tags.get(&Some(czt)) {
|
||||||
|
for tag in extra_tags {
|
||||||
|
ifd.push_tag(tag.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ifd.extend_tags(self.extra_tags.to_owned());
|
if let Some(extra_tags) = self.extra_tags.get(&None) {
|
||||||
ifd.push_tag(Tag::ascii(306, &format!("{}", Utc::now().format("%Y:%m:%d %H:%M:%S"))));
|
for tag in extra_tags {
|
||||||
|
ifd.push_tag(tag.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ifd.push_tag(Tag::ascii(
|
||||||
|
306,
|
||||||
|
&format!("{}", Utc::now().format("%Y:%m:%d %H:%M:%S")),
|
||||||
|
));
|
||||||
where_to_write_next_ifd_offset = ifd.write(self, where_to_write_next_ifd_offset)?;
|
where_to_write_next_ifd_offset = ifd.write(self, where_to_write_next_ifd_offset)?;
|
||||||
} else {
|
} else {
|
||||||
warn = true;
|
warn = true;
|
||||||
}
|
}
|
||||||
if warn {
|
if warn {
|
||||||
println!("Some frames were not added to the tif file, either you forgot them, \
|
println!(
|
||||||
or an error occurred and the tif file was closed prematurely.")
|
"Some frames were not added to the tif file, either you forgot them, \
|
||||||
|
or an error occurred and the tif file was closed prematurely."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.file.seek(SeekFrom::Start(where_to_write_next_ifd_offset))?;
|
self.file
|
||||||
|
.seek(SeekFrom::Start(where_to_write_next_ifd_offset))?;
|
||||||
self.file.write(&0u64.to_le_bytes())?;
|
self.file.write(&0u64.to_le_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use anyhow::Result;
|
|||||||
use ndarray::{s, Array2};
|
use ndarray::{s, Array2};
|
||||||
use tiffwrite::IJTiffFile;
|
use tiffwrite::IJTiffFile;
|
||||||
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
println!("Hello World!");
|
println!("Hello World!");
|
||||||
let mut f = IJTiffFile::new("foo.tif", (2, 1, 1))?;
|
let mut f = IJTiffFile::new("foo.tif", (2, 1, 1))?;
|
||||||
@@ -12,12 +11,12 @@ fn main() -> Result<()> {
|
|||||||
arr[[i, j]] = i as u16;
|
arr[[i, j]] = i as u16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.save(arr.to_owned(), 0, 0, 0, None)?;
|
f.save(arr.to_owned(), 0, 0, 0)?;
|
||||||
|
|
||||||
let mut arr = Array2::<u16>::zeros((100, 100));
|
let mut arr = Array2::<u16>::zeros((100, 100));
|
||||||
arr.slice_mut(s![64.., ..64]).fill(1);
|
arr.slice_mut(s![64.., ..64]).fill(1);
|
||||||
arr.slice_mut(s![..64, 64..]).fill(2);
|
arr.slice_mut(s![..64, 64..]).fill(2);
|
||||||
arr.slice_mut(s![64.., 64..]).fill(3);
|
arr.slice_mut(s![64.., 64..]).fill(3);
|
||||||
f.save(arr.to_owned(), 1, 0,0, None)?;
|
f.save(arr.to_owned(), 1, 0, 0)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
266
src/py.rs
266
src/py.rs
@@ -1,101 +1,153 @@
|
|||||||
|
use crate::{Colors, IJTiffFile, Tag};
|
||||||
|
use ndarray::s;
|
||||||
|
use num::{Complex, FromPrimitive, Rational32};
|
||||||
|
use numpy::{PyArrayMethods, PyReadonlyArray2};
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use crate::{IJTiffFile, Tag};
|
|
||||||
use num::{Complex, Rational32, FromPrimitive};
|
|
||||||
use numpy::{PyReadonlyArray2, PyArrayMethods};
|
|
||||||
|
|
||||||
|
|
||||||
#[pyclass(subclass)]
|
#[pyclass(subclass)]
|
||||||
#[pyo3(name = "Tag")]
|
#[pyo3(name = "Tag")]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct PyTag {
|
struct PyTag {
|
||||||
tag: Tag
|
tag: Tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyTag {
|
impl PyTag {
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn byte(code: u16, byte: Vec<u8>) -> Self {
|
fn byte(code: u16, byte: Vec<u8>) -> Self {
|
||||||
PyTag { tag: Tag::byte(code, byte) }
|
PyTag {
|
||||||
|
tag: Tag::byte(code, &byte),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn ascii(code: u16, ascii: &str) -> Self {
|
fn ascii(code: u16, ascii: &str) -> Self {
|
||||||
PyTag { tag: Tag::ascii(code, ascii) }
|
PyTag {
|
||||||
|
tag: Tag::ascii(code, ascii),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn short(code: u16, short: Vec<u16>) -> Self {
|
fn short(code: u16, short: Vec<u16>) -> Self {
|
||||||
PyTag { tag: Tag::short(code, short) }
|
PyTag {
|
||||||
|
tag: Tag::short(code, &short),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn long(code: u16, long: Vec<u32>) -> Self {
|
fn long(code: u16, long: Vec<u32>) -> Self {
|
||||||
PyTag { tag: Tag::long(code, long) }
|
PyTag {
|
||||||
|
tag: Tag::long(code, &long),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn rational(code: u16, rational: Vec<f64>) -> Self {
|
fn rational(code: u16, rational: Vec<f64>) -> Self {
|
||||||
PyTag { tag: Tag::rational(code, rational.into_iter().map(|x| Rational32::from_f64(x).unwrap()).collect()) }
|
PyTag {
|
||||||
|
tag: Tag::rational(
|
||||||
|
code,
|
||||||
|
&rational
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| Rational32::from_f64(x).unwrap())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn sbyte(code: u16, sbyte: Vec<i8>) -> Self {
|
fn sbyte(code: u16, sbyte: Vec<i8>) -> Self {
|
||||||
PyTag { tag: Tag::sbyte(code, sbyte) }
|
PyTag {
|
||||||
|
tag: Tag::sbyte(code, &sbyte),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn sshort(code: u16, sshort: Vec<i16>) -> Self {
|
fn sshort(code: u16, sshort: Vec<i16>) -> Self {
|
||||||
PyTag { tag: Tag::sshort(code, sshort) }
|
PyTag {
|
||||||
|
tag: Tag::sshort(code, &sshort),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn slong(code: u16, slong: Vec<i32>) -> Self {
|
fn slong(code: u16, slong: Vec<i32>) -> Self {
|
||||||
PyTag { tag: Tag::slong(code, slong) }
|
PyTag {
|
||||||
|
tag: Tag::slong(code, &slong),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn srational(code: u16, srational: Vec<f64>) -> Self {
|
fn srational(code: u16, srational: Vec<f64>) -> Self {
|
||||||
PyTag { tag: Tag::srational(code, srational.into_iter().map(|x| Rational32::from_f64(x).unwrap()).collect()) }
|
PyTag {
|
||||||
|
tag: Tag::srational(
|
||||||
|
code,
|
||||||
|
&srational
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| Rational32::from_f64(x).unwrap())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn float(code: u16, float: Vec<f32>) -> Self {
|
fn float(code: u16, float: Vec<f32>) -> Self {
|
||||||
PyTag { tag: Tag::float(code, float) }
|
PyTag {
|
||||||
|
tag: Tag::float(code, &float),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn double(code: u16, double: Vec<f64>) -> Self {
|
fn double(code: u16, double: Vec<f64>) -> Self {
|
||||||
PyTag { tag: Tag::double(code, double) }
|
PyTag {
|
||||||
|
tag: Tag::double(code, &double),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn ifd(code: u16, ifd: Vec<u32>) -> Self {
|
fn ifd(code: u16, ifd: Vec<u32>) -> Self {
|
||||||
PyTag { tag: Tag::ifd(code, ifd) }
|
PyTag {
|
||||||
|
tag: Tag::ifd(code, &ifd),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn unicode(code: u16, unicode: &str) -> Self {
|
fn unicode(code: u16, unicode: &str) -> Self {
|
||||||
PyTag { tag: Tag::unicode(code, unicode) }
|
PyTag {
|
||||||
|
tag: Tag::unicode(code, unicode),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn complex(code: u16, complex: Vec<(f32, f32)>) -> Self {
|
fn complex(code: u16, complex: Vec<(f32, f32)>) -> Self {
|
||||||
PyTag { tag: Tag::complex(code, complex.into_iter().map(|(x, y)| Complex { re: x, im: y }).collect()) }
|
PyTag {
|
||||||
|
tag: Tag::complex(
|
||||||
|
code,
|
||||||
|
&complex
|
||||||
|
.into_iter()
|
||||||
|
.map(|(x, y)| Complex { re: x, im: y })
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn long8(code: u16, long8: Vec<u64>) -> Self {
|
fn long8(code: u16, long8: Vec<u64>) -> Self {
|
||||||
PyTag { tag: Tag::long8(code, long8) }
|
PyTag {
|
||||||
|
tag: Tag::long8(code, &long8),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn slong8(code: u16, slong8: Vec<i64>) -> Self {
|
fn slong8(code: u16, slong8: Vec<i64>) -> Self {
|
||||||
PyTag { tag: Tag::slong8(code, slong8) }
|
PyTag {
|
||||||
|
tag: Tag::slong8(code, &slong8),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
fn ifd8(code: u16, ifd8: Vec<u64>) -> Self {
|
fn ifd8(code: u16, ifd8: Vec<u64>) -> Self {
|
||||||
PyTag { tag: Tag::ifd8(code, ifd8) }
|
PyTag {
|
||||||
|
tag: Tag::ifd8(code, &ifd8),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count(&self) -> u64 {
|
fn count(&self) -> u64 {
|
||||||
@@ -103,55 +155,157 @@ impl PyTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[pyclass(subclass)]
|
#[pyclass(subclass)]
|
||||||
#[pyo3(name = "IJTiffFile")]
|
#[pyo3(name = "IJTiffFile")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PyIJTiffFile {
|
struct PyIJTiffFile {
|
||||||
ijtifffile: Option<IJTiffFile>
|
ijtifffile: Option<IJTiffFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyIJTiffFile {
|
impl PyIJTiffFile {
|
||||||
#[new]
|
#[new]
|
||||||
fn new(path: &str, shape: (usize, usize, usize)) -> PyResult<Self> {
|
fn new(path: &str, shape: (usize, usize, usize)) -> PyResult<Self> {
|
||||||
Ok(PyIJTiffFile { ijtifffile: Some(IJTiffFile::new(path, shape)?) } )
|
Ok(PyIJTiffFile {
|
||||||
|
ijtifffile: Some(IJTiffFile::new(path, shape)?),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_colors(&mut self, colors: (u8, u8, u8)) -> Self {
|
#[getter]
|
||||||
todo!()
|
fn get_colors(&self) -> PyResult<Option<Vec<Vec<u8>>>> {
|
||||||
|
if let Some(ijtifffile) = &self.ijtifffile {
|
||||||
|
if let Colors::Colors(colors) = &ijtifffile.colors {
|
||||||
|
return Ok(Some(colors.to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_colormap(&mut self, colormap: Vec<(u8, u8, u8)>) -> Self {
|
#[setter]
|
||||||
todo!()
|
fn set_colors(&mut self, colors: PyReadonlyArray2<u8>) -> PyResult<()> {
|
||||||
|
if let Some(ijtifffile) = &mut self.ijtifffile {
|
||||||
|
let a = colors.to_owned_array();
|
||||||
|
ijtifffile.colors = Colors::Colors(
|
||||||
|
(0..a.shape()[0])
|
||||||
|
.map(|i| Vec::from(a.slice(s![i, ..]).as_slice().unwrap()))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_px_size(&mut self, pxsize: f64) -> Self {
|
#[getter]
|
||||||
todo!()
|
fn get_colormap(&mut self) -> PyResult<Option<Vec<Vec<u8>>>> {
|
||||||
|
if let Some(ijtifffile) = &self.ijtifffile {
|
||||||
|
if let Colors::Colormap(colormap) = &ijtifffile.colors {
|
||||||
|
return Ok(Some(colormap.to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_delta_z(&mut self, delta_z: f64) -> Self {
|
#[setter]
|
||||||
todo!()
|
fn set_colormap(&mut self, colormap: PyReadonlyArray2<u8>) -> PyResult<()> {
|
||||||
|
if let Some(ijtifffile) = &mut self.ijtifffile {
|
||||||
|
let a = colormap.to_owned_array();
|
||||||
|
ijtifffile.colors = Colors::Colormap(
|
||||||
|
(0..a.shape()[0])
|
||||||
|
.map(|i| Vec::from(a.slice(s![i, ..]).as_slice().unwrap()))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_time_interval(&mut self, time_interval: f64) -> Self {
|
#[getter]
|
||||||
todo!()
|
fn get_px_size(&self) -> PyResult<Option<f64>> {
|
||||||
}
|
if let Some(ijtifffile) = &self.ijtifffile {
|
||||||
|
Ok(ijtifffile.px_size)
|
||||||
fn with_comments(&mut self, comments: String) -> Self {
|
} else {
|
||||||
todo!()
|
Ok(None)
|
||||||
}
|
|
||||||
|
|
||||||
fn append_extra_tag(&mut self, tag: PyTag) {
|
|
||||||
if let Some(ijtifffile) = self.ijtifffile.as_mut() {
|
|
||||||
ijtifffile.extra_tags.push(tag.tag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_extra_tags(&mut self, tags: Vec<PyTag>) {
|
#[setter]
|
||||||
if let Some(ijtifffile) = self.ijtifffile.as_mut() {
|
fn set_px_size(&mut self, px_size: f64) -> PyResult<()> {
|
||||||
ijtifffile.extra_tags.extend(tags.into_iter().map(|x| x.tag));
|
if let Some(ijtifffile) = &mut self.ijtifffile {
|
||||||
|
ijtifffile.px_size = Some(px_size);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[getter]
|
||||||
|
fn get_delta_z(&self) -> PyResult<Option<f64>> {
|
||||||
|
if let Some(ijtifffile) = &self.ijtifffile {
|
||||||
|
Ok(ijtifffile.delta_z)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[setter]
|
||||||
|
fn set_delta_z(&mut self, delta_z: f64) -> PyResult<()> {
|
||||||
|
if let Some(ijtifffile) = &mut self.ijtifffile {
|
||||||
|
ijtifffile.delta_z = Some(delta_z);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[getter]
|
||||||
|
fn get_time_interval(&self) -> PyResult<Option<f64>> {
|
||||||
|
if let Some(ijtifffile) = &self.ijtifffile {
|
||||||
|
Ok(ijtifffile.time_interval)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[setter]
|
||||||
|
fn set_time_interval(&mut self, time_interval: f64) -> PyResult<()> {
|
||||||
|
if let Some(ijtifffile) = &mut self.ijtifffile {
|
||||||
|
ijtifffile.time_interval = Some(time_interval);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[getter]
|
||||||
|
fn get_comment(&self) -> PyResult<Option<String>> {
|
||||||
|
if let Some(ijtifffile) = &self.ijtifffile {
|
||||||
|
Ok(ijtifffile.comment.clone())
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[setter]
|
||||||
|
fn set_comment(&mut self, comment: &str) -> PyResult<()> {
|
||||||
|
if let Some(ijtifffile) = &mut self.ijtifffile {
|
||||||
|
ijtifffile.comment = Some(String::from(comment));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_extra_tag(&mut self, tag: PyTag, czt: Option<(usize, usize, usize)>) {
|
||||||
|
if let Some(ijtifffile) = self.ijtifffile.as_mut() {
|
||||||
|
if let Some(extra_tags) = ijtifffile.extra_tags.get_mut(&czt) {
|
||||||
|
extra_tags.push(tag.tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tags(&self, czt: Option<(usize, usize, usize)>) -> PyResult<Vec<PyTag>> {
|
||||||
|
if let Some(ijtifffile) = &self.ijtifffile {
|
||||||
|
if let Some(extra_tags) = ijtifffile.extra_tags.get(&czt) {
|
||||||
|
let v = extra_tags
|
||||||
|
.iter()
|
||||||
|
.map(|tag| PyTag {
|
||||||
|
tag: tag.to_owned(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
return Ok(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&mut self) -> PyResult<()> {
|
fn close(&mut self) -> PyResult<()> {
|
||||||
@@ -160,20 +314,19 @@ impl PyIJTiffFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
macro_rules! impl_save {
|
macro_rules! impl_save {
|
||||||
($T:ty, $t:ident) => {
|
($T:ty, $t:ident) => {
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyIJTiffFile {
|
impl PyIJTiffFile {
|
||||||
fn $t(&mut self, frame: PyReadonlyArray2<$T>, c: usize, t: usize, z: usize,
|
fn $t(
|
||||||
extra_tags: Option<Vec<PyTag>>) -> PyResult<()> {
|
&mut self,
|
||||||
let extra_tags = if let Some(extra_tags) = extra_tags {
|
frame: PyReadonlyArray2<$T>,
|
||||||
Some(extra_tags.into_iter().map(|x| x.tag).collect())
|
c: usize,
|
||||||
} else {
|
t: usize,
|
||||||
None
|
z: usize,
|
||||||
};
|
) -> PyResult<()> {
|
||||||
if let Some(ijtifffile) = self.ijtifffile.as_mut() {
|
if let Some(ijtifffile) = self.ijtifffile.as_mut() {
|
||||||
ijtifffile.save(frame.to_owned_array(), c, t, z, extra_tags)?;
|
ijtifffile.save(frame.to_owned_array(), c, t, z)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -192,7 +345,6 @@ impl_save!(i64, save_i64);
|
|||||||
impl_save!(f32, save_f32);
|
impl_save!(f32, save_f32);
|
||||||
impl_save!(f64, save_f64);
|
impl_save!(f64, save_f64);
|
||||||
|
|
||||||
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
#[pyo3(name = "tiffwrite_rs")]
|
#[pyo3(name = "tiffwrite_rs")]
|
||||||
fn tiffwrite_rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
fn tiffwrite_rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
|||||||
Reference in New Issue
Block a user