- update README
- fix tests - crt-static in build.rs
This commit is contained in:
@@ -1,9 +1,219 @@
|
|||||||
# libCZIrw-sys
|
# libCZIrw-sys
|
||||||
|
|
||||||
Crate linking to [libCZIAPI](https://github.com/ZEISS/libczi).
|
Crate linking to [libCZIAPI](https://github.com/ZEISS/libczi). This crate attempts to provide safe wrappers to objects
|
||||||
This crate attempts to provide save wrappers to objects and functions in libCZIAPI.
|
and functions in libCZIAPI. Direct often unsafe access using pointer is available through the `sys` module.
|
||||||
Direct often unsafe access using pointer is available through the sys module.
|
|
||||||
|
|
||||||
By default, libCZIAPI will be statically linked. The feature 'dynamic' will switch it to dynamic linking.
|
By default, libCZIAPI will be statically linked. The feature `dynamic` will switch it to dynamic linking.
|
||||||
|
|
||||||
This code is licensed with an MIT or APACHE 2 license, but Zeiss' libCZI which is included as a submodule has a LGPL license.
|
This code is licensed with an MIT or APACHE 2 license, but Zeiss' libCZI which is included as a submodule has a LGPL
|
||||||
|
license.
|
||||||
|
|
||||||
|
### Reading a CZI file
|
||||||
|
|
||||||
|
The typical pattern is: create a reader, open it with an input stream, and query statistics or sub-blocks:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libczirw_sys::{CziReader, Dimension, InputStream, ReaderOpenInfo};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// create the reader and a stream for the file
|
||||||
|
let czi = CziReader::create()?;
|
||||||
|
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
|
||||||
|
czi.open(ReaderOpenInfo::new(&stream))?;
|
||||||
|
|
||||||
|
// get the (simple) statistics about the sub-blocks
|
||||||
|
let statistics = czi.get_statistics_simple()?;
|
||||||
|
println!("number of sub-blocks: {}", statistics.get_sub_block_count());
|
||||||
|
|
||||||
|
// iterate over the dimensions that are present (Z, C, T, ...)
|
||||||
|
let dim_bounds = statistics.get_dim_bounds();
|
||||||
|
let dimensions = Dimension::vec_from_bitflags(dim_bounds.get_dimensions_valid());
|
||||||
|
for (i, dim) in dimensions.iter().enumerate() {
|
||||||
|
println!("{:?}: {}", dim, dim_bounds.get_size()[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the bounding box of all sub-blocks
|
||||||
|
let bbox = statistics.get_bounding_box();
|
||||||
|
println!(
|
||||||
|
"overall extent: {} x {} (offset {}, {})",
|
||||||
|
bbox.get_w(),
|
||||||
|
bbox.get_h(),
|
||||||
|
bbox.get_x(),
|
||||||
|
bbox.get_y()
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reading pixel data
|
||||||
|
|
||||||
|
Each sub-block can be decoded into a bitmap. Locking the bitmap gives access to the raw pixels:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libczirw_sys::{CziReader, InputStream, ReaderOpenInfo};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let czi = CziReader::create()?;
|
||||||
|
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
|
||||||
|
czi.open(ReaderOpenInfo::new(&stream))?;
|
||||||
|
|
||||||
|
// read the first sub-block and turn it into a bitmap
|
||||||
|
let sub_block = czi.read_sub_block(0)?;
|
||||||
|
let bitmap = sub_block.create_bitmap()?;
|
||||||
|
let info = bitmap.get_info()?;
|
||||||
|
println!(
|
||||||
|
"width: {}, height: {}, pixel type: {:?}",
|
||||||
|
info.get_width(),
|
||||||
|
info.get_height(),
|
||||||
|
info.get_pixel_type()?
|
||||||
|
);
|
||||||
|
|
||||||
|
// locking gives access to the raw pixel data
|
||||||
|
let locked = bitmap.lock()?;
|
||||||
|
let stride = locked.lock_info.get_stride();
|
||||||
|
let pixels = locked.lock_info.get_data_roi(); // copies the pixel bytes
|
||||||
|
|
||||||
|
// the bitmap is automatically unlocked when `locked` goes out of scope
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reading the metadata
|
||||||
|
|
||||||
|
CZI files store metadata as an XML document, which can be retrieved as a string:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libczirw_sys::{CziReader, InputStream, ReaderOpenInfo};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let czi = CziReader::create()?;
|
||||||
|
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
|
||||||
|
czi.open(ReaderOpenInfo::new(&stream))?;
|
||||||
|
|
||||||
|
let metadata_segment = czi.get_metadata_segment()?;
|
||||||
|
let xml = metadata_segment.get_metadata_as_xml()?;
|
||||||
|
let xml: String = (&xml).try_into()?;
|
||||||
|
println!("{}", xml);
|
||||||
|
|
||||||
|
// document-level information (title, user, creation date, ...) as JSON
|
||||||
|
let doc_info = metadata_segment.get_czi_document_info()?;
|
||||||
|
let general_info = doc_info.get_general_document_info()?;
|
||||||
|
println!("{}", general_info);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reading attachments
|
||||||
|
|
||||||
|
Attachments hold additional data (e.g. microscope setup or experiment info) and are decoded based on their content type:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libczirw_sys::{AttachmentData, CziReader, InputStream, ReaderOpenInfo};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let czi = CziReader::create()?;
|
||||||
|
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
|
||||||
|
czi.open(ReaderOpenInfo::new(&stream))?;
|
||||||
|
|
||||||
|
for index in 0..czi.get_attachment_count()? {
|
||||||
|
let info = czi.get_attachment_info_from_directory(index)?;
|
||||||
|
println!(
|
||||||
|
"attachment '{}' of type {}",
|
||||||
|
info.get_name()?,
|
||||||
|
info.get_content_file_type()?
|
||||||
|
);
|
||||||
|
|
||||||
|
let attachment = czi.read_attachment(index)?;
|
||||||
|
match attachment.get_data()? {
|
||||||
|
AttachmentData::Float(values) => println!(" floats: {:?}", values),
|
||||||
|
AttachmentData::Xml(xml) => println!(" xml: {}", &xml[..xml.len().min(100)]),
|
||||||
|
AttachmentData::Unknown(bytes) => println!(" raw bytes: {}", bytes.len()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Writing a CZI file
|
||||||
|
|
||||||
|
Create a writer, add sub-blocks and metadata, and close the file to finalize it:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libczirw_sys::{
|
||||||
|
AddSubBlockInfo, Coordinate, CziWriter, OutputStream, PixelType, WriteMetadataInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let writer = CziWriter::create(r#"{"allow_duplicate_subblocks": true}"#)?;
|
||||||
|
let stream = OutputStream::create_for_file_utf8("output.czi", true)?;
|
||||||
|
writer.init(&stream, r#"{"minimum_m_index": 0, "maximum_m_index": 0}"#)?;
|
||||||
|
|
||||||
|
// a single gray-8-bit sub-block of 100 x 100 pixels
|
||||||
|
let width = 100;
|
||||||
|
let height = 100;
|
||||||
|
let pixels: Vec<u8> = (0..(width * height)).map(|i| i as u8).collect();
|
||||||
|
|
||||||
|
// bit 0 => dimension Z is valid (Z is the first dimension, value 1);
|
||||||
|
// the Z coordinate of this sub-block is 0
|
||||||
|
let coordinate = Coordinate::new(1 << 0, [0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
|
||||||
|
let sub_block = AddSubBlockInfo::new(
|
||||||
|
coordinate,
|
||||||
|
0, // m_index_valid
|
||||||
|
0, // m_index
|
||||||
|
0, // x
|
||||||
|
0, // y
|
||||||
|
width, // logical width
|
||||||
|
height, // logical height
|
||||||
|
width, // physical width
|
||||||
|
height, // physical height
|
||||||
|
PixelType::Gray8,
|
||||||
|
0, // compression mode (none)
|
||||||
|
&pixels, // raw pixel data
|
||||||
|
b"", // sub-block metadata
|
||||||
|
b"", // attachment data
|
||||||
|
);
|
||||||
|
writer.add_sub_block(sub_block)?;
|
||||||
|
|
||||||
|
let xml_metadata = br#"<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ImageDocument>
|
||||||
|
<Metadata>
|
||||||
|
<Information>
|
||||||
|
<Title>My document</Title>
|
||||||
|
</Information>
|
||||||
|
</Metadata>
|
||||||
|
</ImageDocument>"#;
|
||||||
|
writer.write_metadata(WriteMetadataInfo::new(xml_metadata))?;
|
||||||
|
|
||||||
|
// finalize the file - required to produce a valid CZI
|
||||||
|
writer.close()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Version and build information
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use libczirw_sys::{LibCZIBuildInformation, LibCZIVersionInfo};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let version = LibCZIVersionInfo::get_lib_czi_version_info()?;
|
||||||
|
println!(
|
||||||
|
"libCZI version {}.{}.{}",
|
||||||
|
version.get_major(),
|
||||||
|
version.get_minor(),
|
||||||
|
version.get_patch()
|
||||||
|
);
|
||||||
|
|
||||||
|
let build_info = LibCZIBuildInformation::get()?;
|
||||||
|
println!("compiler: {}", build_info.get_compiler_information());
|
||||||
|
println!("repository: {}", build_info.get_repository_url());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error handling
|
||||||
|
|
||||||
|
All fallible operations return a `Result` with the crate's `Error` type, which maps libCZIAPI error codes onto
|
||||||
|
descriptive variants (see `src/error.rs`). The `?` operator can be used directly; conversions from UTF-8 and
|
||||||
|
null-termination errors are provided via `From` implementations on the error type.
|
||||||
|
|||||||
@@ -236,13 +236,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let _importexport_guard = patch_importexport(&libcziapi_inc)?;
|
let _importexport_guard = patch_importexport(&libcziapi_inc)?;
|
||||||
let dst = cmake_config.build();
|
let dst = cmake_config.build();
|
||||||
|
|
||||||
#[cfg(not(feature = "dynamic"))]
|
let bindings = bindgen::Builder::default()
|
||||||
let bindings = bindgen::Builder::default();
|
|
||||||
|
|
||||||
#[cfg(feature = "dynamic")]
|
|
||||||
let bindings = bindgen::Builder::default();
|
|
||||||
|
|
||||||
let bindings = bindings
|
|
||||||
.merge_extern_blocks(true)
|
.merge_extern_blocks(true)
|
||||||
.clang_args([
|
.clang_args([
|
||||||
"-fms-extensions",
|
"-fms-extensions",
|
||||||
@@ -268,11 +262,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.ok_or(Error::msg("cannot into string"))?,
|
.ok_or(Error::msg("cannot into string"))?,
|
||||||
)
|
)
|
||||||
.generate()?;
|
.generate()?;
|
||||||
|
|
||||||
bindings.write_to_file(dst.join("lib_czi_api.rs"))?;
|
bindings.write_to_file(dst.join("lib_czi_api.rs"))?;
|
||||||
|
|
||||||
#[cfg(not(feature = "dynamic"))]
|
if cfg!(any(not(feature = "dynamic"), target_feature = "crt-static")) {
|
||||||
{
|
|
||||||
let libcziapi_dir = dst.join("build/Src/libCZIAPI");
|
let libcziapi_dir = dst.join("build/Src/libCZIAPI");
|
||||||
let libcziapi_dir2 = dst.join("Src/libCZIAPI");
|
let libcziapi_dir2 = dst.join("Src/libCZIAPI");
|
||||||
let libczi_dir = dst.join("build/Src/libCZI");
|
let libczi_dir = dst.join("build/Src/libCZI");
|
||||||
@@ -367,10 +359,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
println!("cargo::rustc-link-lib=shell32");
|
println!("cargo::rustc-link-lib=shell32");
|
||||||
println!("cargo::rustc-link-lib=Windowscodecs");
|
println!("cargo::rustc-link-lib=Windowscodecs");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
#[cfg(feature = "dynamic")]
|
|
||||||
{
|
|
||||||
println!(
|
println!(
|
||||||
"cargo::rustc-link-search=native={}",
|
"cargo::rustc-link-search=native={}",
|
||||||
dst.join("build/Src/libCZIAPI").display()
|
dst.join("build/Src/libCZIAPI").display()
|
||||||
|
|||||||
+4
-11
@@ -17,14 +17,11 @@ mod tests {
|
|||||||
use crate::handle::{CziReader, InputStream};
|
use crate::handle::{CziReader, InputStream};
|
||||||
use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo};
|
use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo};
|
||||||
use crate::misc::Dimension;
|
use crate::misc::Dimension;
|
||||||
use std::env;
|
use std::path::{Path, PathBuf};
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_shape() -> Result<(), Box<dyn std::error::Error>> {
|
fn test_read_shape() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let path = env::home_dir()
|
let path = Path::new("test-files/Experiment-2029.czi");
|
||||||
.unwrap()
|
|
||||||
.join("code/rust/ndbioimage/tests/files/Experiment-2029.czi");
|
|
||||||
assert!(path.exists());
|
assert!(path.exists());
|
||||||
let czi = CziReader::create()?;
|
let czi = CziReader::create()?;
|
||||||
let stream = InputStream::create_from_file_utf8(path.to_string_lossy().as_ref())?;
|
let stream = InputStream::create_from_file_utf8(path.to_string_lossy().as_ref())?;
|
||||||
@@ -48,9 +45,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_bytes() -> Result<(), Box<dyn std::error::Error>> {
|
fn test_read_bytes() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let path = env::home_dir()
|
let path = Path::new("test-files/Experiment-2029.czi");
|
||||||
.unwrap()
|
|
||||||
.join("code/rust/ndbioimage/tests/files/Experiment-2029.czi");
|
|
||||||
assert!(path.exists());
|
assert!(path.exists());
|
||||||
let czi = CziReader::create()?;
|
let czi = CziReader::create()?;
|
||||||
let stream = InputStream::create_from_file_utf8(path.to_string_lossy().as_ref())?;
|
let stream = InputStream::create_from_file_utf8(path.to_string_lossy().as_ref())?;
|
||||||
@@ -72,9 +67,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_libczi_xml() -> Result<(), Box<dyn std::error::Error>> {
|
fn test_libczi_xml() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let path = env::home_dir()
|
let path = Path::new("test-files/Experiment-2029.czi");
|
||||||
.unwrap()
|
|
||||||
.join("code/rust/ndbioimage/tests/files/Experiment-2029.czi");
|
|
||||||
assert!(path.exists());
|
assert!(path.exists());
|
||||||
let czi = CziReader::create()?;
|
let czi = CziReader::create()?;
|
||||||
let stream = InputStream::create_from_file_utf8(path.to_string_lossy().as_ref())?;
|
let stream = InputStream::create_from_file_utf8(path.to_string_lossy().as_ref())?;
|
||||||
|
|||||||
Reference in New Issue
Block a user