- can now save the common types

This commit is contained in:
Wim Pomp
2024-10-09 12:05:11 +02:00
parent 82931f7715
commit 52785037b9
6 changed files with 241 additions and 659 deletions

View File

@@ -1,3 +1,6 @@
#[cfg(not(feature = "nopython"))]
mod py;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
@@ -15,6 +18,7 @@ use chrono::Utc;
const TAG_SIZE: usize = 20;
const OFFSET_SIZE: usize = 8;
const OFFSET: u64 = 16;
const COMPRESSION: u16 = 1;
#[derive(Clone, Debug)]
@@ -238,7 +242,7 @@ struct Frame {
image_width: u32,
image_length: u32,
bits_per_sample: u16,
compression: u16,
sample_format: u16,
tile_width: u16,
tile_length: u16,
extra_tags: Vec<Tag>
@@ -246,17 +250,63 @@ struct Frame {
impl Frame {
fn new(
tilebyteoffsets: Vec<u64>, tilebytecounts: Vec<u64>, image_width: u32,
image_length: u32, bits_per_sample: u16, compression: u16, tile_width: u16, tile_length: u16
tilebyteoffsets: Vec<u64>, tilebytecounts: Vec<u64>, image_width: u32, image_length: u32,
bits_per_sample: u16, sample_format: u16, tile_width: u16, tile_length: u16
) -> Self {
Frame {
tilebyteoffsets, tilebytecounts, image_width, image_length, bits_per_sample,
compression, tile_width, tile_length, extra_tags: Vec::new()
sample_format, tile_width, tile_length, extra_tags: Vec::new()
}
}
}
pub trait Bytes {
const BITS_PER_SAMPLE: u16;
const SAMPLE_FORMAT: u16;
fn bytes(&self) -> Vec<u8>;
}
macro_rules! bytes_impl {
($T:ty, $bits_per_sample:expr, $sample_format:expr) => {
impl Bytes for $T {
const BITS_PER_SAMPLE: u16 = $bits_per_sample;
const SAMPLE_FORMAT: u16 = $sample_format;
#[inline]
fn bytes(&self) -> Vec<u8>
{
self.to_le_bytes().to_vec()
}
}
};
}
bytes_impl!(u8, 8, 1);
bytes_impl!(u16, 16, 1);
bytes_impl!(u32, 32, 1);
bytes_impl!(u64, 64, 1);
bytes_impl!(u128, 128, 1);
#[cfg(target_pointer_width = "64")]
bytes_impl!(usize, 64, 1);
#[cfg(target_pointer_width = "32")]
bytes_impl!(usize, 32, 1);
bytes_impl!(i8, 8, 2);
bytes_impl!(i16, 16, 2);
bytes_impl!(i32, 32, 2);
bytes_impl!(i64, 64, 2);
bytes_impl!(i128, 128, 2);
#[cfg(target_pointer_width = "64")]
bytes_impl!(isize, 64, 2);
#[cfg(target_pointer_width = "32")]
bytes_impl!(isize, 32, 2);
bytes_impl!(f32, 32, 3);
bytes_impl!(f64, 64, 3);
#[derive(Debug)]
pub struct IJTiffFile {
file: File,
@@ -331,19 +381,6 @@ impl IJTiffFile {
desc
}
pub fn save(&mut self, frame: Array2<u16>, c: usize, z: usize, t: usize,
extra_tags: Option<Vec<Tag>>) -> Result<()> {
let mut compressed_frame = self.compress_frame(frame)?;
if let Some(tags) = extra_tags {
for tag in tags {
compressed_frame.extra_tags.push(tag);
}
}
self.frames.insert(self.get_frame_number(c, z, t), compressed_frame);
Ok(())
}
fn get_frame_number(&self, c: usize, z: usize, t: usize) -> (usize, u8) {
if let (None, None) = (self.colormap.as_ref(), self.colors.as_ref()) {
(z + t * self.shape.1, c as u8)
@@ -352,7 +389,7 @@ impl IJTiffFile {
}
}
pub fn hash<T: Hash>(value: &T) -> u64 {
fn hash<T: Hash>(value: &T) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
@@ -383,21 +420,32 @@ impl IJTiffFile {
}
}
fn compress_frame(&mut self, frame: Array2<u16>) -> Result<Frame> {
pub fn save<T: Bytes + Clone + Zero>(&mut self, frame: Array2<T>, c: usize, z: usize, t: usize,
extra_tags: Option<Vec<Tag>>) -> Result<()> {
self.compress_frame(frame, c, z, t, extra_tags);
Ok(())
}
fn compress_frame<T: Bytes + Clone + Zero>(&mut self, frame: Array2<T>, c: usize, z: usize, t: usize,
extra_tags: Option<Vec<Tag>>) {
let image_width = frame.shape()[0] as u32;
let image_length = frame.shape()[1] as u32;
let mut tilebyteoffsets = Vec::new();
let mut tilebytecounts = Vec::new();
let tiles = IJTiffFile::tile(frame.reversed_axes(), 64);
for tile in tiles {
let bytes: Vec<u8> = tile.into_flat().into_iter().map(
|x| x.to_le_bytes()).into_iter().flatten().collect();
let bytes: Vec<u8> = tile.map(|x| x.bytes()).into_iter().flatten().collect();
tilebytecounts.push(bytes.len() as u64);
tilebyteoffsets.push(self.write(&bytes)?);
tilebyteoffsets.push(self.write(&bytes).unwrap());
}
Ok(Frame::new(tilebyteoffsets, tilebytecounts, image_width, image_length,
16, 1, 64, 64))
let mut frame = Frame::new(tilebyteoffsets, tilebytecounts, image_width, image_length,
T::BITS_PER_SAMPLE, T::SAMPLE_FORMAT, 64, 64);
if let Some(tags) = extra_tags {
for tag in tags {
frame.extra_tags.push(tag);
}
}
self.frames.insert(self.get_frame_number(c, z, t), frame);
}
fn tile<T: Clone + Zero>(frame: Array2<T>, size: usize) -> Vec<Array2<T>> {
@@ -438,15 +486,13 @@ impl IJTiffFile {
fn get_colormap(&self, colormap: &Vec<u16>) -> Result<Vec<u16>> {
todo!();
Ok(Vec::new())
}
fn get_color(&self, colors: (u8, u8, u8)) -> Result<Vec<u16>> {
todo!();
Ok(Vec::new())
}
pub fn close(&mut self) -> Result<()> {
fn close(&mut self) -> Result<()> {
let mut where_to_write_next_ifd_offset = OFFSET - OFFSET_SIZE as u64;
let mut warn = false;
for frame_number in 0..self.n_frames {
@@ -467,7 +513,7 @@ impl IJTiffFile {
ifd.push_tag(Tag::long(256, vec![frame.image_width]));
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(259, vec![1]));
ifd.push_tag(Tag::short(259, vec![COMPRESSION]));
ifd.push_tag(Tag::ascii(270, &self.description()));
ifd.push_tag(Tag::short(277, vec![frame_count as u16]));
ifd.push_tag(Tag::ascii(305, "tiffwrite_rs"));
@@ -475,6 +521,7 @@ impl IJTiffFile {
ifd.push_tag(Tag::short(323, vec![frame.tile_length]));
ifd.push_tag(Tag::long8(324, tilebyteoffsets));
ifd.push_tag(Tag::long8(325, tilebytecounts));
ifd.push_tag(Tag::short(339, vec![frame.sample_format]));
if frame_number == 0 {
if let Some(colormap) = &self.colormap {
ifd.push_tag(Tag::short(320, self.get_colormap(colormap)?));

View File

@@ -1,28 +1,23 @@
#[cfg(not(feature = "nopython"))]
mod py;
mod lib;
use anyhow::Result;
use ndarray::{s, Array2};
use rayon::prelude::*;
use crate::lib::IJTiffFile;
use tiffwrite::IJTiffFile;
fn main() -> Result<()> {
println!("Hello World!");
let mut f = IJTiffFile::new("foo.tif", (1, 2, 1))?;
let mut f = IJTiffFile::new("foo.tif", (2, 1, 1))?;
let mut arr = Array2::<u16>::zeros((100, 100));
// for i in 0..arr.shape()[0] {
// for j in 0..arr.shape()[1] {
// arr[[i, j]] = i as u16;
// }
// }
for i in 0..arr.shape()[0] {
for j in 0..arr.shape()[1] {
arr[[i, j]] = i as u16;
}
}
f.save(arr.to_owned(), 0, 0, 0, None)?;
// let mut arr = Array2::<u16>::zeros((100, 100));
// arr.slice_mut(s![64.., ..64]).fill(1);
// arr.slice_mut(s![..64, 64..]).fill(2);
// arr.slice_mut(s![64.., 64..]).fill(3);
f.save(arr.to_owned(), 0, 1,0, None)?;
let mut arr = Array2::<u16>::zeros((100, 100));
arr.slice_mut(s![64.., ..64]).fill(1);
arr.slice_mut(s![..64, 64..]).fill(2);
arr.slice_mut(s![64.., 64..]).fill(3);
f.save(arr.to_owned(), 1, 0,0, None)?;
Ok(())
}

View File

@@ -1,9 +1,8 @@
use pyo3::prelude::*;
use crate::lib::{IJTiffFile, Tag};
use std::time::Duration;
use pyo3::types::{PyInt, PyString};
use crate::{IJTiffFile, Tag};
use fraction::Fraction;
use num::Complex;
use numpy::{PyReadonlyArray2, PyArrayMethods};
#[pyclass(subclass)]
@@ -106,14 +105,14 @@ impl PyTag {
#[pyo3(name = "IJTiffFile")]
#[derive(Debug)]
struct PyIJTiffFile {
ijtifffile: IJTiffFile
ijtifffile: Option<IJTiffFile>
}
#[pymethods]
impl PyIJTiffFile {
#[new]
fn new(path: &str, shape: (usize, usize, usize), dtype: &str) -> PyResult<Self> {
Ok(PyIJTiffFile { ijtifffile: IJTiffFile::new(path, shape)? } )
fn new(path: &str, shape: (usize, usize, usize)) -> PyResult<Self> {
Ok(PyIJTiffFile { ijtifffile: Some(IJTiffFile::new(path, shape)?) } )
}
fn with_colors(&mut self, colors: (u8, u8, u8)) -> Self {
@@ -140,19 +139,67 @@ impl PyIJTiffFile {
todo!()
}
fn append_extra_tag(&mut self, extra_tag: PyTag) {
if let Some(extra_tags) = self.ijtifffile.extra_tags.as_mut() {
extra_tags.push(extra_tag.tag);
} else {
self.ijtifffile.extra_tags = Some(vec![extra_tag.tag]);
fn append_extra_tag(&mut self, tag: PyTag) {
if let Some(ijtifffile) = self.ijtifffile.as_mut() {
if let Some(extra_tags) = ijtifffile.extra_tags.as_mut() {
extra_tags.push(tag.tag);
} else {
ijtifffile.extra_tags = Some(vec![tag.tag]);
}
}
}
fn extend_extra_tags(&mut self, tags: Vec<PyTag>) {
if let Some(ijtifffile) = self.ijtifffile.as_mut() {
if let Some(extra_tags) = ijtifffile.extra_tags.as_mut() {
extra_tags.extend(tags.into_iter().map(|x| x.tag));
} else {
ijtifffile.extra_tags = Some(tags.into_iter().map(|x| x.tag).collect());
}
}
}
fn close(&mut self) -> PyResult<()> {
self.ijtifffile.take();
Ok(())
}
}
macro_rules! impl_save {
($T:ty, $t:ident) => {
#[pymethods]
impl PyIJTiffFile {
fn $t(&mut self, frame: PyReadonlyArray2<$T>, c: usize, t: usize, z: usize,
extra_tags: Option<Vec<PyTag>>) -> PyResult<()> {
let extra_tags = if let Some(extra_tags) = extra_tags {
Some(extra_tags.into_iter().map(|x| x.tag).collect())
} else {
None
};
if let Some(ijtifffile) = self.ijtifffile.as_mut() {
ijtifffile.save(frame.to_owned_array(), c, t, z, extra_tags)?;
}
Ok(())
}
}
};
}
impl_save!(u8, save_u8);
impl_save!(u16, save_u16);
impl_save!(u32, save_u32);
impl_save!(u64, save_u64);
impl_save!(i8, save_i8);
impl_save!(i16, save_i16);
impl_save!(i32, save_i32);
impl_save!(i64, save_i64);
impl_save!(f32, save_f32);
impl_save!(f64, save_f64);
#[pymodule]
fn tiffwrite(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyTag>()?;
m.add_class::<PyIJTiffFile>()?;
Ok(())
}
}