w.pomp 76d7d6a8ad - update README
- fix tests
- crt-static in build.rs
2026-08-07 11:30:02 +02:00
2026-03-16 11:15:01 +01:00
2026-08-07 11:30:02 +02:00
2026-07-15 04:03:01 +02:00
2026-08-07 11:30:02 +02:00
2026-07-14 17:15:26 +02:00
2026-05-17 20:22:17 +02:00
2026-05-17 20:22:17 +02:00
2026-08-07 11:30:02 +02:00

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.

S
Description
Rust wrapper around libCZIAPI
Readme
25 MiB
Languages
Rust 100%