From 52785037b9403889ad3cb1b219d30053d31c2493 Mon Sep 17 00:00:00 2001 From: Wim Pomp Date: Wed, 9 Oct 2024 12:05:11 +0200 Subject: [PATCH] - can now save the common types --- Cargo.lock | 596 ------------------------------------------ Cargo.toml | 10 +- src/lib.rs | 105 ++++++-- src/main.rs | 29 +- src/py.rs | 71 ++++- tiffwrite/__init__.py | 89 ++++++- 6 files changed, 241 insertions(+), 659 deletions(-) delete mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index efdd80a..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,596 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cc" -version = "1.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "fraction" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7" -dependencies = [ - "lazy_static", - "num", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - -[[package]] -name = "js-sys" -version = "0.3.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.159" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "matrixmultiply" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" -dependencies = [ - "autocfg", - "rawpointer", -] - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "ndarray" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841" -dependencies = [ - "matrixmultiply", - "num-complex", - "num-integer", - "num-traits", - "portable-atomic", - "portable-atomic-util", - "rawpointer", -] - -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "portable-atomic" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" - -[[package]] -name = "portable-atomic-util" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdd8420072e66d54a407b3316991fe946ce3ab1083a7f575b2463866624704d" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pyo3" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15ee168e30649f7f234c3d49ef5a7a6cbf5134289bc46c29ff3155fa3221c225" -dependencies = [ - "anyhow", - "cfg-if", - "indoc", - "libc", - "memoffset", - "once_cell", - "portable-atomic", - "pyo3-build-config", - "pyo3-ffi", - "pyo3-macros", - "unindent", -] - -[[package]] -name = "pyo3-build-config" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e61cef80755fe9e46bb8a0b8f20752ca7676dcc07a5277d8b7768c6172e529b3" -dependencies = [ - "once_cell", - "python3-dll-a", - "target-lexicon", -] - -[[package]] -name = "pyo3-ffi" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ce096073ec5405f5ee2b8b31f03a68e02aa10d5d4f565eca04acc41931fa1c" -dependencies = [ - "libc", - "pyo3-build-config", -] - -[[package]] -name = "pyo3-macros" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2440c6d12bc8f3ae39f1e775266fa5122fd0c8891ce7520fa6048e683ad3de28" -dependencies = [ - "proc-macro2", - "pyo3-macros-backend", - "quote", - "syn", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be962f0e06da8f8465729ea2cb71a416d2257dff56cbe40a70d3e62a93ae5d1" -dependencies = [ - "heck", - "proc-macro2", - "pyo3-build-config", - "quote", - "syn", -] - -[[package]] -name = "python3-dll-a" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0b78171a90d808b319acfad166c4790d9e9759bbc14ac8273fe133673dd41b" -dependencies = [ - "cc", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "syn" -version = "2.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - -[[package]] -name = "tiffwrite" -version = "2024.10.0" -dependencies = [ - "anyhow", - "chrono", - "fraction", - "ndarray", - "num", - "pyo3", - "rayon", -] - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unindent" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" - -[[package]] -name = "wasm-bindgen" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index 8680bd6..71603b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,23 @@ [package] name = "tiffwrite" -version = "2024.10.0" +version = "2024.10.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] name = "tiffwrite" -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] [dependencies] -pyo3 = { version = "0.22.3", features = ["extension-module", "abi3-py310", "generate-import-lib", "anyhow"] } +pyo3 = { version = "0.21.2", features = ["extension-module", "abi3-py310", "generate-import-lib", "anyhow", "multiple-pymethods"] } anyhow = "1.0.89" rayon = "1.10.0" fraction = "0.15.3" num = "0.4.3" -ndarray = "0.16.1" +ndarray = "0.15.6" chrono = "0.4.38" +numpy = "0.21.0" +futures = "0.3.31" [features] nopython = [] diff --git a/src/lib.rs b/src/lib.rs index 13f946f..0953369 100644 --- a/src/lib.rs +++ b/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 @@ -246,17 +250,63 @@ struct Frame { impl Frame { fn new( - tilebyteoffsets: Vec, tilebytecounts: Vec, image_width: u32, - image_length: u32, bits_per_sample: u16, compression: u16, tile_width: u16, tile_length: u16 + tilebyteoffsets: Vec, tilebytecounts: Vec, 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; +} + + +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 + { + 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, c: usize, z: usize, t: usize, - extra_tags: Option>) -> 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(value: &T) -> u64 { + fn 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) -> Result { + pub fn save(&mut self, frame: Array2, c: usize, z: usize, t: usize, + extra_tags: Option>) -> Result<()> { + self.compress_frame(frame, c, z, t, extra_tags); + Ok(()) + } + + fn compress_frame(&mut self, frame: Array2, c: usize, z: usize, t: usize, + extra_tags: Option>) { 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 = tile.into_flat().into_iter().map( - |x| x.to_le_bytes()).into_iter().flatten().collect(); + let bytes: Vec = 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(frame: Array2, size: usize) -> Vec> { @@ -438,15 +486,13 @@ impl IJTiffFile { fn get_colormap(&self, colormap: &Vec) -> Result> { todo!(); - Ok(Vec::new()) } fn get_color(&self, colors: (u8, u8, u8)) -> Result> { 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)?)); diff --git a/src/main.rs b/src/main.rs index f3fd0a5..658eac3 100644 --- a/src/main.rs +++ b/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::::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::::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::::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(()) } \ No newline at end of file diff --git a/src/py.rs b/src/py.rs index 4ba2d78..e8f92ef 100644 --- a/src/py.rs +++ b/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 } #[pymethods] impl PyIJTiffFile { #[new] - fn new(path: &str, shape: (usize, usize, usize), dtype: &str) -> PyResult { - Ok(PyIJTiffFile { ijtifffile: IJTiffFile::new(path, shape)? } ) + fn new(path: &str, shape: (usize, usize, usize)) -> PyResult { + 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) { + 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>) -> 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::()?; m.add_class::()?; Ok(()) -} \ No newline at end of file +} diff --git a/tiffwrite/__init__.py b/tiffwrite/__init__.py index 7f1ac1f..ebf2282 100644 --- a/tiffwrite/__init__.py +++ b/tiffwrite/__init__.py @@ -1,5 +1,92 @@ +from __future__ import annotations + +import numpy as np +from typing import Any, Self, Sequence +from pathlib import Path +from numpy.typing import ArrayLike, DTypeLike + from . import tiffwrite as rs -class IJTiffFile(rs.IJTiffFile): +__all__ = ['Tag', 'IJTiffFile', 'tiffwrite'] + + +class IFD(dict): pass + + +class Tag(rs.Tag): + pass + + +class IJTiffFile(rs.IJTiffFile): + def __new__(cls, path: str | Path, shape: tuple[int, int, int], dtype: DTypeLike = 'uint16', + colors: Sequence[str] = None, colormap: str = None, pxsize: float = None, + deltaz: float = None, timeinterval: float = None, comment: str = None, + **extratags: Tag.Value | Tag) -> None: + new = super().__new__(cls, str(path), shape) + if colors is not None: + new = new.with_colors(colors) + if colormap is not None: + new = new.with_colormap(colormap) + if pxsize is not None: + new = new.with_pxsize(pxsize) + if deltaz is not None: + new = new.with_deltaz(deltaz) + if timeinterval is not None: + new = new.with_timeinterval(timeinterval) + if comment is not None: + new = new.with_comment(comment) + if extratags: + new = new.extend_extratags(extratags) + return new + + def __init__(self, path: str | Path, shape: tuple[int, int, int], dtype: DTypeLike = 'uint16', + colors: Sequence[str] = None, colormap: str = None, pxsize: float = None, + deltaz: float = None, timeinterval: float = None, comment: str = None, + **extratags: Tag.Value | Tag) -> None: + self.path = Path(path) + self.dtype = np.dtype(dtype) + + def __enter__(self) -> Self: + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def save(self, frame: ArrayLike, c: int, z: int, t: int) -> None: + frame = np.asarray(frame).astype(self.dtype) + match self.dtype: + case np.uint8: + self.save_u8(frame, c, z, t) + case np.uint16: + self.save_u16(frame, c, z, t) + case np.uint32: + self.save_u32(frame, c, z, t) + case np.uint64: + self.save_u64(frame, c, z, t) + case np.int8: + self.save_i8(frame, c, z, t) + case np.int16: + self.save_i16(frame, c, z, t) + case np.int32: + self.save_i32(frame, c, z, t) + case np.int64: + self.save_i64(frame, c, z, t) + case np.float32: + self.save_f32(frame, c, z, t) + case np.float64: + self.save_f64(frame, c, z, t) + case _: + raise TypeError(f'Cannot save type {self.dtype}') + + +def tiffwrite(file: str | Path, data: ArrayLike, axes: str = 'TZCXY', dtype: DTypeLike = None, bar: bool = False, + *args: Any, **kwargs: Any) -> None: + """ file: string; filename of the new tiff file + data: 2 to 5D numpy array + axes: string; order of dimensions in data, default: TZCXY for 5D, ZCXY for 4D, CXY for 3D, XY for 2D data + dtype: string; datatype to use when saving to tiff + bar: bool; whether to show a progress bar + other args: see IJTiffFile + """