File Formats#
imread and imwrite designed for 3D and 4D image data in .tiff, .zarr, .h5 formats, but has been extended to include several other formats including suite2p .bin/.ops files, .hdf5 and .npy.
Quick Reference#
Input |
Returns |
Shape |
Description |
|
|---|---|---|---|---|
|
TIFF image stacks |
|||
↳ ScanImage |
|
(T, Z, Y, X) |
Multi-ROI volumetric with phase correction |
|
↳ LBM |
|
(T, Z, Y, X) |
Z-planes as ScanImage channels |
|
↳ Piezo |
|
(T, Z, Y, X) |
Piezo z-stacks, optional averaging |
|
↳ Single-plane |
|
(T, 1, Y, X) |
Single-plane time series |
|
↳ Standard |
|
(T, Z, Y, X) |
Lazy page access, auto-detects volumetric |
|
↳ MBO metadata |
|
(T, Z, Y, X) |
Dask-backed with MBO metadata |
|
|
|
(T, Y, X) |
Direct binary file manipulation |
|
|
|
varies |
HDF5 datasets |
|
|
|
(T, Z, Y, X) |
Zarr v3 / OME-Zarr stores |
|
|
|
varies |
Memory-mapped or in-memory arrays |
|
|
|
varies |
Neurodata Without Borders files |
|
Directory |
||||
↳ |
|
(T, Y, X) |
Suite2p workflow integration |
|
↳ |
|
(T, Z, Y, X) |
Multi-plane Suite2p output |
|
↳ |
|
(T, Z, Y, X) |
Multi-plane TIFF volume |
|
↳ Isoview lightsheet |
|
(T, Z, V, Y, X) |
Multi-view lightsheet data |
Decision Tree#
imread(path)
│
├── np.ndarray? ──────────────────────────────► NumpyArray
│
├── .npy ─────────────────────────────────────► NumpyArray (memory-mapped)
├── .nwb ─────────────────────────────────────► NWBArray
├── .h5 / .hdf5 ──────────────────────────────► H5Array
├── .zarr ────────────────────────────────────► ZarrArray
├── .bin ─────────────────────────────────────► BinArray
│
├── .tif / .tiff
│ ├── Has ScanImage ROI metadata?
│ │ ├── LBM acquisition? ─────────────────► LBMArray
│ │ ├── Piezo enabled? ───────────────────► PiezoArray
│ │ └── Single-plane? ────────────────────► SinglePlaneArray
│ ├── Has MBO metadata? ────────────────────► MBOTiffArray
│ └── Standard TIFF ────────────────────────► TiffArray
│
├── Directory
│ ├── Contains planeXX.tiff? ───────────────► TiffArray (volumetric)
│ ├── Contains planeXX/ with ops.npy? ──────► Suite2pArray (volumetric)
│ ├── Contains ops.npy? ────────────────────► Suite2pArray
│ └── Contains raw ScanImage TIFFs? ────────► ScanImageArray
│
└── List of paths
├── All .tif? ────────────────────────────► TiffArray or MBOTiffArray
└── All .zarr? ───────────────────────────► ZarrArray (stacked along Z)
Tip: Use open_scanimage() for automatic ScanImage subclass detection:
from mbo_utilities.arrays import open_scanimage
arr = open_scanimage("/path/to/data.tif") # Returns LBMArray, PiezoArray, or SinglePlaneArray
Array Type Details#
ScanImage Arrays#
Returned when: Reading raw ScanImage TIFF files
The ScanImage array hierarchy handles different acquisition types from ScanImage:
ScanImageArray (base class)
├── LBMArray # Light Beads Microscopy (z-planes as channels)
├── PiezoArray # Piezo z-stacks with optional frame averaging
└── SinglePlaneArray # Single-plane time series
Use open_scanimage() for automatic stack type detection, or instantiate the specific class directly.
ScanImageArray (Base Class)#
import mbo_utilities as mbo
from mbo_utilities.arrays import open_scanimage, ScanImageArray
# Auto-detect stack type
scan = open_scanimage("/path/to/raw/*.tif")
print(type(scan).__name__) # 'LBMArray', 'PiezoArray', or 'SinglePlaneArray'
print(scan.stack_type) # 'lbm', 'piezo', or 'single_plane'
# Or use imread (returns ScanImageArray base class)
scan = mbo.imread("/path/to/raw/*.tif")
print(scan.shape) # (T, Z, Y, X) - e.g., (10000, 14, 456, 896)
print(scan.num_rois) # Number of ROIs
print(scan.num_planes) # Alias for num_channels (Z planes)
# ROI handling
scan.roi = None # Stitch all ROIs horizontally (default)
scan.roi = 0 # Split into separate ROIs (returns tuple)
scan.roi = 1 # Use only ROI 1 (1-indexed)
scan.roi = [1, 2] # Select specific ROIs
# Phase correction settings
scan.fix_phase = True # Enable bidirectional scan-phase correction
scan.use_fft = True # Use FFT-based phase correction
scan.phasecorr_method = "mean" # "mean", "median", "max"
scan.border = 3 # Border pixels to exclude
scan.upsample = 5 # Subpixel upsampling factor
scan.max_offset = 4 # Maximum phase offset to search
LBMArray#
For Light Beads Microscopy stacks where z-planes are interleaved as ScanImage channels.
from mbo_utilities.arrays import LBMArray, open_scanimage
# Auto-detect and get LBMArray
arr = open_scanimage("/path/to/lbm_data.tif")
# arr is LBMArray if detected as LBM
# Key metadata context (shown in GUI tooltips):
# - Ly: Total height including fly-to lines between mROIs
# - dz: Must be user-supplied (not in ScanImage metadata for LBM)
# - fs: Volume rate (frame rate / num_zplanes)
PiezoArray#
For piezo z-stacks with optional frame averaging.
from mbo_utilities.arrays import PiezoArray, open_scanimage
# With frame averaging enabled
arr = open_scanimage("/path/to/piezo_data.tif", average_frames=True)
# Piezo-specific properties
print(arr.frames_per_slice) # Frames acquired per z-slice
print(arr.log_average_factor) # >1 if pre-averaged at acquisition
print(arr.can_average) # True if averaging is possible
# Toggle averaging (changes shape)
arr.average_frames = True
print(arr.shape) # Reduced T dimension when averaging
# Key metadata context:
# - frames_per_slice: Frames at each z-position before piezo moves
# - log_average_factor: If >1, frames were pre-averaged during acquisition
# - dz: Z-step size from hStackManager.stackZStepSize
SinglePlaneArray#
For single-plane time series without z-stack.
from mbo_utilities.arrays import SinglePlaneArray, open_scanimage
arr = open_scanimage("/path/to/single_plane.tif")
# arr is SinglePlaneArray if no z-stack detected
Key Features (all ScanImage arrays):
Automatic ROI stitching/splitting via
roipropertyBidirectional scan-phase correction (configurable methods)
Stack type detection via
stack_typepropertyROI position extraction from ScanImage metadata
Contextual metadata descriptions via
get_param_description()Stores all ScanImage-metadata in
array.metadata["si"]
Legacy alias: MboRawArray is an alias for ScanImageArray for backwards compatibility.
TiffArray#
Returned when: Reading processed TIFF file(s) without ScanImage metadata, or a directory with planeXX.tiff files
# Single or multiple standard TIFF files
arr = mbo.imread("/path/to/processed.tif")
arr = mbo.imread(["/path/file1.tif", "/path/file2.tif"])
# Returns: TiffArray
print(type(arr)) # <class 'TiffArray'>
print(arr.shape) # (T, 1, Y, X) - 4D with Z=1 for single files
print(arr.dtype) # Data type from TIFF
# Directory with planeXX.tiff files (auto-detected volumetric)
vol = mbo.imread("/path/to/tiff_output/")
# Detects plane01.tiff, plane02.tiff, etc.
print(vol.shape) # (T, Z, Y, X) - e.g., (10000, 14, 512, 512)
print(vol.is_volumetric) # True
print(vol.num_planes) # 14
# Lazy frame reading
frame = arr[0] # Read first frame
subset = arr[10:20] # Read frames 10-19 (only those pages are loaded)
# Dtype conversion
arr32 = arr.astype(np.float32)
Key Features:
Uses
TiffFilehandles for lazy page accessAuto-detects volumetric structure from
planeXX.tifffilename patternsMulti-file support (concatenated along time axis)
Thread-safe page reading
Always outputs 4D format: (T, Z, Y, X) where Z=1 for single files
MBOTiffArray#
Returned when: Reading TIFFs with MBO-specific metadata (uses Dask backend)
# MBO-processed TIFFs with metadata
arr = mbo.imread("/path/to/processed/*.tif")
# Returns: MBOTiffArray if MBO metadata detected
print(type(arr)) # <class 'MBOTiffArray'>
print(arr.shape) # (T, Z, Y, X) - Dask infers shape
print(arr.dask) # Access underlying dask.Array
# Lazy operations via Dask
mean_proj = arr[:100].mean(axis=0).compute()
Key Features:
Dask-backed for truly lazy, chunked access
Uses
tifffile.imread(aszarr=True)for memory-mapped accessAutomatic dimension handling (2D → TZYX, 3D → TZYX, 4D passthrough)
Preserves file tags from filenames
BinArray#
Returned when: Explicitly reading a .bin file path
# Reading a specific binary file
arr = mbo.imread("path/to/data_raw.bin")
# Returns: BinArray
print(type(arr)) # <class 'BinArray'>
print(arr.shape) # (nframes, Ly, Lx)
print(arr.nframes) # Number of frames
print(arr.Ly, arr.Lx) # Spatial dimensions
# Access data like numpy array
frame = arr[0] # First frame
subset = arr[0:100] # First 100 frames
# Write access (if opened for writing)
arr[0] = new_frame
# Context manager support
with BinArray("data.bin", shape=(100, 512, 512)) as arr:
arr[:] = data
# Close when done
arr.close()
Key Features:
Direct binary file access via
np.memmapAuto-infers shape from adjacent
ops.npyif presentCan provide shape manually:
BinArray("file.bin", shape=(1000, 512, 512))Read/write access (supports
__setitem__)Useful for creating new binary files from scratch
When to use:
Reading/writing specific binary files in a Suite2p workflow
Creating new binary files from scratch
When you want to work with the file directly, not through Suite2p’s abstraction
Suite2pArray#
Returned when: Reading a directory containing ops.npy, or ops.npy directly, or a directory with multiple planeXX/ subdirectories
# Reading a Suite2p single-plane directory
arr = mbo.imread("/path/to/suite2p/plane0")
arr = mbo.imread("/path/to/suite2p/plane0/ops.npy")
# Returns: Suite2pArray
print(type(arr)) # <class 'Suite2pArray'>
print(arr.shape) # (nframes, Ly, Lx) - 3D for single plane
print(arr.metadata) # Full ops.npy contents
# Multi-plane Suite2p output (auto-detected volumetric)
vol = mbo.imread("/path/to/suite2p_output/")
# Detects plane01_stitched/, plane02_stitched/, etc.
print(vol.shape) # (T, Z, Y, X) - e.g., (10000, 14, 512, 512)
print(vol.is_volumetric) # True
print(vol.num_planes) # 14
# File paths (single plane)
print(arr.raw_file) # Path to data_raw.bin (unregistered)
print(arr.reg_file) # Path to data.bin (registered)
print(arr.active_file) # Currently active file
# Switch between raw and registered
arr.switch_channel(use_raw=True) # Use data_raw.bin
arr.switch_channel(use_raw=False) # Use data.bin (default)
# Visualization with both channels
iw = arr.imshow() # Shows raw and registered side-by-side if both exist
Key Features:
Full Suite2p context (metadata from
ops.npy)Auto-detects volumetric structure from
planeXX/subdirectory patternsAccess to both raw (
data_raw.bin) and registered (data.bin) dataMemory-mapped via
np.memmapfor lazy loadingFile size validation against ops metadata
For volumes:
switch_channel()applies to all planes
H5Array#
Returned when: Reading HDF5 files (.h5, .hdf5)
# HDF5 dataset
arr = mbo.imread("/path/to/data.h5")
# Returns: H5Array
print(type(arr)) # <class 'H5Array'>
print(arr.shape) # Dataset shape
print(arr.dataset_name) # Auto-detected: 'mov', 'data', or first available
# Optionally specify dataset name
arr = mbo.imread("/path/to/data.h5", dataset="imaging_data")
# Access data
frame = arr[0]
subset = arr[10:20, :, 100:200]
# File-level metadata
print(arr.metadata) # HDF5 file attributes
# Close file handle
arr.close()
Key Features:
Auto-detects common dataset names:
'mov','data','scan_corrections'Lazy loading via
h5py.DatasetSupports ellipsis indexing (
arr[..., 100:200])File-level attributes exposed via
.metadata
ZarrArray#
Returned when: Reading Zarr stores (.zarr directories)
# Zarr store (standard or OME-Zarr)
arr = mbo.imread("/path/to/data.zarr")
# Returns: ZarrArray
print(type(arr)) # <class 'ZarrArray'>
print(arr.shape) # (T, Z, Y, X) - always 4D
# Read multiple zarr stores as z-planes
arr = mbo.imread(["/path/plane01.zarr", "/path/plane02.zarr"])
# Access pre-computed statistics (if available in OME metadata)
print(arr.zstats) # {'mean': [...], 'std': [...], 'snr': [...]}
# Access metadata (OME-NGFF attributes if present)
print(arr.metadata)
Key Features:
Supports both standard Zarr arrays and OME-Zarr groups
Auto-detects OME-Zarr structure (looks for
"0"subarray in groups)Multi-store support (stacked along Z axis)
Zarr v3 compatible
Exposes OME-NGFF metadata via
.metadata
NumpyArray#
Returned when: Reading .npy files OR passing an in-memory numpy array to imread()
This is the most versatile array type - it wraps any numpy array and provides full imwrite() support.
From .npy Files (Memory-Mapped)#
# Read .npy file - memory-mapped for lazy loading
arr = mbo.imread("/path/to/data.npy")
# Returns: NumpyArray
print(type(arr)) # <class 'NumpyArray'>
print(arr.shape) # (T, Y, X) or (T, Z, Y, X)
print(arr.dims) # 'TYX' or 'TZYX' (auto-inferred)
From In-Memory Numpy Arrays#
import numpy as np
import mbo_utilities as mbo
# Create or load a numpy array from anywhere
data = np.random.randn(100, 512, 512).astype(np.float32)
# Wrap with imread - returns NumpyArray
arr = mbo.imread(data)
print(arr)
# NumpyArray(shape=(100, 512, 512), dtype=float32, dims='TYX' (in-memory))
# Now you have full imwrite support with all features
mbo.imwrite(arr, "output", ext=".zarr") # Zarr v3 with chunking/sharding
mbo.imwrite(arr, "output", ext=".tiff") # BigTIFF
mbo.imwrite(arr, "output", ext=".bin") # Suite2p binary + ops.npy
mbo.imwrite(arr, "output", ext=".h5") # HDF5
mbo.imwrite(arr, "output", ext=".npy") # NumPy format
4D Volumetric Data#
# 4D arrays are automatically detected as (T, Z, Y, X)
volume = np.random.randn(100, 15, 512, 512).astype(np.float32)
arr = mbo.imread(volume)
print(arr.dims) # 'TZYX'
print(arr.num_planes) # 15
# Write specific planes
mbo.imwrite(arr, "output", ext=".zarr", planes=[1, 7, 14])
Key Features:
Automatic dimension inference (
TYX,TZYX,YX, etc.)Memory-mapped for
.npyfiles (lazy loading)Full
imwrite()support with all output formatsChunked reduction operations (
mean,std,max,min)Metadata auto-generation from array shape
NWBArray#
Returned when: Reading NWB (Neurodata Without Borders) files
# NWB file with TwoPhotonSeries
arr = mbo.imread("/path/to/experiment.nwb")
# Returns: NWBArray
print(type(arr)) # <class 'NWBArray'>
print(arr.shape) # Shape from TwoPhotonSeries data
# Access data
frame = arr[0]
Key Features:
Reads
TwoPhotonSeriesacquisition data from NWB filesRequires
pynwbpackage (pip install pynwb)Exposes underlying NWB data object
IsoviewArray#
Returned when: Manually instantiated for isoview lightsheet microscopy data
from mbo_utilities.arrays import IsoviewArray
# Isoview lightsheet data (multi-timepoint)
arr = IsoviewArray("/path/to/output")
# Shape: (T, Z, Views, Y, X) - 5D
print(arr.shape) # (10, 543, 4, 2048, 2048)
print(arr.views) # [(0, 0), (1, 0), (2, 1), (3, 1)] - (camera, channel) pairs
print(arr.num_views) # 4
# Access specific view
frame = arr[0, 100, 0] # timepoint 0, z=100, view 0 (camera 0, channel 0)
# Get view index for camera/channel
idx = arr.view_index(camera=1, channel=0)
# Access labels and projections (consolidated structure only)
labels = arr.get_labels(timepoint=0, camera=0, label_type='segmentation')
proj = arr.get_projection(timepoint=0, camera=0, proj_type='xy')
Key Features:
Supports two data structures:
Consolidated:
data_TM000000_SPM00.zarr/camera_0/0/Separate:
SPM00_TM000000_CM00_CHN01.zarr
Multi-view (camera/channel combinations)
5D shape:
(T, Z, Views, Y, X)or 4D(Z, Views, Y, X)for single timepointAccess to segmentation labels and projections
Lazy loading via Zarr
Common Properties Across All Array Types#
All lazy array types provide these standard properties:
Property |
Type |
Description |
|---|---|---|
|
|
Array dimensions |
|
|
Data type |
|
|
Number of dimensions |
|
|
Array/file metadata |
|
|
Source file paths |
|
method |
Write to any output format |
Most array types also provide:
Property |
Type |
Description |
|---|---|---|
|
|
Number of Z-planes |
|
|
Number of ROIs (ScanImage arrays) |
|
|
Stack type: ‘lbm’, ‘piezo’, or ‘single_plane’ (ScanImage arrays) |
|
method |
Release file handles |
PiezoArray-specific properties:
Property |
Type |
Description |
|---|---|---|
|
|
Frames acquired per z-slice |
|
|
Averaging factor from acquisition |
|
|
True if frame averaging is possible |
|
|
Toggle frame averaging on/off |
API Reference#
mbo_utilities.imread()- Smart file readermbo_utilities.imwrite()- Universal file writermbo_utilities.arrays- Direct access to array classes