Tools¶
BioImageFlow has two tool types, each suited to different kinds of operations.
Process boundaries¶
Choose the tool type by process boundary first:
Use
ProcessingToolfor row-wise or batched work that should run in an isolated worker environment.Use
DataFrameToolfor main-process dataframe loading, filtering, merging, and aggregation.
Tool-specific dependencies for ProcessingTool classes must be imported
inside process_row or process_batch. Module import must stay light so
schemas can be inspected by the orchestrator, docs, tests, and GUIs without
installing every worker dependency. Imports from the Python standard library
and bioimageflow-core are safe at module level.
ProcessingTool¶
ProcessingTool is the workhorse of BioImageFlow.
It runs in an isolated environment and processes data row-by-row or in batches.
When to use: image processing, segmentation, feature extraction, measurement — anything that operates on individual images or arrays.
from pathlib import Path
from typing import Annotated
from bioimageflow_core import GENERAL_ENV, ImageSpec, Template
class MyTool(ProcessingTool):
display_name = "My Tool"
environment = GENERAL_ENV # or a custom EnvironmentSpec for specialized deps
class Inputs:
image: Annotated[Path, ImageSpec()]
threshold: float = 0.5
class Outputs:
mask: Annotated[Path, ImageSpec(semantics={"binary"})] = Template(
"{image.stem}_mask.tif"
)
def process_row(self, arguments: Arguments) -> "MyTool.Outputs":
...
Key properties:
Isolated execution: each tool declares an
EnvironmentSpec. UseGENERAL_ENVfor tools that only need standard scientific packages (numpy, scipy, scikit-image, imageio, tifffile, Pillow) or the Python standard library. Simple download, path, CSV/table, and file utility tools should useGENERAL_ENVinstead of declaring one-off environments. Tools with specialized dependencies declare their ownEnvironmentSpec.Row-level parallelism:
process_rowis called once per row, enabling future parallel execution.Batch mode: override
process_batchfor GPU-batched operations.Explosion: return a list from
process_rowto produce multiple output rows (e.g., tiling).
DataFrameTool¶
DataFrameTool runs in the main process and transforms
entire DataFrames. It has access to pandas and pydantic.
When to use: loading data, filtering rows, reshaping tables, combining results, computing aggregate statistics.
class MyTransform(DataFrameTool):
display_name = "My Transform"
class Inputs:
min_area: float = 100.0
def transform(self, df, arguments):
return df[df["area"] >= arguments.min_area]
Key properties:
Main-process only: has access to the full pandas DataFrame.
Merge control: override
merge_dataframesto customize how multiple upstream DataFrames are combined.Passthrough: use
Passthroughoutputs to signal that input columns are preserved.Source tools: set
accepts_upstream = Falseon tools that produce a DataFrame from constants alone (e.g.Files,Generate). Constructing a source tool with positional upstream arguments raisesSourceToolUpstreamError. See Graph Construction for the source-node patterns.
Dynamic output schemas¶
Most tools declare their output columns statically on the Outputs class.
Two cases need a dynamic schema — output column names that depend on
runtime values:
Inputs-driven schema — override resolve_outputs(cls, inputs) when
the column names come from constant parameters. Generate is the
canonical example, where column_name is a runtime parameter:
from bioimageflow_common_tools import Generate
gen = Generate()
sweep = gen(column_name="x", values=[0.1, 0.5, 1.0])
sweep["x"] # OK — column_name resolved at construction time
sweep["unknown"] # raises ColumnNotFoundError immediately
Upstream-driven schema — built-in merge tools (InnerJoin,
CrossJoin, JoinOnColumn, Concat, Collect) override
resolve_merge_schema(cls, upstream_schemas, inputs) instead, because
their output columns depend on the upstream schemas, not on their own
inputs.
A node’s effective schema is available via Node.get_output_schema(),
and is also what node["col"] consults for construction-time
validation.
IOModel¶
IOModel is the lightweight base class for
Inputs and Outputs. It’s not pydantic — it’s a simple namespace with
type annotations and validation.
class Inputs(IOModel):
image: Annotated[Path, ImageSpec()]
sigma: float = 1.0
Fields without defaults are required (must be bound to upstream columns or constants).
Fields with defaults are optional parameters.
Annotations are used for type checking and template resolution.
Attach
GUIMetato provide GUI hints (see Type System).
Arguments¶
Arguments is the namespace passed to
process_row and process_batch. It contains resolved values for all
input and output fields:
def process_row(self, arguments: Arguments):
arguments.image # Path to the input image
arguments.sigma # float, resolved from constant or column
arguments.mask # Path, resolved from output template
If you access a non-existent attribute, Arguments raises an
AttributeError with close-match suggestions (via difflib).