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

.py

Programmatic recipes with logic

JSON action list

.json

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

adaptive_brush

Paint with adaptive brush

ras, params

paint

Standard paint brush

ras, radius

erase

Erase voxels

ras, radius

threshold

Apply threshold

lower, upper

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

  1. Open SegmentEditorAdaptiveBrushReviewer module

  2. Click Start Recording

  3. Switch to Segment Editor

  4. Perform segmentation with Adaptive Brush

  5. Return to Reviewer and click Stop Recording

  6. 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

  1. Start at center: First click should be in the middle of the structure

  2. Cover edges: Add clicks at boundary regions that need attention

  3. Use landmarks: Place clicks at reproducible anatomical landmarks

  4. Minimize count: Use fewest clicks needed for good segmentation

Parameter Selection

  1. Be consistent: Use same parameters across clicks when possible

  2. Document rationale: Explain why specific values were chosen

  3. 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

  1. Commit recipes: Store in version control

  2. Track changes: Note parameter updates

  3. 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_*/
    """