# Segmenting a DICOM volume in 3D Slicer's Segment Editor
A concrete end-to-end workflow for segmenting an anatomical structure (bone, organ, lesion) from a DICOM series using the **Segment Editor** module in 3D Slicer, with matching Python-console equivalents for automation. Verified against 3D Slicer 5.x docs, the Slicer script repository, and core-developer Discourse answers.
## 1. Load the DICOM volume
**GUI:** Open the **DICOM** module. If the database browser is not visible, click **Show DICOM database**. Import your folder, then double-click the patient, study, or series to load it (or select items — Shift/Ctrl for ranges — and click **Load**). Dragging a folder onto the Slicer window also offers to import it into the DICOM database.
**Python (console or scripted module):**
```python
from DICOMLib import DICOMUtils
dicomDataDir = "/PATH/TO/DICOM_FOLDER"
loadedNodeIDs = []
with DICOMUtils.TemporaryDICOMDatabase() as db:
DICOMUtils.importDicom(dicomDataDir, db)
patientUIDs = db.patients()
for patientUID in patientUIDs:
loadedNodeIDs.extend(DICOMUtils.loadPatientByUID(patientUID))
volumeNode = slicer.util.getNode(loadedNodeIDs[0]) # loaded scalar volume
```
To load one specific series instead of the whole patient: `DICOMUtils.loadSeriesByUID([SERIES_INSTANCE_UID])`, which returns the loaded node IDs.
## 2. Set up the Segment Editor
**GUI:** Switch to the **Segment Editor** module. Set **Master volume** to the loaded volume, create a new segmentation (the **Segmentation** selector), and click **Add** to create a segment. Click **Show 3D** to see a live surface preview while you work. **Undo/Redo** is available if an effect misbehaves.
**Python:** create the segmentation node, match it to the volume geometry, and add segments:
```python
segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
segmentationNode.CreateDefaultDisplayNodes()
segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(volumeNode)
segmentID = segmentationNode.GetSegmentation().AddEmptySegment("bone")
```
To drive effects without a GUI, create a headless Segment Editor widget (this is the documented pattern; the Slicer script repository advises that for full batch scripting it can be simpler to run the underlying VTK/ITK filters directly):
```python
segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
segmentEditorNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentEditorNode")
segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
segmentEditorWidget.setSegmentationNode(segmentationNode)
segmentEditorWidget.setMasterVolumeNode(volumeNode)
```
If the Segment Editor module has been opened before, you can reuse its live widget instead of creating a new one: `segmentEditorWidget = slicer.modules.SegmentEditorWidget.editor`, then `slicer.util.selectModule("SegmentEditor")` to jump to it.
Select the active segment before each effect:
```python
segmentEditorNode.SetSelectedSegmentID(segmentID)
```
## 3. Segment with effects
### Threshold — fast first pass for high-contrast structures
**GUI:** Choose **Threshold**, move the lower/upper sliders (use the histogram), and click **Apply** to write the thresholded region into the selected segment. Global thresholding works best for cases like bone in CT; it is the classic starting point.
**Python:**
```python
segmentEditorWidget.setActiveEffectByName("Threshold")
effect = segmentEditorWidget.activeEffect()
effect.setParameter("MinimumThreshold", LOWER_VALUE)
effect.setParameter("MaximumThreshold", UPPER_VALUE)
effect.self().onApply()
```
**Tip:** Threshold also works as an intensity gate for other tools: set it as the **Editable intensity range** (masking settings) so Paint, Scissors, and other effects only touch voxels inside your intensity range.
### Grow from seeds — for structures thresholding cannot isolate
**GUI (this is an interactive, two-step effect):**
1. Add at least **two segments** — one inside the target structure, one inside the background/tissue around it. Only **visible** segments participate.
2. With **Paint** (or another brush), paint seed strokes inside the target on several slices, and seeds for the background segment in the surrounding anatomy.
3. Choose **Grow from seeds** and click **Initialize** to compute the preview (first computation is the slow one). Refine by adding more seeds and clicking **Update**, or enable **Auto-update**. Click **Apply** to overwrite the seed segments with the grown result; **Cancel** discards the preview but keeps your seeds.
### Islands — remove stray fragments
**GUI:** Choose **Islands**, pick **Keep largest island** to drop disconnected fragments, or **Remove small islands** with a **Minimum size** (in voxels). **Split islands to segments** turns each qualifying island into its own segment; **Keep/Remove selected island** keeps only the island you click.
**Python:**
```python
segmentEditorWidget.setActiveEffectByName("Islands")
effect = segmentEditorWidget.activeEffect()
effect.setParameter("Operation", "REMOVE_SMALL_ISLANDS")
effect.setParameter("MinimumSize", MIN_VOXELS)
effect.self().onApply()
```
### Margin — grow or shrink the boundary
**GUI:** Choose **Margin** and set a positive margin to dilate the segment or a negative one to erode it (millimeters).
**Python:**
```python
segmentEditorWidget.setActiveEffectByName("Margin")
effect = segmentEditorWidget.activeEffect()
effect.setParameter("MarginSizeMm", MARGIN_MM) # positive = grow, negative = shrink
effect.self().onApply()
```
### Smoothing — clean up the result
**GUI:** Choose **Smoothing**, pick a method, and click **Apply** to smooth the whole segment. Dragging with the mouse applies the same method locally (brush size only marks the region, not the strength). Methods:
- **Median** — removes small extrusions, fills small gaps; keeps contours mostly unchanged.
- **Opening** — removes extrusions smaller than the kernel size (removes only).
- **Closing** — fills sharp corners and holes smaller than the kernel size (adds only).
- **Gaussian** — strongest smoothing, tends to shrink the segment.
- **Joint smoothing** — smooths all visible segments together while preserving a watertight interface between them; higher segments in the table take priority on overlap.
**Python:**
```python
segmentEditorWidget.setActiveEffectByName("Smoothing")
effect = segmentEditorWidget.activeEffect()
effect.setParameter("SmoothingMethod", "MEDIAN") # or OPENING, CLOSING, GAUSSIAN, JOINT
effect.setParameter("KernelSizeMm", KERNEL_MM)
effect.self().onApply()
```
Clean up the headless widget when done:
```python
segmentEditorWidget = None
slicer.mrmlScene.RemoveNode(segmentEditorNode)
```
## 4. Export the segmentation
**GUI:** Go to the **Segmentations** module, open the **Export to files** section (also reachable from the Segment Editor via the dropdown of the **Segmentations** button), choose **NRRD** or **NIFTI** as the file format — NRRD is recommended for general use — and optionally set a **Reference volume** so the output geometry (origin, spacing, axes) matches your source volume exactly. Saving a binary-labelmap segmentation as `.seg.nrrd` also works from **File / Save** and preserves segment names, colors, and terminology metadata in the file's custom fields.
**Python:** export visible segments to a labelmap volume matching your source volume, then save:
```python
labelmapNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLLabelMapVolumeNode")
slicer.modules.segmentations.logic().ExportVisibleSegmentsToLabelmapNode(
segmentationNode, labelmapNode, volumeNode)
slicer.util.saveNode(labelmapNode, "/PATH/TO/segmentation.nrrd")
```
## A proven effect order
For a structure like a bone in CT: **Threshold** (rough pass) → **Islands / Keep largest island** (drop fragments) → **Margin** (small dilation to recover thin edges, or erosion to separate touching structures) → **Smoothing / Median** (clean contours) → **Export to files**. For soft-tissue structures where thresholding fails: paint seeds for target and background and use **Grow from seeds** instead of Threshold, then continue with Margin/Smoothing cleanup.