Below we will review the changes to the API of the SegmentationService
SegmentationService API
Events​
SEGMENTATION_UPDATED -> SEGMENTATION_MODIFIED
Just a rename to match the cornerstone terminology
VolumeId vs SegmentationId​
Previously, we used the SegmentationId as the VolumeId for volume-based segmentations, which led to confusion and issues.
Now, we have two separate IDs: one for the segmentation and one for the volume.
segmentationService.getLabelmapVolume(segmentationId) will return the volume associated with the segmentation.
If your code uses cache.getVolume(segmentationId), update it to use the new getLabelmapVolume method.
getSegmentation(segmentationId)​
remains the same it will return the segmentation object = cornerstone segmentation object with the following properties:
/**
 * Global Segmentation Data which is used for the segmentation
 */
type Segmentation = {
  /** segmentation id  */
  segmentationId: string;
  /** segmentation label */
  label: string;
  segments: {
    [segmentIndex: number]: Segment;
  };
  /**
   * Representations of the segmentation. Each segmentation "can" be viewed
   * in various representations. For instance, if a DICOM SEG is loaded, the main
   * representation is the labelmap. However, for DICOM RT the main representation
   * is contours, and other representations can be derived from the contour (currently
   * only labelmap representation is supported)
   */
  representationData: RepresentationsData;
  /**
   * Segmentation level stats, Note each segment can have its own stats
   * This is used for caching stats for the segmentation level
   */
  cachedStats: { [key: string]: unknown };
};
export type Segment = {
  /** segment index */
  segmentIndex: number;
  /** segment label */
  label: string;
  /** is segment locked for editing */
  locked: boolean;
  /** cached stats for the segment, e.g., pt suv mean, max etc. */
  cachedStats: { [key: string]: unknown };
  /** is segment active for editing, at the same time only one segment can be active for editing */
  active: boolean;
};
Compared to Cornerstone3D 1.x
Previously this function was returning this
export type Segmentation = {
  segmentationId: string;
  type: Enums.SegmentationRepresentations;
  label: string;
  activeSegmentIndex: number;
  segmentsLocked: Set<number>;
  cachedStats: { [key: string]: number };
  segmentLabels: { [key: string]: string };
  representationData: SegmentationRepresentationData;
};
As you can see segmentLabels, segmentsLocked, activeSegmentIndex, are all gathered under the new segments object. We now have support for per segment cachedStats as well.
getSegmentations​
It provides all segmentations in the state. Previously, it accepted a filterNonhydrated flag, but since we've moved away from hydration and every loaded segmentation is now hydrated by default, it returns all segmentations.
getActiveSegmentation​
After migrating to viewport-specific segmentations, different viewports can have distinct active segmentations for editing. The panel will always display the active segmentation when the active viewport changes.
Before (3.8)
// Returns full segmentation object
public getActiveSegmentation(): Segmentation {
  const segmentations = this.getSegmentations();
  return segmentations.find(segmentation => segmentation.isActive);
}
After (3.9)
public getActiveSegmentation(viewportId: string): Segmentation | null {
  return cstSegmentation.activeSegmentation.getActiveSegmentation(viewportId);
}
Key Changes
- Viewport Specificity
- Before: Global active segmentation across all tool groups
 - After: Active segmentation per viewport
 
 - Required Parameters
- Before: No parameters needed
 - After: Requires viewportId parameter
 
 
Migration Examples
Before:
// Get active segmentation
const activeSegmentation = segmentationService.getActiveSegmentation();
if (activeSegmentation) {
  console.log('Active segmentation:', activeSegmentation.segmentationId);
  console.log('Active segment:', activeSegmentation.activeSegmentIndex);
}
After:
// Get active segmentation for specific viewport
const activeSegmentation = segmentationService.getActiveSegmentation('viewport1');
getToolGroupIdsWithSegmentation​
is now -> getViewportIdsWithSegmentation as you guessed
setActiveSegmentationForToolGroup​
-> setActiveSegmentation
Before (OHIF 3.8)
setActiveSegmentationForToolGroup(
  segmentationId: string,
  toolGroupId?: string,
  suppressEvents?: boolean
): void
After (OHIF 3.9)
setActiveSegmentation(
  viewportId: string,
  segmentationId: string
): void
Migration Examples
- 
Basic Usage Update
// Before - OHIF 3.8
segmentationService.setActiveSegmentationForToolGroup(
segmentationId,
toolGroupId
);
// After - OHIF 3.9
segmentationService.setActiveSegmentation(
viewportId,
segmentationId
); 
addSegment​
The addSegment method in OHIF 3.9 has been updated to handle segmentation properties in a viewport-centric way, removing tool group dependencies and simplifying the configuration structure.
Before (OHIF 3.8)
addSegment(
  segmentationId: string,
  config: {
    segmentIndex?: number;
    toolGroupId?: string;
    properties?: {
      label?: string;
      color?: ohifTypes.RGB;
      opacity?: number;
      visibility?: boolean;
      isLocked?: boolean;
      active?: boolean;
    };
  }
): void
After (OHIF 3.9)
addSegment(
  segmentationId: string,
  config: {
    segmentIndex?: number;
    label?: string;
    isLocked?: boolean;
    active?: boolean;
    color?: csTypes.Color;
    visibility?: boolean;
  }
): void
Key Changes
- Configuration Structure
- Removed double nested 
propertiesobject - Configuration options now at top level
 - Removed 
toolGroupIdparameter - Removed 
opacityparameter (now part of color) 
 - Removed double nested 
 - Segment Index Generation
- Changed from length-based to max-value-based indexing
 - More reliable for non-sequential segment indices
 
 - Color Handling
- Color now includes alpha channel (opacity)
 - Applied to all relevant viewports automatically
 
 
Migration Examples
- 
Basic Segment Creation
// Before - OHIF 3.8
segmentationService.addSegment(segmentationId, {
properties: {
label: 'Segment 1'
}
});
// After - OHIF 3.9
segmentationService.addSegment(segmentationId, {
label: 'Segment 1'
}); - 
Creating Segment with Color
// Before - OHIF 3.8
segmentationService.addSegment(segmentationId, {
properties: {
color: [255, 0, 0],
opacity: 255
}
});
// After - OHIF 3.9
segmentationService.addSegment(segmentationId, {
color: [255, 0, 0, 255] // RGB + Alpha
}); - 
Setting Visibility and Lock Status
// Before - OHIF 3.8
segmentationService.addSegment(segmentationId, {
toolGroupId: 'myToolGroup',
properties: {
visibility: true,
isLocked: true
}
});
// After - OHIF 3.9
segmentationService.addSegment(segmentationId, {
visibility: true,
isLocked: true
}); - 
Complete Configuration Example
// Before - OHIF 3.8
segmentationService.addSegment(segmentationId, {
segmentIndex: 1,
toolGroupId: 'myToolGroup',
properties: {
label: 'Tumor',
color: [255, 0, 0],
opacity: 200,
visibility: true,
isLocked: false,
active: true
}
});
// After - OHIF 3.9
segmentationService.addSegment(segmentationId, {
segmentIndex: 1,
label: 'Tumor',
color: [255, 0, 0, 200], // RGB + Alpha
visibility: true,
isLocked: false,
active: true
}); 
Important Changes
- 
Tool Group Removal
// Before - OHIF 3.8
segmentationService.addSegment(segmentationId, {
toolGroupId: 'myToolGroup'
// ... other properties
});
// After - OHIF 3.9
// No tool group needed - automatically applies to all relevant viewports
segmentationService.addSegment(segmentationId, {
// ... properties
}); - 
Segment Index Generation
// Before - OHIF 3.8
// Used array length
segmentIndex = segmentation.segments.length === 0 ? 1 : segmentation.segments.length;
// After - OHIF 3.9
// Uses highest existing index + 1
segmentIndex = Math.max(...Object.keys(csSegmentation.segments).map(Number)) + 1; - 
Color and Opacity
// Before - OHIF 3.8
segmentationService.addSegment(segmentationId, {
properties: {
color: [255, 0, 0],
opacity: 200
}
});
// After - OHIF 3.9
segmentationService.addSegment(segmentationId, {
color: [255, 0, 0, 200] // Combined color and opacity
}); 
getActiveSegment​
now requires viewportId, since we have moved away from global active segmentation to viewport specific one
API Changes
// Before
getActiveSegment(): Segment
// After
getActiveSegment(viewportId: string): Segment | null