Habitat domain API

Imaging-side protocols, built-in operators, registries, and SubjectPipeline. This is the embedding API: each protocol is op(subject) or op(field) and returns a typed contract. Import registries from the v2 capability packages (they are not top-level habit exports).

Walkthrough (stop after any step, bring your own Subject): Atomic operators. Concept: Habitat analysis. Arrays in: Load data.

For a whole-cohort study without hand-wiring each operator, use fit_predict() with stages (Python API guide (v2.0), beginner path Quickstart: Python API). The mode-named aliases (two_step_habitat(), direct_pooling_habitat(), one_step_habitat()) remain as thin validators.

A backend is optional. One subject is pipe(subject). Parallel / checkpoints: Parallel execution and fault tolerance.

import the v2 capability packages  # registers all built-ins
from habit.habitat_model import HabitatAssignerRegistry, HabitatModelFitterRegistry
from habit.habitat_features import HabitatFeatureExtractorRegistry
from habit.pipeline import SubjectPipeline
from habit.supervoxel import SupervoxelFeatureExtractorRegistry, SupervoxelizerRegistry
from habit.voxel_features import VoxelFeatureExtractorRegistry

Protocols

Protocol

Call shape

Level

VoxelFeatureExtractor

(Subject) -> VoxelFeatureField

Subject

Supervoxelizer

(VoxelFeatureField) -> Supervoxelization

Subject

SupervoxelFeatureExtractor

enriches supervoxel features

Subject

HabitatModelFitter

fit(units, cohort=...) -> HabitatModel

Cohort

HabitatAssigner

(Supervoxelization) -> HabitatMap

Subject

HabitatFeatureExtractor

(Subject, HabitatMap) -> FeatureTable

Subject

Seedable

set_random_state(seed)

mixin

PoolingMarker

() marker (built-in pool)

Dataflow watershed

Ordered stages and the shared executor

A Stage is a named component slot. Stage name values are custom labels (not role keywords); scientific roles are inferred from position + registry domain. Author stages. The classic named fields (voxel_feature_extractor, supervoxelizer, …, pooling) are a deprecated constructor that expands to the same sequence.

from habit.pipeline.stages import resolve_habitat_stages, run_subject_stage_prefix
from habit.spec import HabitatSpec, Spec, Stage
import habit.recipes as recipes

stages = (
    Stage("extract_voxel_features", Spec("raw", {"modalities": ["T1", "T2"]})),
    Stage("pool", Spec("pool")),
    Stage("fit", Spec("kmeans", {
        "min_habitats": 2, "max_habitats": 10,
        "validation": "elbow", "n_init": 5,
    })),
    Stage("assign", Spec("nearest_centroid")),
    Stage("quantify", Spec("volume")),
)
spec = HabitatSpec(name="direct", stages=stages, random_seed=42)
# Subject-level prefix only (no Cohort required):
# units = run_subject_stage_prefix(subject, spec)
result = recipes.Study(spec=spec).fit_predict(cohort)

Domain: pooling (entry-point group habit.pooling). Built-in marker: PoolingRegistry.create("pool").

Registry pattern (all domains)

from habit.supervoxel import SlicSupervoxelizer, SupervoxelizerRegistry

names = SupervoxelizerRegistry.available()
print(SupervoxelizerRegistry.constructor_signature("slic"))
op = SupervoxelizerRegistry.create("slic", n_supervoxels=50)
op = SlicSupervoxelizer(n_supervoxels=50)

Voxel feature extractors

Domain: voxel_feature_extractor

from habit.voxel_features import (
    RawVoxelFeatures,
    VoxelFeatureExtractorRegistry,
    VoxelRadiomicsFeatures,
    extract_voxel_texture,
)

voxel = VoxelFeatureExtractorRegistry.create(
    "raw",
    modalities=["T1", "T2"],
)
# Equivalent class form:
voxel = RawVoxelFeatures(modalities=["T1", "T2"])
field = voxel(subject)

texture = VoxelRadiomicsFeatures(modalities=["T2"], kernel_radius=3)
# Same extractor, one ImageVolume + mask (no Subject):
field = extract_voxel_texture(image, mask, kernel_radius=3, bin_width=12)
  • raw → RawVoxelFeatures

  • local_entropy → LocalEntropyVoxelFeatures

  • voxel_radiomics → VoxelRadiomicsFeatures (per-voxel texture)

  • concat / expression / kinetic → compose the families above

  • extract_voxel_texture() — volume-level voxel_radiomics

Full names and knobs: Habitat Spec component catalog.

Supervoxelizers

Domain: supervoxelizer

from habit.supervoxel import SupervoxelizerRegistry
# All built-in supervoxelizers use n_supervoxels (not sklearn's
# n_clusters / n_components). Confirm with the constructor:
# SupervoxelizerRegistry.constructor_signature("kmeans")
slic = SupervoxelizerRegistry.create("slic", n_supervoxels=50)
km = SupervoxelizerRegistry.create("kmeans", n_supervoxels=40)
gmm = SupervoxelizerRegistry.create("gmm", n_supervoxels=40)
# All three are Seedable (default seed 0). Current skimage SLIC has no
# RNG; set_random_state still records the seed for API uniformity.
slic.set_random_state(42)
km.set_random_state(42)
unit = slic(field)
  • slic → SlicSupervoxelizer (Seedable; backend currently deterministic)

  • kmeans → KMeansSupervoxelizer (Seedable)

  • gmm → GmmSupervoxelizer (Seedable)

Supervoxel feature extractors

Domain: supervoxel_feature_extractor

from habit.supervoxel import SupervoxelFeatureExtractorRegistry
mean_fx = SupervoxelFeatureExtractorRegistry.create("mean_voxel_features")
rad_fx = SupervoxelFeatureExtractorRegistry.create("supervoxel_radiomics")
  • mean_voxel_features → MeanVoxelFeatures

  • supervoxel_radiomics → SupervoxelRadiomicsFeatures

  • mean / std / percentile → per-supervoxel statistics (compose with concat)

Step inspection (optional)

To observe every habitat pipeline boundary in memory, pass inspect=StepRecorder(...) to a recipe. See Feature preprocessing (“Inspect every step”) and Atomic operators.

from habit.inspection import StepRecorder
import habit.recipes as recipes

rec = StepRecorder(steps=["supervoxels.described"], max_subjects=2)
result = recipes.Study(spec=spec).fit_predict(cohort, inspect=rec)
result.inspection.summary()

Habitat model fitters

Domain: habitat_model_fitter (cohort-level)

from habit.habitat_model import HabitatModelFitterRegistry
fitter = HabitatModelFitterRegistry.create(
    "kmeans",
    n_habitats=4,
    n_init=10,
)
fitter.set_random_state(42)
model = fitter.fit(units, cohort=cohort)

gmm_fitter = HabitatModelFitterRegistry.create("gmm", n_habitats=4)
  • kmeans → KMeansHabitatModelFitter

  • gmm → GmmHabitatModelFitter

Omit n_habitats (or pass None) to select the count over min_habitats..max_habitats via the fitter’s validation score. There is no string "auto" value — that was a documentation error.

Habitat assigners

Domain: habitat_assigner

from habit.habitat_model import HabitatAssignerRegistry
# nearest_centroid requires the fitted HabitatModel (not a bare create()).
assigner = HabitatAssignerRegistry.create("nearest_centroid", model=model)
# Preferred after fit — same object, no registry call needed:
assigner = model.assigner()
habitat_map = assigner(unit)
  • nearest_centroid → NearestCentroidAssigner (constructor arg: model)

Habitat feature extractors

Domain: habitat_feature_extractor

from habit.habitat_features import HabitatFeatureExtractorRegistry
msi = HabitatFeatureExtractorRegistry.create("msi")
ith = HabitatFeatureExtractorRegistry.create("ith_score")
vol = HabitatFeatureExtractorRegistry.create("volume")
# Built-in graph topology family (not a private plugin).
graph = HabitatFeatureExtractorRegistry.create(
    "graph",
    edge_method="min_distance",
    node_method="uniform_grid",
    block_size=8,
)
non_rad = HabitatFeatureExtractorRegistry.create("non_radiomics")
trad = HabitatFeatureExtractorRegistry.create("traditional")
whole = HabitatFeatureExtractorRegistry.create("whole_habitat")
each = HabitatFeatureExtractorRegistry.create("each_habitat")

table = msi(subject, habitat_map)
graph_table = graph(subject, habitat_map)
  • msi → MsiHabitatFeatures

  • ith_score → IthHabitatFeatures

  • volume → HabitatVolumeFeatures

  • graph → GraphHabitatFeatures (built-in; see Graph topology features)

  • non_radiomics → NonRadiomicsHabitatFeatures

  • traditional → TraditionalRadiomicsHabitatFeatures

  • whole_habitat → WholeHabitatRadiomicsFeatures

  • each_habitat → EachHabitatRadiomicsFeatures

Compare features between habitats

After each_habitat (or any wide habitat_{id}_{feature} table), to_habitat_feature_panel() / compare_habitat_features() contrast habitats on the cohort (paired Cliff’s delta, BH-FDR) or on one subject. Figures: plot_habitat_feature_heatmap(), plot_habitat_feature_effect(), plot_habitat_feature_components(), plot_habitat_feature_violin(), plot_habitat_feature_bars(). See Whole / Each Habitat Radiomics. Gallery: Habitat feature contrast.

For array-only callers, use the public kernel helpers extract_graph_features() / HabitatGraphFeatureOptions (same numeric definitions). Prefer this domain / kernel path; the habit.compat.graph_plugin shim was removed in v2.0.0.

SubjectPipeline

from habit.pipeline import SubjectPipeline
pipe = SubjectPipeline(
    voxel_feature_extractor=voxel,
    supervoxelizer=svx,                 # or None for direct clustering
    habitat_assigner=model.assigner(),
    supervoxel_feature_extractor=None,  # optional
)
habitat_map = pipe(subject)
table = pipe.extract_features(subject, [msi, ith, vol])
maps = cohort.map(pipe)

Prefer build_habitat_components() when starting from a HabitatSpec: attribute names on HabitatComponents match Spec / pipeline fields (voxel_feature_extractor, supervoxel_feature_extractor, habitat_model_fitter, habitat_features, and the singular *_feature_preprocessor chains). See Domain protocols and registries.

Direct (no-supervoxel) designs use voxel_units:

from habit.pipeline import voxel_units

units = [voxel_units(voxel(s)) for s in cohort]
model = fitter.fit(units, cohort=cohort)
pipe = SubjectPipeline(voxel, None, model.assigner())

Hand-assembled two-step chain

fields = [voxel(s) for s in cohort]
units = [svx(f) for f in fields]
model = fitter.fit(units, cohort=cohort)
maps = [model.assigner()(u) for u in units]

Image preprocessing domain

Domain: preprocessor (image-space steps). Discover names the same way; parameters live on each component constructor.

from habit.plugins import list_plugins
from habit.image_preprocessing import PreprocessorRegistry

for info in list_plugins("preprocessor"):
    print(info.name, PreprocessorRegistry.constructor_signature(info.name))

Registered names: resample, reorientation, registration, n4_correction, zscore_normalization, histogram_standardization, adaptive_histogram_equalization, dcm2nii, custom_preprocessor.