Imaging Format Readers¶
Beyond the SimpleITK formats handled by konfai/utils/ITK.py, KonfAI ships two
optional readers for formats common in clinical and bioimaging workflows: DICOM
series and OME-Zarr (OME-NGFF) stores. Both live in konfai/utils/, are wired
into konfai.utils.dataset.Dataset, and support channel-first, geometry-aware
reads and writes.
Each reader is an optional dependency — install only what a given dataset needs.
DICOM¶
Install the reader:
pip install "konfai[dicom]" # pulls in pydicom
A DICOM acquisition is not a folder of independent images: a CT or MRI series
is a set of .dcm files that together define one 3-D volume. The reader handles
series discovery, slice ordering, geometry extraction, and CT intensity rescale.
read_dicom_series¶
from konfai.utils.dicom import read_dicom_series
volume, origin, spacing, direction = read_dicom_series("path/to/series")
konfai.utils.dicom.read_dicom_series(directory, *, series_uid=None, apply_rescale=True)
returns a four-tuple:
Returned |
Shape |
Meaning |
|---|---|---|
|
|
channel-first voxel data |
|
|
physical position of the first voxel, mm |
|
|
voxel size |
|
|
row-major 3×3 direction-cosine matrix, flattened |
The origin / spacing / direction triple maps directly onto an Attribute
(Origin, Spacing, Direction; see Datasets and groups), so a DICOM series travels
through the pipeline under the same geometry contract as any other format.
Key behaviors:
Slice ordering uses
ImagePositionPatientprojected onto the slice normal derived fromImageOrientationPatient, not filename orInstanceNumber.apply_rescale=True(the default) appliesRescaleSlope/RescaleInterceptto convert stored values to Hounsfield Units for CT. Set it toFalseto keep raw integers (for example for label maps).Missing geometry tags, inconsistent slice shapes, and unreadable pixel data all raise
DatasetManagerErrorwith an actionable message.
Multi-series folders: series_uid¶
A single folder can hold more than one series (for example a T1 and a T2 from the
same session). read_dicom_series resolves this as follows:
one series present → it is used automatically;
multiple series with
series_uid=None→DatasetManagerErrorlisting the availableSeriesInstanceUIDs;pass
series_uid="1.2.840…"to select one explicitly.
Use konfai.utils.dicom.discover_series(directory) to list the available UIDs
first:
from konfai.utils.dicom import discover_series, read_dicom_series
series = discover_series("path/to/study") # {uid: [Path, ...]}
uid = next(iter(series))
volume, origin, spacing, direction = read_dicom_series("path/to/study", series_uid=uid)
write_dicom_series writes one uncompressed scalar DICOM series. Integer data
round-trips exactly; floating-point data is stored as signed 16-bit pixels with
RescaleSlope and RescaleIntercept.
OME-Zarr¶
Install the reader:
pip install "konfai[omezarr]" # pulls in zarr + ngff-zarr
Unlike a NIfTI file, an OME-Zarr array is already lazy: it is stored as chunked Zarr, so reading a sub-region only fetches the chunks it touches. This maps naturally onto KonfAI’s patch-based loading — the reader never materializes the whole volume.
Multiscale levels¶
OME-NGFF stores a multiscale pyramid: the same image at several resolutions,
level 0 being full resolution and each higher level a downsampled copy. Each
level carries its own physical scale (spacing) and translation (origin) in the
.zattrs metadata, so geometry stays correct at every level.
read_ome_zarr_slice¶
from konfai.utils.ome_zarr import read_ome_zarr_slice
patch, axes, scale, translation = read_ome_zarr_slice(
"image.zarr",
(slice(0, 64), slice(0, 256), slice(0, 256)), # (Z, Y, X)
level=0,
)
konfai.utils.ome_zarr.read_ome_zarr_slice(store_path, slices, *, level=0, channel=None, timepoint=0)
reads one spatial patch and returns:
Returned |
Meaning |
|---|---|
|
channel-first |
|
axis names from the OME-NGFF metadata (e.g. |
|
voxel spacing for the selected level |
|
origin translation for the selected level |
The reader inspects the stored axes to place the spatial slices on the right
dimensions and to index optional T (time) and C (channel) axes, so the same
call works for ZYX, CZYX, and TCZYX arrays. Local .zarr directories and
remote stores (s3://, gs://) are both supported.
Choosing a resolution: select_level¶
konfai.utils.ome_zarr.select_level(zattrs, target_spacing_mm=None) picks the
pyramid level whose voxel spacing is closest to a target:
import zarr
from konfai.utils.ome_zarr import select_level, read_ome_zarr_slice
zattrs = dict(zarr.open("image.zarr", mode="r").attrs)
level = select_level(zattrs, target_spacing_mm=1.0) # nearest level to 1 mm
patch, *_ = read_ome_zarr_slice("image.zarr", (slice(0, 64),) * 3, level=level)
With target_spacing_mm=None it returns level 0 (full resolution). Otherwise it
compares the median spatial scale of each level against the target and returns the
closest — letting you trade resolution for field of view at a fixed patch size.
Use konfai.utils.ome_zarr.get_ome_zarr_info(store_path, level=0) for a metadata
summary (axes, shape, chunks, dtype, scale, translation, n_levels)
without reading any pixels.
write_ome_zarr writes a single-level OME-NGFF store with channel/spatial axes,
chunking, scale, translation, and the original KonfAI attributes.
Use as a KonfAI dataset¶
Both formats use the normal grouped Dataset API:
from konfai.utils.dataset import Dataset
dicom_dataset = Dataset("DatasetDicom", "dicom")
ome_dataset = Dataset("DatasetOme", "omezarr") # aliases: ome-zarr, ome_zarr, zarr
dicom_dataset.write("CT", "CASE_001", volume, attributes)
patch, attributes = dicom_dataset.read_data_slice(
"CT", "CASE_001", (slice(None), slice(10, 20), slice(32, 96), slice(32, 96))
)
ome_dataset.write("CT", "CASE_001", volume, attributes)
names = ome_dataset.get_names("CT")
The layouts are <root>/<case>/<group>/*.dcm and
<root>/<case>/<group>.ome.zarr. get_infos reads only metadata, OME patch
reads touch only selected chunks, and DICOM patch reads decode only selected
slices. In workflow YAML use ./Dataset:dicom or ./Dataset:omezarr in
dataset_filenames.
Why ngff-zarr over ome-zarr¶
Two Python libraries read OME-NGFF: the OME consortium’s ome-zarr and
ngff-zarr. KonfAI’s omezarr extra depends on ngff-zarr (alongside raw
zarr) for one decisive reason:
ngff-zarrexposes per-scale physical coordinates — thescaleandtranslationof each pyramid level — as plain numeric arrays that convert directly to SimpleITK geometry. That is exactly the(Origin, Spacing, Direction)triple KonfAI stores in anAttribute, so OME-Zarr input lines up with every other format without a bespoke geometry adapter.
It also handles multiscale selection transparently and adds helpers tuned for 3-D
medical/bioimage workflows. When ngff-zarr is unavailable, the reader falls back
to parsing the .zattrs JSON with zarr alone.
See also¶
Datasets and groups — the dataset and
Attributemodel these readers feedInstallation — the optional-extras table