Type System¶
BioImageFlow uses Python’s Annotated types to describe image data flowing
through the pipeline. The type system catches incompatibilities at
graph-construction time, before any computation runs.
ImageSpec¶
ImageSpec is a frozen dataclass with four optional
constraint sets:
@dataclass(frozen=True)
class ImageSpec:
semantics: frozenset[str] = frozenset() # what pixels represent
layouts: frozenset[str] = frozenset() # axis ordering
dtypes: frozenset[str] = frozenset() # numpy dtypes
formats: frozenset[str] = frozenset() # "memory" or file formats
An empty set means wildcard — any value is accepted.
Semantic¶
Semantic describes what the pixel values represent:
Value |
Meaning |
|---|---|
|
Binary mask (0/1) |
|
Instance or semantic label map (integers) |
|
Raw intensity image |
|
Probability map (0.0–1.0) |
|
Vector field / displacement map |
|
Feature map (e.g., embeddings) |
Semantic Groups¶
SCALAR_IMAGE_SEMANTICS is a convenience set for consumers that accept
displayable scalar raster images without requiring one specific pixel meaning:
from pathlib import Path
from typing import Annotated
from bioimageflow_core import ImageSpec, SCALAR_IMAGE_SEMANTICS
image: Annotated[Path, ImageSpec(semantics=SCALAR_IMAGE_SEMANTICS)]
It contains INTENSITY, BINARY, LABEL, and PROBABILITY. It
intentionally excludes DISPLACEMENT and FEATURE. This is useful for
visualization and montage tools; algorithms that require raw physical values
should still declare Semantic.INTENSITY specifically.
Layout¶
Layout describes the axis ordering:
Value |
Axes |
|
|---|---|---|
|
YX |
2 |
|
CYX |
3 |
|
TYX |
3 |
|
TCYX |
4 |
|
ZYX |
3 |
|
CZYX |
4 |
|
TZYX |
4 |
|
TCZYX |
5 |
Compatibility checking¶
check_compatibility() validates that a producer’s
output type is compatible with a consumer’s input type. The rules use
asymmetric wildcard semantics:
Empty consumer set = accept anything (wildcard)
Empty producer set + non-empty consumer set = warn but accept
Both non-empty = sets must intersect
from bioimageflow_core import ImageSpec, check_compatibility
producer = ImageSpec(semantics=frozenset({"label"}))
consumer = ImageSpec(semantics=frozenset({"label", "binary"}))
check_compatibility(producer, consumer) # True: {"label"} & {"label", "binary"} = {"label"}
consumer_strict = ImageSpec(semantics=frozenset({"binary"}))
check_compatibility(producer, consumer_strict) # False: {"label"} & {"binary"} = {}
Semantic groups do not add subtype rules. For example, a binary producer is
still incompatible with a strict intensity consumer; the consumer must declare
SCALAR_IMAGE_SEMANTICS or an explicit set containing BINARY.
This checking happens automatically when you bind a column reference to an input — you don’t need to call it manually.
GUIMeta¶
GUIMeta attaches GUI hints to Inputs and
Outputs fields using the same Annotated mechanism as ImageSpec.
A separate GUI can introspect these hints to render appropriate widgets,
labels, and tooltips.
from pathlib import Path
from typing import Annotated
from bioimageflow_core import Connectable, GUIMeta, ImageSpec, Semantic, Template
class Inputs(IOModel):
image: Annotated[
Path,
ImageSpec(semantics={Semantic.INTENSITY}),
GUIMeta(
display_name="Input image",
description="Fluorescence image to segment.",
connectable=Connectable.BY_DEFAULT,
),
]
diameter: Annotated[float, GUIMeta(
display_name="Cell diameter",
description="Approximate cell diameter, in pixels.",
min=1.0, max=500.0, step=0.5,
)] = 30.0
Parameters:
display_name (
str | None): human-readable label shown in the GUI. WhenNone, frontends fall back to the field name.description (
str | None): longer help text for tooltips / inline help, describing what the field means and when to change it.connectable (
Connectable, defaultConnectable.NOT_BY_DEFAULT): controls pin visibility forInputsfields.NEVERhides the pin entirely,NOT_BY_DEFAULTshows it only when toggled via checkbox,BY_DEFAULTshows it out of the box. Ignored forOutputsfields, which always expose a pin.min / max (
float | None): numeric bounds for the widget.step (
float | None): step increment for spinbox or slider widgets.group (
str | None): logical group name for tabs or sections (e.g."general","advanced","gpu").
Fields without GUIMeta default to Connectable.NOT_BY_DEFAULT with no
label, description, numeric bounds, or group. Data input fields (image paths)
should use explicit GUIMeta(connectable=Connectable.BY_DEFAULT) to show
their pins.
Output fields can also carry GUIMeta so the GUI can label output pins and
show tooltips:
class Outputs(IOModel):
mask: Annotated[
Path,
ImageSpec(semantics={Semantic.LABEL}),
GUIMeta(
display_name="Segmentation mask",
description="Label image; each cell gets a unique ID.",
),
] = Template("{input_image.stem}_mask{ext}")
cell_count: Annotated[int, GUIMeta(
display_name="Cell count",
description="Number of cells detected.",
)]
GUIMeta and ImageSpec coexist as separate Annotated metadata
entries:
image: Annotated[
Path,
ImageSpec(semantics={Semantic.INTENSITY}),
GUIMeta(connectable=Connectable.BY_DEFAULT),
]
Introspection¶
Programmatic schema introspection (get_inputs_schema,
serialize_input_schema, serialize_output_schema, the
Connectable serialization, and the wire-format helpers) lives in
the GUI tree — see Tool Schemas.