libCZIrw-sys
Crate linking to libCZIAPI. This crate attempts to provide safe wrappers to objects
and functions in libCZIAPI. Direct often unsafe access using pointers 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>> {
let czi = CziReader::create()?;
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
czi.open(ReaderOpenInfo::new(&stream))?;
let statistics = czi.get_statistics_simple()?;
println!("number of sub-blocks: {}", statistics.get_sub_block_count());
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]);
}
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))?;
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()?
);
let locked = bitmap.lock()?;
let stride = locked.lock_info.get_stride();
let pixels = locked.lock_info.get_data_roi();
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);
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}"#)?;
let width = 100;
let height = 100;
let pixels: Vec<u8> = (0..(width * height)).map(|i| i as u8).collect();
let coordinate = Coordinate::new(1 << 0, [0, 0, 0, 0, 0, 0, 0, 0, 0]);
let sub_block = AddSubBlockInfo::new(
coordinate,
0, 0, 0, 0, width, height, width, height, PixelType::Gray8,
0, &pixels, b"", b"", );
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))?;
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.