libCZI
Reading and Writing CZI documents made easy
|
The first thing you want to do with a CZI-file is to open it. This is done with the CZIReader-object. An instance of a CZIReader is created with the function
The CZIReader-object has the following methods
With the Open-method, the caller has to pass in an object which implements the interface "IStream". This object is used by the CZIReader in order to access the data in a CZI-file. It is just the abstraction of a random-access stream. libCZI includes a (simple) implementation for reading a file which is constructed by passing in a filename:
When the Open-method has executed, the sub-block directory has been read and parsed, and the remaing methods can be called. This code can be used to open a CZI-file:
The SubBlockStatistics
gives information about the sub-blocks in the CZI-file. The coordinates of each sub-block are examined, and the mininum and maximum are determined and are available in the struct returned by the method ISubBlockRepository::GetStatistics()
. It is usually important to examine the dimBounds member in order to determine which dimensions are used in the CZI-file.
All the sub-blocks in the CZI-file can be enumerated by the method ISubBlockRepository::EnumerateSubBlocks
:
This code will print some of the information for all sub-blocks contained in a CZI-file:
It will print out something like:
... Index 4: Z4C0T0 Rect=(0,0,512,512) Index 5: Z5C0T0 Rect=(0,0,512,512) Index 6: Z6C0T0 Rect=(0,0,512,512) Index 7: Z7C0T0 Rect=(0,0,512,512) Index 8: Z8C0T0 Rect=(0,0,512,512) Index 9: Z9C0T0 Rect=(0,0,512,512) Index 10: Z10C0T0 Rect=(0,0,512,512) Index 11: Z11C0T0 Rect=(0,0,512,512) Index 12: Z12C0T0 Rect=(0,0,512,512) Index 13: Z13C0T0 Rect=(0,0,512,512) Index 14: Z14C0T0 Rect=(0,0,512,512) ...
The index argument we got here is used to identify a sub-block and can be used with the method ISubBlockRepository::ReadSubBlock
. This code will enumerate all sub-blocks found in a CZI-file, read them and save them to a PNG-file:
Note that the function SaveAsPng
is not part of libCZI. It is also worth noting that the ISubBlock::CreateBitmap
method will transparently decode the bitmap (in case we have a compressed bitmap in the sub-block).
This piece of code will extract a small rectangular region from a huge multi-tile document:
Note that we are using the SubBlockStatistics
in order to specify a ROI with coordinates relative to the upper left corner of the bounding box. The result is depicted here:
Here is an example which leverages the SingleChannelScalingTileAccessor:
The ROI is now x= \(\frac{width}{4}\), y= \(\frac{height}{4}\), w= \(\frac{5}{8}\cdot width\), h= \(\frac{5}{8}\cdot height\) - where \(width\) and \(height\) refer to the width and height of the bounding box of the document.
The zoom is given as 0.1 - so the resulting document will have width= \(0.1\times\frac{5}{8}\cdot width\) and height=width= \(0.1\times\frac{5}{8}\cdot height\).
This operaton is depicted here:
In order to create a colorful picture from a bunch of channels (usually grayscale), we need to apply a color to it - that's refered to as "tinting". Furthermore, we want to apply a gradation curve. All the required parameters for this are refered to as "display settings". In a CZI-file we can find display settings in the metadata.
The following sample is reading the display settings from the metadata; then we get a (scaled) multi-tile composite for each of the channels (more exactly: only for those channels which are marked 'active' in the display settings). Those bitmaps are then fed into a function which will produce the multi-channel-composite (according to the display settings).
In this sample we used the same document and ROI as before. The CZI-file in this case contained 5 channels (and all being 'active'), so we had to get 5 tile-composites (using the SingleChannelScalingTileAccessor), which are then all put into the ComposeMultiChannel_Bgr24
function (alongside with the corresponding display-settings). The function will produce a new bitmap (always of pixeltype Bgr24) which then contains the multi-channel-composite image. In this process we leveraged a utility CDisplaySettingsHelper
which hides some of the book-keeping (among other things, it helps sorting out the 'active' channels and it converts the display-settings we got from the document's metadata into the form expected by the ComposeMultiChannel_Bgr24
function).
The complete operation is depicted here:
All input/output operations in libCZI are done through stream objects. Stream objects are used by the CZIReader to access the data in a CZI-file. The stream object is an abstraction of a random-access stream.
libCZI defines three different stream objects - read-only streams, write-only streams and read-write streams. The respective interfaces are: IStream, IOutputStream and IInputOutputStream. libCZI provides implementations for reading from a file and for writing to a file in the file-system.
In addition, there is an experimental implementation for reading from an http(s)-server. This implementation is based on libcurl and allows reading from a CZI-file which is located on a web-server.
For creating a stream object for reading, a class factory is provided (in the file libCZI_StreamsLib.h).