Transforms#
Tip
See the KonfAI on real data for before/after images generated by the actual transform implementations.
Transforms are deterministic pre/post-processing steps in
konfai/data/transform.py. They run every time (train / predict / evaluate),
either whole-image at load time (transforms:) or per extracted patch
(patch_transforms:), under a dataset group:
Dataset:
groups_src:
CT:
groups_dest:
CT:
transforms:
Standardize: { } # bare name → konfai.data.transform.Standardize
is_input: true
A transform whose base class is TransformInverse can be run in reverse during
post-processing / reassembly when you pass inverse: true.
Important
A transform that changes the spatial shape must implement transform_shape()
correctly — patch planning depends on an exact prediction. The Shape column
below flags those. Transforms that only change the channel count (OneHot,
Argmax, Sum, …) intentionally do not override it (patching strips the
channel axis first). Inv flags whether a working inverse() exists.
Patch streaming#
A patch is cut straight from disk when every transform in the chain can name the source region that patch depends on. A chain that cannot name it falls back to the whole volume: the case is loaded whole, and the result is the same, just resident. The Stream column below is that answer, per transform.
Streaming happens where the cache is off. A cached case is already in memory, so
its patches are cut from memory whatever the column says. Training exposes
use_cache under Dataset: and defaults it to true; PREDICTION carries no
use_cache key and does not cache; EVALUATION caches. A memory_budget under
Dataset: overrides all three with a size estimate.
Three rules apply to the chain rather than to any single transform:
One region transform per chain.
Dilate,Gradient,Crop,Canonical,Flip,Permuteand the resamplers each read a region other than the patch itself. Two of them in one chain —[Dilate, Gradient],[Canonical, Permute]— load the whole volume. Augmentations count: a copy’s draw is planned as part of the same chain.A statistic is seeded from the stored volume.
Normalize,StandardizeandClip(min_value="min")read their statistic from disk, which is their input only if every earlier stage left the values alone. Reorienting does; mapping values does not.[Canonical(), Normalize()]streams,[Clip(-200, 400), Normalize()]loads whole. A cast keeps the statistic only where it keeps every value:float32andfloat64do,float16and an integer cast do not.A halo must stay within half the patch.
Dilate(4)streams at patch 64 and at patch 8, and loads whole at patch 4. The bound is checked per axis against the patch size, or against the case’s own extent where that is smaller.
A streamed patch reproduces the whole-volume result exactly, borders included. Four kinds round rather than diverge:
A seeded statistic is summed in a different order than the whole-volume pass, so a standardised voxel can land a few float32 ulps away.
A trilinear resample computes its interpolation weights in the read sub-region’s frame. The deviation scales with the local intensity gradient and with the extent, not with the voxel’s own value, and is a small fraction of the intensity range. Nearest-neighbour interpolation uses no weights and stays exact.
A
Translatedraw is sampled the same way and rounds for the same reason.An integer volume through a streamed resample quantises that rounding, so a voxel can land one least-significant bit away.
A transform you write yourself starts at the whole volume and streams nothing until it declares otherwise.
Intensity & normalisation#
Name |
Purpose |
Key args (defaults) |
Shape |
Inv |
Stream |
|---|---|---|---|---|---|
|
Clamp intensities to a fixed or data-dependent range. |
|
no |
no |
yes — fixed bounds clamp each voxel on its own, and a |
|
Linear map to |
|
no |
yes |
yes — Min/Max read from disk |
|
Map |
|
no |
no |
yes |
|
Zero-mean / unit-std from cached, given, or mask-derived stats. |
|
no |
yes |
yes — Mean/Std read from disk, or neither when both are given; no with |
|
SimpleITK histogram match to a reference group. |
|
no |
no |
no — the LUT needs the volume’s whole histogram |
Geometry & resampling#
Name |
Purpose |
Key args (defaults) |
Shape |
Inv |
Stream |
|---|---|---|---|---|---|
|
|
|
yes |
yes |
no‡ |
|
Crop to foreground bounding box; caches the box; updates Origin. |
|
yes |
yes (pads back) |
yes — once the |
|
Resample to a target voxel spacing (per-axis |
|
yes |
yes |
yes — resampled from the source region |
|
Resample to a target shape (per-axis |
|
yes |
yes |
yes — resampled from the source region |
|
Warp by stored SimpleITK transforms read from the dataset. |
|
no |
no |
no — nothing bounds how far the stored displacement reaches |
|
Reorient to canonical direction (3-D); updates Origin/Direction. |
|
no |
yes |
yes — when the case’s direction is a signed axis permutation; no on an oblique one (it is resampled) |
|
Permute spatial axes. |
|
yes |
yes |
yes — index remap |
|
Flip spatial axes. |
|
no |
yes (self-inverse) |
yes — index remap |
|
|
|
yes |
yes |
no‡ |
|
Flatten to 1-D. |
— |
yes |
no |
no‡ |
Labels & masks#
Name |
Purpose |
Key args (defaults) |
Shape |
Inv |
Stream |
|---|---|---|---|---|---|
|
Cast dtype; caches original for inverse. |
|
no |
yes |
yes |
|
One-hot encode a label map (changes channels). |
|
no† |
yes |
yes |
|
|
|
no† |
no |
yes at |
|
|
|
no |
no |
yes at |
|
Binarise selected labels (else |
|
no |
no |
yes |
|
Remap labels; entries are |
|
no |
no |
yes |
|
Set voxels where mask==0 to |
|
no |
no |
no — it cannot tell where its tensor sits, so it cannot read the matching mask region |
|
Binary dilation via max-pool (2D/3D). |
|
no |
no |
yes — halo of |
|
Sum over |
|
no† |
no |
yes at |
|
Gradient-magnitude image (or components). |
|
no† |
no |
yes — halo of 1 voxel |
Ensemble / uncertainty post-processing#
Operate on a stacked [N, …] ensemble axis (prediction post-processing).
Name |
Purpose |
Key args |
Shape |
Stream |
|---|---|---|---|---|
|
Aggregate an ensemble stack (mean / median / seg-argmax); writes an |
|
no |
no — it writes the whole per-member stack |
|
Vector magnitude over the trailing axis (drops it). |
— |
yes |
no‡ |
|
Per-voxel variance over N. |
— |
no† |
yes |
|
Per-voxel std over N. |
— |
no† |
yes |
|
Per-voxel label disagreement across N segmentations. |
|
no† |
yes |
|
|
|
no |
yes |
Side-effect & advanced#
Name |
Purpose |
Stream |
|---|---|---|
|
Records ImageMin/Max/Mean/Std to the attribute cache and returns the tensor unchanged (feeds the perceptual criteria |
no‡ |
|
Writes the preprocessed volume to a cache dataset and passes the tensor through. Once that cache exists it is read instead, and the transforms before it are skipped. |
no — it needs the whole volume to write |
|
Run a nested KonfAI app inference in a spawned subprocess. Needs |
no‡ |
† changes the channel dimension, not spatial — no transform_shape
override needed.
‡ declares no patch locality, so the case is loaded whole. That is the safe
default every Transform inherits, not a statement that the operation could not
be streamed.
Next steps#
Augmentations — the random, train-time counterpart
Datasets and groups — where transforms sit in a dataset config
Extension points — write your own
Transform(implement__call__andtransform_shape())