Creating Recipes
This guide explains how to create segmentation recipes for reproducible workflows, testing, and optimization.
Recipe Formats
SlicerAdaptiveBrush supports two recipe formats:
Format |
Extension |
Use Case |
|---|---|---|
Python function |
|
Programmatic recipes with logic |
JSON action list |
|
Simple recorded workflows |
Python Function Recipes
Basic Structure
# recipes/brain_tumor.py
"""Brain tumor segmentation recipe."""
from SegmentEditorAdaptiveBrushTesterLib import Recipe, Action
def create_recipe() -> Recipe:
"""Create the brain tumor segmentation recipe.
Returns:
Recipe configured for MRBrainTumor1 tumor segmentation.
"""
recipe = Recipe(
name="Brain Tumor Segmentation",
sample_data="MRBrainTumor1",
gold_standard="MRBrainTumor1_tumor",
description="5-click tumor segmentation using watershed algorithm",
)
# Define click locations (RAS coordinates)
clicks = [
{"ras": [-5.31, 34.77, 20.83], "description": "Tumor center"},
{"ras": [-5.31, 25.12, 35.97], "description": "Superior edge"},
{"ras": [-12.45, 30.21, 25.67], "description": "Lateral edge"},
{"ras": [2.15, 32.45, 22.34], "description": "Medial edge"},
{"ras": [-5.31, 38.92, 18.45], "description": "Inferior edge"},
]
# Default parameters
params = {
"algorithm": "watershed",
"brush_radius_mm": 25.0,
"edge_sensitivity": 65,
"threshold_zone": 60,
}
# Add actions
for click in clicks:
recipe.add_action(
Action(
action_type="adaptive_brush",
ras=click["ras"],
params=params,
description=click.get("description"),
)
)
return recipe
With Parameter Variations
def create_recipe() -> Recipe:
"""Recipe with different parameters per click."""
recipe = Recipe(
name="Multi-parameter Segmentation",
sample_data="MRBrainTumor1",
)
# First click: large brush for rough segmentation
recipe.add_action(Action(
action_type="adaptive_brush",
ras=[-5.31, 34.77, 20.83],
params={
"algorithm": "watershed",
"brush_radius_mm": 30.0,
"edge_sensitivity": 50,
},
description="Initial rough segmentation",
))
# Subsequent clicks: smaller brush for refinement
refinement_clicks = [
[-5.31, 25.12, 35.97],
[-12.45, 30.21, 25.67],
]
for ras in refinement_clicks:
recipe.add_action(Action(
action_type="adaptive_brush",
ras=ras,
params={
"algorithm": "watershed",
"brush_radius_mm": 15.0,
"edge_sensitivity": 70,
},
description="Boundary refinement",
))
return recipe
With Conditional Logic
def create_recipe(aggressive: bool = False) -> Recipe:
"""Recipe with conditional parameters.
Args:
aggressive: If True, use more aggressive segmentation.
"""
recipe = Recipe(
name="Conditional Segmentation",
sample_data="MRBrainTumor1",
)
if aggressive:
params = {
"algorithm": "connected_threshold",
"brush_radius_mm": 35.0,
"edge_sensitivity": 30,
}
else:
params = {
"algorithm": "watershed",
"brush_radius_mm": 25.0,
"edge_sensitivity": 65,
}
clicks = [[-5.31, 34.77, 20.83], [-5.31, 25.12, 35.97]]
for ras in clicks:
recipe.add_action(Action(
action_type="adaptive_brush",
ras=ras,
params=params,
))
return recipe
JSON Action Recipes
Basic Structure
{
"version": "1.0",
"name": "Brain Tumor Segmentation",
"description": "5-click tumor segmentation",
"sample_data": "MRBrainTumor1",
"gold_standard": "MRBrainTumor1_tumor",
"actions": [
{
"type": "adaptive_brush",
"ras": [-5.31, 34.77, 20.83],
"params": {
"algorithm": "watershed",
"brush_radius_mm": 25.0,
"edge_sensitivity": 65
},
"description": "Tumor center"
},
{
"type": "adaptive_brush",
"ras": [-5.31, 25.12, 35.97],
"params": {
"algorithm": "watershed",
"brush_radius_mm": 25.0,
"edge_sensitivity": 65
},
"description": "Superior edge"
}
],
"metadata": {
"author": "user",
"created": "2026-01-26T10:00:00",
"notes": "Created from manual segmentation"
}
}
Action Types
Type |
Description |
Required Fields |
|---|---|---|
|
Paint with adaptive brush |
|
|
Standard paint brush |
|
|
Erase voxels |
|
|
Apply threshold |
|
Adaptive Brush Parameters
{
"type": "adaptive_brush",
"ras": [x, y, z],
"params": {
"algorithm": "watershed|geodesic_distance|connected_threshold|...",
"brush_radius_mm": 25.0,
"edge_sensitivity": 50,
"threshold_zone": 50,
"watershed_gradient_scale": 1.2,
"watershed_smoothing": 0.7
}
}
Recording Recipes
Using the Recorder
Open SegmentEditorAdaptiveBrushReviewer module
Click Start Recording
Switch to Segment Editor
Perform segmentation with Adaptive Brush
Return to Reviewer and click Stop Recording
Click Save Recipe
What Gets Recorded
Click locations (RAS coordinates)
Active algorithm
Parameter values at each click
Timestamps
Cleaning Up Recorded Recipes
Recorded recipes may need cleanup:
import json
# Load recorded recipe
with open("recorded_recipe.json") as f:
recipe = json.load(f)
# Remove unnecessary clicks
recipe["actions"] = [a for a in recipe["actions"] if a["ras"][2] > 10]
# Standardize parameters
for action in recipe["actions"]:
action["params"]["brush_radius_mm"] = 25.0
# Save cleaned recipe
with open("cleaned_recipe.json", "w") as f:
json.dump(recipe, f, indent=2)
Converting Between Formats
Python to JSON
from SegmentEditorAdaptiveBrushTesterLib import Recipe
# Load Python recipe
import recipes.brain_tumor as module
recipe = module.create_recipe()
# Convert to JSON
recipe.save_json("brain_tumor.json")
JSON to Python
# Load JSON recipe
with open("brain_tumor.json") as f:
data = json.load(f)
# Generate Python code
code = f'''
from SegmentEditorAdaptiveBrushTesterLib import Recipe, Action
def create_recipe() -> Recipe:
recipe = Recipe(
name="{data['name']}",
sample_data="{data['sample_data']}",
)
'''
for action in data["actions"]:
code += f'''
recipe.add_action(Action(
action_type="{action['type']}",
ras={action['ras']},
params={action['params']},
))
'''
code += '''
return recipe
'''
print(code)
Testing Recipes
Quick Test
from SegmentEditorAdaptiveBrushTesterLib import RecipeRunner
# Load and run
import recipes.brain_tumor as module
recipe = module.create_recipe()
runner = RecipeRunner()
result = runner.run(recipe)
print(f"Voxels segmented: {result.voxel_count}")
With Gold Standard Comparison
from SegmentEditorAdaptiveBrushTesterLib import RecipeTestRunner
runner = RecipeTestRunner()
result = runner.run_recipe("recipes/brain_tumor.py")
print(f"Dice: {result.dice:.4f}")
print(f"Hausdorff95: {result.hausdorff_95:.2f}mm")
print(f"Passed: {result.passed}")
Best Practices
Click Location Selection
Start at center: First click should be in the middle of the structure
Cover edges: Add clicks at boundary regions that need attention
Use landmarks: Place clicks at reproducible anatomical landmarks
Minimize count: Use fewest clicks needed for good segmentation
Parameter Selection
Be consistent: Use same parameters across clicks when possible
Document rationale: Explain why specific values were chosen
Test variations: Verify recipe works with small parameter changes
Recipe Organization
recipes/
├── brain/
│ ├── tumor.py
│ ├── white_matter.py
│ └── ventricles.py
├── chest/
│ ├── lung.py
│ └── heart.py
└── template.py
Version Control
Commit recipes: Store in version control
Track changes: Note parameter updates
Link to gold standards: Reference associated gold standards
Documentation
def create_recipe() -> Recipe:
"""Brain tumor segmentation using watershed algorithm.
This recipe segments the tumor in MRBrainTumor1 sample data.
It uses 5 clicks starting from the tumor center and moving
outward to capture the full extent.
Optimized parameters from 50-trial Optuna optimization:
- Best Dice: 0.9991
- Algorithm: watershed
- Edge sensitivity: 65
Returns:
Recipe configured for tumor segmentation.
Notes:
- Works best with T1-weighted MRI
- May need adjustment for different tumor sizes
- See optimization results: optimization_results/2026-01-26_*/
"""