Skip to content

Zeiss AxioVision ZVI

Zeiss AxioVision 4.x saves microscope images (grey or colour, with Z stacks and channels) as .zvi files. OpenReadout returns the image with its planes, channels, exposure times and pixel size.

Derived from hex dumps of public files from many depositors (several microscope stands and cameras, 2003 to 2025) and comparison with Bio-Formats 8.5.0 run as a black box; olefile (BSD-2) is a second container reader for a file Bio-Formats cannot open. See docs/provenance/zvi.md. Format id zvi, crate openreadout-zvi. No ZEISS specification or SDK was used; every name below is ours.

Container

A ZVI file is an OLE2 compound file (Microsoft’s public [MS-CFB] specification; signature D0 CF 11 E0 A1 B1 1A E1). The shared reader openreadout_core::cfb (moved there from the Shimadzu reader) parses the header, FAT/DIFAT, mini FAT and directory tree and reads streams through their sector chains.

Detection: the .zvi extension and the compound-file signature → definite; the extension alone → extension-only.

Streams observed:

stream content our use
Image/Contents typed values (below), then stored-object names of the sub-storages not needed
Image/Tags/Contents tag list of the whole image scale, file name, image-level defaults
Image/Item(n)/Contents one plane: typed values, the raw-image header, the samples pixels
Image/Item(n)/Tags/Contents tag list of that plane indices, channel, exposure, times, optics
Tags a 16-byte identifier, then a tag list (document level) vendor.document_tags
Thumbnail a 16-byte identifier, 10 more bytes, then a Windows BMP (BM, size at +2) attachment #0 (.bmp)
Image/Layers/…, Image/Scaling/…, Image/DisplayItem, Image/RootFolder, SummaryInformation, DocumentSummaryInformation layers, shapes, property sets listed by info --view structure

Typed values and tag lists (tags.rs)

A value is a little-endian u16 type code followed by its data. The codes observed are the OLE Automation VARENUM numbers of Microsoft’s public [MS-OAUT] specification, so their data sizes follow that document: 0/1 empty; 2 i16; 3 i32; 4 f32; 5 f64; 7 date (f64 days since 1899-12-30); 8 text (u32 byte length + UTF-16LE, NUL-terminated); 11 bool (i16); 9/13 a 16-byte payload (all zeros in the corpus); 65 blob and 69 stored-object name (u32 length + bytes). Others from [MS-OAUT] (i8, u8, u16, u32, i64, u64, currency, ANSI text, 66–70) are accepted by size.

A tag list (TagList, parse_tags) is: value = version (i32 0x20001000 in every file), value = count, then count entries of (value, i32 tag id, i32 attribute). Parsing stops cleanly at the first entry that is truncated or has an unknown type code (unparsed).

Item Contents and the raw-image header

After the typed values, every plane stream holds a 28-byte header of seven little-endian u32: IMAGE_MARKER (0x10002000), width, height, depth (1), bytes per pixel, pixel format, valid bits; the samples follow immediately and end the stream (width × height × bytes per pixel bytes). find_header searches the first 64 KiB for the marker and accepts it only when the sample block fits in the stream. Observed: in every file the header starts at offset 296 and the samples at 324.

bytes per pixel, format samples status
2, 4 uint16 grey validated (9 files)
6, 8 3 × uint16 colour, stored blue, green, red (returned as red, green, blue) validated (1 file)
1, – uint8 grey inferred, no corpus file
3, – 3 × uint8 colour (BGR) inferred, no corpus file
other – exit 6 (extra.unsupported_sample_layout)

Tag ids used (identified by value matching across files and with Bio-Formats’ output)

constant id meaning (ours)
TAG_WIDTH, TAG_HEIGHT, TAG_PIXEL_FORMAT 515, 516, 518 image size and pixel format (same as the raw header)
TAG_Z_INDEX, TAG_C_INDEX, TAG_T_INDEX 2819, 2820, 2821 stored z, channel and time index of an item; values need not start at 0 or be contiguous (z 13–30, channels 2 and 4 are seen): distinct values sorted ascending become the z/c/t ordinals
POSITION_TAGS 2822, 2823, 2827 further per-item indices, 0 in every corpus file; items that differ in them become separate images (inferred)
TAG_CHANNEL_NAME, TAG_CHANNEL_COLOR, TAG_EXPOSURE_MS 1284, 1282, 2564 channel name, display colour (0x00BBGGRR), exposure in ms (50 ↔ Bio-Formats 0.05 s)
TAG_EXCITATION_NM, TAG_EMISSION_NM 0x1000110, 0x1000111 wavelengths (498/516 for eGFP)
TAG_SCALE_X/TAG_UNIT_X, TAG_SCALE_Y/TAG_UNIT_Y, TAG_SCALE_Z/TAG_UNIT_Z 769/770, 772/773, 775/776 pixel size and its unit code (Image/Tags); UNIT_MICROMETRE = 76; code 0 = uncalibrated (no physical size; Bio-Formats reports 1.0 µm)
TAG_ACQUIRED 1025 acquisition date (OLE date, local time) → acquired_at without a zone (a note says so)
TAG_RELATIVE_TIME 300 time of the item since the first, in days → frame delta_t_s; time_increment_s when T > 1
TAG_OBJECTIVE_NAME, TAG_OBJECTIVE_MAGNIFICATION, TAG_OBJECTIVE_NA 2049, 2076, 2077 objective (Plan Apochromat 100x/1.40 Oil …, 100, 1.4)
TAG_MICROSCOPE, TAG_CAMERA 2075, 1042 instrument.model (Axioskop 2), instrument.detector (AxioCamHR3)
TAG_FILE_NAME 1553 name the image was saved under → image name
TAG_CENTER_X, TAG_CENTER_Y 2073, 2074 image centre in µm (half the calibrated width/height) → extra.center_um

Every tag of the image and of each item is kept in vendor (info --view full), keyed by numeric id.

Normalized model

One image per distinct POSITION_TAGS combination (one in every corpus file). size_z/c/t = number of distinct stored indices; dimension_order = the axis that changes first between consecutive items is fastest (XYZCT for z stacks stored channel by channel, XYCZT for channel-interleaved stacks). Channels in ascending stored channel index. extra: bytes_per_pixel, pixel_format, bits_significant, stored_indices, center_um, position_indices. Frames (info --view full): c, z, t, frame (item number), delta_t_s, exposure_ms.

Differences from Bio-Formats (black-box observations, oracle/metadata_compare.py, docs/provenance/zvi.md): Bio-Formats reports 1.0 µm for uncalibrated axes (we omit them); it reports an exposure of 0 for one channel in 3 files where tag 2564 holds 100, 5894 and 1001 ms (we report the stored values); its AcquisitionDate is tag 1025 of the second item in all 11 multi-plane files with dates (none for single-plane files), where we report the first item’s (the acquisition start); it cannot open figshare32769939-zvi-a1 at all. Colour snapshots keep the exposure only in Image/Tags: a single channel falls back to the image tags for its name, exposure and wavelengths.

check finding codes

code severity meaning
compound_file error the compound-file directory or FAT has problems
planes error an item without a raw-image header, or (c, z, t) positions without exactly one item
truncated error an item’s sample block is not fully reachable (truncated file)
tags warning tag entries that could not be parsed

Vocabulary (every public identifier in openreadout-zvi/src must appear here)

identifier meaning
ZviReader, ZviDataset, FORMAT_ID, open, items, images format reader, opened file, the id zvi, open, stored planes, images
ZviItem, number, width, height, bytes_per_pixel, pixel_format, valid_bits, data_offset, stream_size, z, c, t, position, tags one Image/Item(n)
ZviImage, planes, layout, info one image: (c, z, t) → item, sample layout, normalized info
SampleLayout, sample_layout, pixel_type, samples_per_pixel, bgr how samples are stored
IMAGE_MARKER, IMAGE_HEADER_LEN the raw-image header
TAG_WIDTH, TAG_HEIGHT, TAG_PIXEL_FORMAT, TAG_Z_INDEX, TAG_C_INDEX, TAG_T_INDEX, POSITION_TAGS, TAG_CHANNEL_NAME, TAG_CHANNEL_COLOR, TAG_EXPOSURE_MS, TAG_EXCITATION_NM, TAG_EMISSION_NM, TAG_SCALE_X, TAG_UNIT_X, TAG_SCALE_Y, TAG_UNIT_Y, TAG_SCALE_Z, TAG_UNIT_Z, UNIT_MICROMETRE, TAG_ACQUIRED, TAG_RELATIVE_TIME, TAG_OBJECTIVE_NAME, TAG_OBJECTIVE_MAGNIFICATION, TAG_OBJECTIVE_NA, TAG_MICROSCOPE, TAG_CAMERA, TAG_FILE_NAME, TAG_CENTER_X, TAG_CENTER_Y tag ids (table above)
zvi_color, ole_date 0x00BBGGRR → #RRGGBB; OLE date → ISO-8601 local time
TagValue (Empty, Int, Float, Date, Bool, Text, Bytes, Opaque), read_value, as_f64, as_i64, as_text, to_json one typed value
Tag, id, value, attribute, TagList, version, unparsed, get, f64, i64, text, parse_tags tag lists

How this reader was derived, file by file: provenance log.