# 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 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: ```rust use libczirw_sys::{CziReader, Dimension, InputStream, ReaderOpenInfo}; fn main() -> Result<(), Box> { // 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> { 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> { 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> { 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> { 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 = (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#" My document "#; 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> { 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.