Applications
The Imaris File Format is designed to allow fast visualization of very large images. For this purpose it stores not only the original image data but also lower resolution versions of the original data. This allows the visualization software to load only low resolution data when those are sufficient. Also for the purpose of fast visualization the Imaris File Format stores the image data in contiguous 3D chunks (hdf-terminology for 3D blocks) which allows the visualization software to load only those data that are in the field of view. The multiresolution structure and the chunk-wise storage layout are the cornerstones of this high performance file format.
The file format is based on the standard HDF5 (Hierarchical Data Format 5) developed by The National Center for Supercomputing Applications, University of Illinois at Urbana-Champaign. This allows the use of widely available HDF5 libraries and tools.
An Imaris 5.5 file consists of three main types of groups (or folders) for the image:
An additional group may contain the Imaris Scene:
In this document, the terms group, attribute and dataset refer to the concepts given in the HDF5 documentation. Whenever a path is given (/DataSet/ResolutionLevel 12) one should open the corresponding groups and subgroups.
The prior “Imaris Classic” and “Imaris3” file formats are not based on the HDF5 specifications and will not be described in this document.
Bitplane uses the HDF5 library from the HDF Group as a backend for reading and writing HDF5 files. A good starting point for reading about the HDF5 file format is the website of the HDF5 Group, http://www.hdfgroup.org/HDF5/. The HDF Group provides its library as open source and free of charge. It is recommended to make use of the library rather than creating your own HDF5 reader/writer.
The HDF Group also provides helpful tools along with the library. Noteworthy is HDFView, a java-based HDF file inspector. It can be downloaded free of charge here.
It is highly recommended to use HDFView to look at some example IMS files in order to gain an understanding of the file format.
The Imaris 5.5 file structure is composed of a root “folder” and three main groups, DataSet, DataSetInfo, and Thumbnail.
The screenshot on the right of an IMS file in HDFView shows the HDF file structure. The example image has two channels and 3 resolution levels.
The DataSet folder contains the actual image data. The DataSetInfo folder contains descriptive parameters. The Thumbnail folder contains a 2D thumbnail of the image.
Path | Attribute | Value | Description |
/ | ImarisDataSet | ImarisDataSet | Specific to format |
FormatVersion | 5.5.0 | Format version | |
/DataSetInfo | Contains DataSet information | ||
/Thumbnail | Contains the Thumbnail dataz |
An Imaris 5.5 DataSet has the following structure. A Dataset consists of one or more 3D images (for 2D images the size of one dimension is equal to 1). When a dataset has multiple channels or multiple time points it stores 3D images for each channel and each time point. Datasets are saved in the file using chunks (see HDF5 documentation) and can optionally be compressed using GNU GZIP. For good reading performance, chunks must be stored in the size that the Imaris Software requests for visualization (see chapter Imaris Datablocks below).
The HDF5 chunks mechanism implies that the effective size of the Dataset can be bigger than the size of the original 3D image. Thus, the attributes ImageSizeZ, ImageSizeY and ImageSizeX are also saved with each Dataset.
The type of the Dataset needs to resemble the type of data to be stored. Valid types are listed in the following table.
HDF5 Data type | Imaris Image Data type |
H5T_NATIVE_UCHAR | 8 bit unsigned integer (char) |
H5T_NATIVE_USHORT | 16 bit unsigned integer (short) |
H5T_NATIVE_UINT32 | 32 bit unsigned integer |
H5T_NATIVE_FLOAT | 32 bit floating point |
The creation attributes for the Dataset should be chunked, storage allocation time incremental, fill value none, compression none or GZIP. If GZIP compression is activated, any valid GZIP parameter may be used (i.e. compression 0 to 9 is acceptable, 3 is preferred).
Along with each 3D image, the corresponding histogram is saved as well (see table below). The histogram is a HDF5 1D Dataset of type 64bit unsigned integer. Its length can be read out using the HDF5 library. The first value corresponds to the count of value HistogramMin in the image. The last value is the count of HistogramMax in the image.
Path | Attribute | Description |
/DataSet/ResolutionLevel 0/TimePoint 0/Channel 0 | Informations concerning resolution 0, time point 0 and channel 0 | |
ImageSizeX = 285 | The size in X in pixel for Resolution Level 0 | |
ImageSizeY = 218 | The size in Y in pixel | |
ImageSizeZ = 64 | The size in Z in pixel | |
ImageBlockSizeX = 64 | The size of a image block in X in pixel for resolution level 0 (alpha version only) - deprecated | |
ImageBlockSizeY = 256 | The size of a image block in Y in pixel(alpha version only) - deprecated | |
ImageBlockSizeZ = 64 | The size of a image block in Z in pixel(alpha version only) - deprecated | |
HistogramMin = 0 | The minimal value of the histogram corresponding to this image | |
HistogramMax = 255 | The maximal value of the histogram | |
/DataSet/Resolution Level 0/TimePoint 0/Channel 0/Data | THE image, with size ImageSizeX*ImageSizeY*ImageSizeZ | |
/DataSet/Resolution Level 0/TimePoint 0/Channel 0/Histogram | The histogram of the image. The first bin corresponds to HistogramMin and the last bin to HistogramMax | |
/DataSet/ResolutionLevel 0/TimePoint 0/Channel 1 | Informations concerning resolution 0, time point 0 and channel 1 | |
/DataSet/ResolutionLevel 0/TimePoint 0/Channel 0/Data | Image data for resolution 0, time point 0 and channel 0 | |
/DataSet/ResolutionLevel 0/TimePoint 0/Channel 0/Histogram | Image histogram for resolution 0, time point 0 and channel 0 |
Example:
mFileId = H5Fopen(mFileName.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); hid_t vDataSetId = H5Gopen(mFileId, “DataSet”); hid_t vLevelId = H5Gopen(vDataSetId, “Resolution Level 0”); hid_t vTimePointId = H5Gopen(vLevelId, "TimePoint 0"); hid_t vChannelId = H5Gopen(vTimePointId, "Channel 0"); hid_t vDataId = H5Dopen(vChannelId, “Data”); // read the attributes ImageSizeX,Y,Z hsize_t vFileDim[3] = {ImageSizeZ, ImageSizeY, ImagesizeX}; hid_t vFileSpaceId = H5Screate_simple(3, vFileDim, NULL); char* vBuffer = new vBuffer[ImageSizeZ*ImageSizeY*ImageSizeX]; H5Dread(vDataId, H5T_NATIVE_CHAR, H5S_ALL, vFileSpaceId, H5P_DEFAULT, vBuffer);
In the above example, the whole 3D image corresponding to resolution 0, time point 0 and channel 0 is read. Using the HDF5 library it also possible to read only part of the image, for memory or other requirements
The DataSetInfo group contains all the image parameters from Imaris. The sections are stored as HDF5 groups, parameters are stored as HDF5 attributes. Hdf5 attributes can be opened using H5Aopen_name and read using H5Aread. All attributes are stores as type string, i.e. numbers have to be converted to string. The HDF5 type for attributes is an array of H5T_C_S1 (C String).
In the following, the term “LSM" is used as an abbreviation of “Laser Scanning Microscope”.
Group / Section Name | Attribute / Parameter Name | Description |
ImarisDataSet | This section holds information about the data organization in the file. (always present) | |
Creator = Imaris | ||
NumberOfImages = 1 | At the moment there is only one multi-channel-time image per file | |
Version = 5.5 | The version of Imaris which created this file | |
Imaris | Information about the thumbnail and Imaris. (always present) | |
Filename = retina.ims | The name of the original file | |
ManufactorString = Generic Imaris 3.x | Manufactor information | |
ManufactorType = LSM | Manufactor type information | |
ThumbnailMode = thumbnailMIP | The type of data representation of the thumbnail. Valid values are "thumbnailNone", "thumbnailMiddleSection", "thumbnailMIP" or "thumbnailNone". | |
Version = 5.5 | The version of Imaris which created this file | |
Image | Information about the DataSet. (always present) | |
Description = nucleus | Detailed description of the image in plain text (can be multiple lines) | |
ExtMax0 = 46.7464 | Data max. extension X (in given units: um, nm, …) | |
ExtMax1 = 35.7182 | Data max. extension Y | |
ExtMax2 = 12.6 | Data max. extension Z | |
ExtMin0 = -10.3 | Data origin X | |
ExtMin1 = -1.5 | Data origin Y | |
ExtMin2 = 3.4 | Data origin Z | |
LensPower = 63x | Deconvolution parameter | |
Name = m1193.pic | Short description of the image (some characters) | |
Noc = 2 | Number of channels | |
RecordingDate = 1991-10-01 16:45:45 | “YYYY-MM-DD HH:MM:SS” | |
Unit = um | "m“, "mm“, "um“ or "nm“ | |
X = 285 | Image Size X (in voxels) | |
Y = 218 | Image Size Y (in voxels) | |
Z = 64 | Image Size Z (in voxels) | |
Channel X | Information about channel X (there is one section per channel) (always present) | |
Color = 1 0 0 | The base color (r,g,b float values from 0.0 to 1.0) | |
ColorMode = BaseColor | “BaseColor”, “TableColor” see example below for the color table mode. | |
ColorOpacity = 0.168 | The opacity with which the volume rendering displays the channel (float value from 0.0 to 1.0) | |
ColorRange = 0 194.921 | The display “contrast” | |
Description = Cy5 | Detailed description of the channel | |
Gain = 0 | Deconvolution parameter | |
LSMEmissionWavelength = | Emission Wavelength | |
LSMExcitationWavelength = | Excitation Wavelength | |
LSMPhotons = | Deconvolution parameter | |
LSMPinhole = | Pinhole diameter | |
Max = 255 | The data maximum value of the channel image | |
MicroscopeMode = | Deconvolution parameter | |
Min = 0 | The data minimum value of the channel image | |
Name = CollagenIV (TxRed) | Short description of the channel (some chars) | |
NumericalAperture = | Numerical Aperture | |
Offset = 0 | Deconvolution parameter | |
Pinhole = 0 | Deconvolution parameter | |
RefractionIndexEmbedding = | Deconvolution parameter | |
RefractionIndexImmersion = | Deconvolution parameter | |
TimeInfo | Information about time for all channels. (always present) | |
DataSetTimePoints = 1 | The number of time points in the DataSet | |
FileTimePoints = 1 | The number of time points in the file (currently the same as DataSetTimePoints) | |
TimePoint1 = 1991-10-01 16:45:45.000 | Time for time point 1. Time must be strictly increasing between time points. | |
Log | Any information about the history of the image (especially the data manipulation). | |
Entries = 3 | Number of entries (number of image data modifications). Can be zero. | |
Entry0 = | First modification | |
Entry1 = | Second modification | |
Entry2 = | Third modification |
Note that any other group/section can be available depending on the original file format (Olympus, Zeiss etc)
Example to read LSMEmissionWavelength of Channel 3
mFileId = H5Fopen(mFileName.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); // open DataSetInfo group hid_t vDataSetInfoId = H5Gopen(mFileId, “DataSetInfo”); // open channel 3 group hid_t vChannel3Id = H5Gopen(vDataSetInfoId, “Channel 3”); // open attribute LSMEmissionWavelength hid_t vAttributeId = H5Aopen_name(vChannelId, “LSMEmissionWavelength”); // get data space hid_t vAttributeSpaceId = H5Aget_space(vAttributeId); // get attribute value size hsize_t vAttributeSize = 0; H5Sget_simple_extent_dims(vAttributeSpaceId, &vAttributeSize, NULL); // create buffer char* vBuffer = new char[(bpSize)vAttributeSize+1]; vBuffer[vAttributeSize] = '\0'; // read attribute value H5Aread(vAttributeId, H5T_C_S1, vBuffer);
The thumbnail directory contains a 2D, square RGBA image, of arbitrary size (typical values being 128x128 or 256x256). The image data for a thumbnail of size W should be read using a buffer of unsigned char with a size of W x W x 4 (with H5Dread). The HDF5 dataset size is W rows and W x 4 columns. Each image pixel is stored as 4 HDF5 values, in the order Red, Green, Blue and Alpha component.
Example
// open the group where the thumbnail stuff is H5Gopen(mFileId, “Thumbnail”); // get the dataset hid_t vThumbnailDataId = H5Dopen(vThumbnailId, "Data"); // get the dataspace hid_t vThumbnailSpaceId = H5Dget_space(vThumbnailDataId); // get the size of data hsize_t vThumbnailDims[2]; H5Sget_simple_extent_dims(vThumbnailSpaceId, vThumbnailDims, NULL); char* vBuffer = new char[(bpSize)(vThumbnailDims[0] * vThumbnailDims[1])]; // read the thumbnail data H5Dread(vThumbnailDataId, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL, H5P_DEFAULT, vBuffer); // create image vThumbnail = new image(vThumbnailDim[1]/4, vThumbnailDim[0]); for all thumbnail pixel { vThumbnail[vPixel].Red = vBuffer[vIndex]; vThumbnail[vPixel].Green = vBuffer[vIndex+1]; vThumbnail[vPixel].Blue = vBuffer[vIndex+2]; vThumbnail[vPixel].Alpha = vBuffer[vIndex+3];
The multiresolution structure and the chunk-wise storage layout are the cornerstones of this high performance file format.
For efficient data access for visualization the Imaris 5.5 file format stores image data in 3D chunks. Typical chunk sizes are 128x128x64 or 256x256x16. The optimal chunk size is determined by the geometry of the image and it is not easy to specify rules for reproducing exactly the chunk sizes that Imaris will write into the hdf-file. It is also not necessary because the hdf library is very efficient at reading data as long as the layout is not entirely different. If you want to write image files for Imaris to read, try to create 3D chunks that are of roughly 1 Megabyte size. This will produce good results for rendering in Imaris. If you need to tweak performance, please contact the Bitplane engineering team to get more detailed information.
For efficient visualization the Imaris 5.5 file format stores not only the original data but also low resolution versions of the original data. The number of resolution levels in the file is determined by the following pseudocode:
void getMultiResolutionPyramidalSizes( const size_t[3] aDataSize, std::vector& aResolutionSizes) { const float mMinVolumeSizeMB = 1.f; aResolutionSizes.clear(); size_t[3] vNewResolution = aDataSize; float vVolumeMB; do { vResolutionSizes.push_back(vNewResolution); size_t[3] vLastResolution = vNewResolution; size_t vLastVolume = vLastResolution[0] * vLastResolution[1] * vLastResolution[2]; for (int d = 0; d < N; ++d) { if ((10*vLastResolution[d]) * (10*vLastResolution[d]) > vLastVolume / vLastResolution[d]) vNewResolution[d] = vLastResolution[d] / 2; else vNewResolution[d] = vLastResolution[d]; // make sure we don't have zero-size dimension vNewResolution[d] = std::max((size_t)1, vNewResolution[d]); } vVolumeMB = vNewResolution[0] * vNewResolution[1] * vNewResolution[2]) / (1024.f * 1024.f); } while (vVolumeMB > mMinVolumeSizeMB); }