220 lines
7.2 KiB
Markdown
220 lines
7.2 KiB
Markdown
# libCZIrw-sys
|
|
|
|
Crate linking to [libCZIAPI](https://github.com/ZEISS/libczi). This crate attempts to provide safe wrappers to objects
|
|
and functions in libCZIAPI. 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.
|
|
|
|
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.
|