7.2 KiB
libCZIrw-sys
Crate linking to libCZIAPI. 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:
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:
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:
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:
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:
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
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.