- can now save the common types
This commit is contained in:
105
src/lib.rs
105
src/lib.rs
@@ -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)?));
|
||||
|
||||
29
src/main.rs
29
src/main.rs
@@ -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(())
|
||||
}
|
||||
71
src/py.rs
71
src/py.rs
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user