Plugin Integration Guide¶
pyCyto integrates with several external tools via a standard wrapper pattern. This page describes how each integration works and how to add new ones.
Integration Patterns¶
1. Python bindings (baremetal)¶
The tool is available as a Python package and called directly:
import cellpose.models as cp_models
model = cp_models.CellposeModel(gpu=True, model_type="cpsam")
masks, _, _ = model.eval(image, diameter=50)
Used by: cyto.segmentation.cellpose, cyto.segmentation.stardist, cyto.preprocessing.register_denoise
2. JVM bridge (PyImageJ)¶
Fiji/TrackMate runs inside a JVM managed by PyImageJ. The imagej pixi environment includes PyImageJ and OpenJDK 11:
import imagej
# Auto-download mode (recommended — caches in ~/.jgo)
ij = imagej.init('sc.fiji:fiji', headless=True)
# Pre-installed Fiji
ij = imagej.init('/opt/Fiji.app', headless=True)
Java classes are imported via scyjava:
import scyjava
Model = scyjava.jimport('fiji.plugin.trackmate.Model')
Used by: cyto.tracking.trackmate, cyto.tracking.trackmate_in_mem
Warm-up on shared NFS: Run once on the login node so all compute nodes reuse the cache:
pixi run -e imagej python -c "import imagej; imagej.init('sc.fiji:fiji')"
3. GPU kernel (pyclesperanto)¶
pyclesperanto uses OpenCL to run GPU kernels directly from Python. The gpu pixi environment includes it:
import pyclesperanto_prototype as cle
cle.select_device("GPU")
labels = cle.voronoi_otsu_labeling(image, spot_sigma=5)
Used by: cyto.postprocessing.contact
4. CLI subprocess¶
For tools with no Python API, invoke via subprocess and exchange data through temporary files:
import subprocess, tempfile, numpy as np
with tempfile.TemporaryDirectory() as tmpdir:
in_path = f"{tmpdir}/input.npy"
out_path = f"{tmpdir}/output.npy"
np.save(in_path, data["image"])
subprocess.run(["external_tool", in_path, out_path], check=True)
result = np.load(out_path)
5. Container-wrapped tool¶
For tools with conflicting environment requirements (e.g. conflicting CUDA versions), wrap in a container. See Docker → Apptainer.
Adding a New Algorithm Class¶
Step 1 — Write the class¶
Follow the standard compute node contract: __init__ takes algorithm params, __call__ takes a dict and returns a dict. Use CellPose in cyto/segmentation/cellpose.py as the canonical example:
# cyto/segmentation/my_segmenter.py
try:
import my_segmenter_lib
except ImportError:
my_segmenter_lib = None
from tqdm import tqdm
class MySegmenter:
def __init__(self, threshold=0.5, verbose=True):
self.name = "MySegmenter"
self.threshold = threshold
self.verbose = verbose
def __call__(self, data: dict) -> dict:
image = data["image"]
if self.verbose:
tqdm.write(f"[{self.name}] segmenting {image.shape}")
label = my_segmenter_lib.segment(image, thresh=self.threshold)
return {"label": label}
Step 2 — Register in main.py¶
Open main.py and add the import alongside existing segmentation imports:
from cyto.segmentation.my_segmenter import MySegmenter
The YAML dispatcher resolves name: MySegmenter to the class using the convention:
cyto.<stage>.<module_file>.<ClassName>
where <stage> is one of preprocessing, segmentation, tracking, postprocessing and <module_file> is the Python filename (without .py). The name: field in the pipeline YAML must match the class name exactly.
Step 3 — Wire into a pipeline YAML¶
pipeline:
segmentation:
- name: MySegmenter
tag: MySegmenter_TCell
channels: [TCell]
input_type: image
output_type: label
args:
threshold: 0.4
output: true
Step 4 — Add the dependency to pixi.toml¶
[feature.myseg.dependencies]
my-segmenter-lib = ">=1.0"
Then install: pixi install -e myseg
Step 5 — Mock in Sphinx conf.py¶
Add to autodoc_mock_imports in doc/source/conf.py so API docs build without the optional dep:
autodoc_mock_imports = [
...
"my_segmenter_lib",
]
Step 6 — Test in isolation¶
import numpy as np
from cyto.segmentation.my_segmenter import MySegmenter
seg = MySegmenter(threshold=0.4, verbose=False)
result = seg({"image": np.random.rand(5, 128, 128).astype("float32")})
assert "label" in result
Step 7 — Package for container execution (optional)¶
If the tool has conflicting environment requirements, wrap it in an Apptainer container. No changes to the Python class are needed — reference the .sif path in the resource YAML:
# configs/distributed/pipeline-resources.yaml
pipeline:
segmentation:
MySegmenter_TCell:
partition: gpu_short
gres: gpu:1
mem: 32G
container: containers/images/my-segmenter.sif
See Docker → Apptainer for building the .sif file.
Adding a New External Tool¶
Create
cyto/<stage>/my_tool.pywith a class that wraps the tool.Guard the import so the default environment does not fail:
try: import my_tool except ImportError: my_tool = None
Add the import to
autodoc_mock_importsindoc/source/conf.pyso Sphinx docs build without the tool installed.Add the dependency to the correct
[feature.<env>]block inpixi.toml.Test with
pixi run -e <env> pytest tests/test_<stage>.py.