@cornerstonejs/tools
triggerAnnotationRenderForViewportIds
现在只需要 viewportIds,不再需要 renderingEngine。
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIds) ---> triggerAnnotationRenderForViewportIds(viewportIds)
Details
为什么?
因为每个视口都有一个渲染引擎,因此不需要将渲染引擎作为参数传递。工具
StackScrollMouseWheelTool -> StackScrollTool
我们已经将鼠标滚轮与工具本身解耦,使其可以像其他鼠标绑定一样应用为绑定。
此更改带来了多个优势:
- 它可以与其他鼠标绑定组合使用
- 它可以与键盘绑定配对使用
- 之前 📦
- 之后 🚀🚀
cornerstoneTools.addTool(StackScrollMouseWheelTool);
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
cornerstoneTools.addTool(StackScrollTool);
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel,
},
],
});
BaseTool
getTargetVolumeId 方法已被移除,取而代之的是 getTargetId,而 getTargetIdImage 已重命名为 getTargetImageData,以更清楚地表明它是图像数据。
使用示例
- 之前 📦
- 之后 🚀
const volumeId = this.getTargetVolumeId(viewport);
const imageData = this.getTargetIdImage(targetId, renderingEngine);
const imageData = this.getTargetImageData(targetId);
新的分割模型
我们有一个新的分割模型,更加灵活且易于使用。
相同术语,不同架构
在 Cornerstone3D 版本 2 中,我们对分割模型进行了重大架构更改,同时保持了熟悉的术语。此重新设计旨在为在不同视口中处理分割提供更灵活和直观的方法。以下是主要更改及其背后的原因:
-
视口特定,而非基于工具组:
- 以前:分割与工具组绑定,工具组通常由多个视口组成。当用户希望在同一工具组内为某些视口添加分割而不是其他视口时,这会带来复杂性。
- 现在:分割现在是视口特定的。用户可以直接向视口添加分割,而不是向工具组添加或移除表示。这为每个视口渲染的内容提供了更细致的控制。
- 为什么:我们发现将渲染绑定到工具组并不是一种有效的方法。它通常需要为特定视口创建额外的工具组以进行自定义或防止渲染。
-
简化分割表示的识别:
- 以前:需要一个唯一的
segmentationRepresentationUID进行识别。 - 现在:分割表示通过
segmentationId和表示type的组合进行识别。这允许每个视口对同一分割有不同的表示。 - 为什么:这种简化使得在不同视口中管理和引用分割表示更加容易。
- 以前:需要一个唯一的
-
数据与可视化的解耦:
- 以前:分割渲染与工具组紧密耦合。
- 现在:分割现在纯粹作为数据处理,与用于交互的工具分离。
- 为什么:虽然将工具绑定到工具组是合适的,但像分割渲染这样的视口特定功能应该由各个视口负责。这种分离允许在不同视口中有更灵活的渲染和交互选项。
-
多态分割支持:
- 新架构更好地支持多态分割的概念,即单个分割可以有多个表示(例如,标签图、轮廓、表面),并且可以在它们之间高效地转换。
- 为什么:这种灵活性允许更高效地存储、分析和实时可视化分割。
-
跨表示类型的一致 API:
- 新的 API 提供了一种统一的方式来处理不同的分割表示,使得管理涉及多个视口和表示类型的复杂场景更加容易。
- 为什么:这种一致性简化了开发,并减少了在处理不同分割类型时出错的可能性。
这些架构更改为处理分割提供了更坚实的基础,特别是在复杂的多视口场景中。新方法已被证明非常有效,并为未来的增强功能打开了可能性。虽然核心概念保持相似,但您在代码中与分割交互的方式将会显著改变。本迁移指南将引导您完成这些更改,提供前后示例,帮助您将现有代码库更新到新架构。
分割状态
Segmentation 类型已被重组,以更好地组织分割信息和表示数据。在讨论迁移指南之前,让我们先看看更改。
- 之前 📦
- 之后 🚀🚀
type Segmentation = {
segmentationId: string;
type: Enums.SegmentationRepresentations;
label: string;
activeSegmentIndex: number;
segmentsLocked: Set<number>;
cachedStats: { [key: string]: number };
segmentLabels: { [key: string]: string };
representationData: SegmentationRepresentationData;
};
type Segmentation = {
segmentationId: string;
label: string;
segments: {
[segmentIndex: number]: Segment;
};
representationData: RepresentationsData;
};
type Segment = {
segmentIndex: number;
label: string;
locked: boolean;
cachedStats: { [key: string]: unknown };
active: boolean;
};
新的分割状态模型提供了更有组织的数据结构。以前分散的信息,如 cachedStats、segmentLabels 和 activeSegmentIndex,已被整合到 segments 属性下。这种重组增强了清晰度和效率。在接下来的部分中,我们将讨论迁移指南,解释如何在新结构中访问和修改这些属性。这种重组主要影响分割存储级别。
表示数据键
SegmentationRepresentations 枚举已更新为使用标题大小写而不是全大写,以使其与其他枚举保持一致。
- 之前 📦
- 之后 🚀🚀
enum SegmentationRepresentations {
Labelmap = 'LABELMAP',
Contour = 'CONTOUR',
Surface = 'SURFACE',
}
enum SegmentationRepresentations {
Labelmap = 'Labelmap',
Contour = 'Contour',
Surface = 'Surface',
}
triggerAnnotationRenderForViewportIds
现在只需要 viewportIds,不再需要 renderingEngine。
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIds) ---> triggerAnnotationRenderForViewportIds(viewportIds)
Details
为什么?
因为每个视口都有一个渲染引擎,因此不需要将渲染引擎作为参数传递。工具
StackScrollMouseWheelTool -> StackScrollTool
我们已经将鼠标滚轮与工具本身解耦,使其可以像其他鼠标绑定一样应用为绑定。
此更改带来了多个优势:
- 它可以与其他鼠标绑定组合使用
- 它可以与键盘绑定配对使用
- 之前 📦
- 之后 🚀🚀
cornerstoneTools.addTool(StackScrollMouseWheelTool);
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
cornerstoneTools.addTool(StackScrollTool);
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel,
},
],
});
BaseTool
getTargetVolumeId 方法已被移除,取而代之的是 getTargetId,而 getTargetIdImage 已重命名为 getTargetImageData,以更清楚地表明它是图像数据。
使用示例
- 之前 📦
- 之后 🚀
const volumeId = this.getTargetVolumeId(viewport);
const imageData = this.getTargetIdImage(targetId, renderingEngine);
const imageData = this.getTargetImageData(targetId);
新的分割模型
我们有一个新的分割模型,更加灵活且易于使用。
相同术语,不同架构
在 Cornerstone3D 版本 2 中,我们对分割模型进行了重大架构更改,同时保持了熟悉的术语。此重新设计旨在为在不同视口中处理分割提供更灵活和直观的方法。以下是主要更改及其背后的原因:
-
视口特定,而非基于工具组:
- 以前:分割与工具组绑定,工具组通常由多个视口组成。当用户希望在同一工具组内为某些视口添加分割而不是其他视口时,这会带来复杂性。
- 现在:分割现在是视口特定的。用户可以直接向视口添加分割,而不是向工具组添加或移除表示。这为每个视口渲染的内容提供了更细致的控制。
- 为什么:我们发现将渲染绑定到工具组并不是一种有效的方法。它通常需要为特定视口创建额外的工具组以进行自定义或防止渲染。
-
简化分割表示的识别:
- 以前:需要一个唯一的
segmentationRepresentationUID进行识别。 - 现在:分割表示通过
segmentationId和表示type的组合进行识别。这允许每个视口对同一分割有不同的表示。 - 为什么:这种简化使得在不同视口中管理和引用分割表示更加容易。
- 以前:需要一个唯一的
-
数据与可视化的解耦:
- 以前: 分割渲染与工具组紧密耦合。
- 现在:分割现在纯粹作为数据处理,与用于交互的工具分离。
- 为什么:虽然将工具绑定到工具组是合适的,但像分割渲染这样的视口特定功能应该由各个视口负责。这种分离允许在不同视口中有更灵活的渲染和交互选项。
-
多态分割支持:
- 新架构更好地支持多态分割的概念,即单个分割可以有多个表示(例如,标签图、轮廓、表面),并且可以在它们之间高效地转换。
- 为什么:这种灵活性允许更高效地存储、分析和实时可视化分割。
-
跨表示类型的一致 API:
- 新的 API 提供了一种统一的方式来处理不同的分割表示,使得管理涉及多个视口和表示类型的复杂场景更加容易。
- 为什么:这种一致性简化了开发,并减少了在处理不同分割类型时出错的可能性。
这些架构更改为处理分割提供了更坚实的基础,特别是在复杂的多视口场景中。新方法已被证明非常有效,并为未来的增强功能打开了可能性。虽然核心概念保持相似,但您在代码中与分割交互的方式将会显著改变。本迁移指南将引导您完成这些更改,提供前后示例,帮助您将现有代码库更新到新架构。
分割状态
Segmentation 类型已被重组,以更好地组织分割信息和表示数据。在讨论迁移指南之前,让我们先看看更改。
- 之前 📦
- 之后 🚀🚀
type Segmentation = {
segmentationId: string;
type: Enums.SegmentationRepresentations;
label: string;
activeSegmentIndex: number;
segmentsLocked: Set<number>;
cachedStats: { [key: string]: number };
segmentLabels: { [key: string]: string };
representationData: SegmentationRepresentationData;
};
type Segmentation = {
segmentationId: string;
label: string;
segments: {
[segmentIndex: number]: Segment;
};
representationData: RepresentationsData;
};
type Segment = {
segmentIndex: number;
label: string;
locked: boolean;
cachedStats: { [key: string]: unknown };
active: boolean;
};
新的分割状态模型提供了更有组织的数据结构。以前分散的信息,如 cachedStats、segmentLabels 和 activeSegmentIndex,已被整合到 segments 属性下。这种重组增强了清晰度和效率。在接下来的部分中,我们将讨论迁移指南,解释如何在新结构中访问和修改这些属性。这种重组主要影响分割存储级别。
表示数据键
SegmentationRepresentations 枚举已更新为使用标题大小写而不是全大写,以使其与其他枚举保持一致。
- 之前 📦
- 之后 🚀🚀
enum SegmentationRepresentations {
Labelmap = 'LABELMAP',
Contour = 'CONTOUR',
Surface = 'SURFACE',
}
enum SegmentationRepresentations {
Labelmap = 'Labelmap',
Contour = 'Contour',
Surface = 'Surface',
}
这项更改影响了表示数据的访问方式:
- 之前 📦
- 之后 🚀🚀
const representationData = segmentation.representationData.SURFACE;
const representationData = segmentation.representationData.LABELMAP;
const representationData = segmentation.representationData.CONTOUR;
const representationData = segmentation.representationData.Surface;
const representationData = segmentation.representationData.Labelmap;
const representationData = segmentation.representationData.Contour;
分割表示
表示结构已被简化,现在是视口特定的。
- 之前 📦
- 之后 🚀🚀
type ToolGroupSpecificRepresentation =
| ToolGroupSpecificLabelmapRepresentation
| ToolGroupSpecificContourRepresentation;
type ToolGroupSpecificRepresentationState = {
segmentationRepresentationUID: string;
segmentationId: string;
type: Enums.SegmentationRepresentations;
active: boolean;
segmentsLocked: Set<number>;
colorLUTIndex: number;
};
type SegmentationState = {
toolGroups: {
[key: string]: {
segmentationRepresentations: ToolGroupSpecificRepresentations;
config: SegmentationRepresentationConfig;
};
};
};
type SegmentationRepresentation =
| LabelmapRepresentation
| ContourRepresentation
| SurfaceRepresentation;
type BaseSegmentationRepresentation = {
colorLUTIndex: number;
segmentationId: string;
type: Enums.SegmentationRepresentations;
visible: boolean;
active: boolean;
segments: {
[segmentIndex: number]: {
visible: boolean;
};
};
};
type SegmentationState = {
viewportSegRepresentations: {
[viewportId: string]: Array<SegmentationRepresentation>;
};
};
以前,分割表示是基于工具组的,这导致了一些问题。在新的结构中,分割表示是视口特定的。它现在由 segmentationId、type 以及该分割的各种设置组成。由于这一变化,几个函数被移除或修改。以下是更改的总结:
移除的函数
getDefaultSegmentationStateManagergetSegmentationRepresentationsgetAllSegmentationRepresentationsgetSegmentationIdRepresentationsfindSegmentationRepresentationByUIDgetToolGroupIdsWithSegmentationgetToolGroupSpecificConfigsetToolGroupSpecificConfiggetGlobalConfigsetGlobalConfigsetSegmentationRepresentationSpecificConfiggetSegmentationRepresentationSpecificConfiggetSegmentSpecificRepresentationConfigsetSegmentSpecificRepresentationConfiggetToolGroupIdFromSegmentationRepresentationUIDaddSegmentationRepresentationgetSegmentationRepresentationByUID
新的函数
addSegmentations(segmentationInputArray)removeSegmentation(segmentationId)getSegmentation(segmentationId)getSegmentations()getSegmentationRepresentation(viewportId, specifier)getSegmentationRepresentations(viewportId, specifier)removeSegmentationRepresentation(viewportId, specifier, immediate)removeAllSegmentationRepresentations()removeLabelmapRepresentation(viewportId, segmentationId, immediate)removeContourRepresentation(viewportId, segmentationId, immediate)removeSurfaceRepresentation(viewportId, segmentationId, immediate)getViewportSegmentations(viewportId, type)getViewportIdsWithSegmentation(segmentationId)getCurrentLabelmapImageIdForViewport(viewportId, segmentationId)updateLabelmapSegmentationImageReferences(segmentationId, imageIds)getStackSegmentationImageIdsForViewport(viewportId, segmentationId)destroy()
移除 SegmentationDisplayTool
不再需要将 SegmentationDisplayTool 添加到 toolGroup。
之前
toolGroup2.addTool(SegmentationDisplayTool.toolName);
toolGroup1.setToolEnabled(SegmentationDisplayTool.toolName);
现在
// 无需任何操作
堆栈标签图
要创建堆栈标签图,您不再需要手动在标签图 imageIds 和视口 imageIds 之间创建引用。我们现在为您自 动处理此过程。
这需要一个长篇的为什么...
以前的模型要求用户提供一个 imageIdReferenceMap,将标签图 imageIds 链接到视口 imageIds。这种方法在实现高级分割用例时带来了几个挑战:
-
手动创建映射容易出错,特别是在 imageIds 的顺序方面。
-
一旦分割与特定的视口 imageIds 相关联,就很难在其他地方渲染。例如:
- 在单个关键图像上渲染 CT 图像堆栈分割。
- 在包含 CT 和其他图像的堆栈上渲染 CT 图像堆栈分割。
- 在能量 1 上渲染 DX 双能分割到能量 2。
- 在同一空间的 PT 标签图上从堆栈视口渲染 CT 标签图。
这些场景突显了以前模型的局限性。
我们现在已经过渡到一个系统,用户只需提供 imageIds。在渲染过程中,我们将视口的当前 imageId 与标签图 imageIds 进行匹配,如果有匹配项,则渲染分割。这个匹配过程发生在 SegmentationStateManager 中,条件是分割必须与引用的视口处于同一平面。
这种新方法启用了许多额外的用例,并为分割渲染提供了更大的灵活性。
- 之前 📦
- 之后 🚀🚀
segmentation.addSegmentations([
{
segmentationId,
representation: {
type: csToolsEnums.SegmentationRepresentations.Labelmap,
data: {
imageIdReferenceMap:
cornerstoneTools.utilities.segmentation.createImageIdReferenceMap(
imageIds,
segmentationImageIds
),
},
},
},
]);
segmentation.addSegmentations([
{
segmentationId,
representation: {
type: csToolsEnums.SegmentationRepresentations.Labelmap,
data: {
imageIds: segmentationImageIds,
},
},
},
]);
triggerAnnotationRenderForViewportIds
现在只需要 viewportIds,不再需要 renderingEngine。
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIds) ---> triggerAnnotationRenderForViewportIds(viewportIds)
Details
为什么?
因为每个视口都有一个渲染引擎,因此不需要将渲染引擎作为参数传递。工具
StackScrollMouseWheelTool -> StackScrollTool
我们已经将鼠标滚轮与工具本身解耦,使其可以像其他鼠标绑定一样应用为绑定。
此更改带来了多个优势:
- 它可以与其他鼠标绑定组合使用
- 它可以与键盘绑定配对使用
- 之前 📦
- 之后 🚀🚀
cornerstoneTools.addTool(StackScrollMouseWheelTool);
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
cornerstoneTools.addTool(StackScrollTool);
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel,
},
],
});
BaseTool
getTargetVolumeId 方法已被移除,取而代之的是 getTargetId,而 getTargetIdImage 已重命名为 getTargetImageData,以更清楚地表明它是图像数据。
使用示例
- 之前 📦
- 之后 🚀
const volumeId = this.getTargetVolumeId(viewport);
const imageData = this.getTargetIdImage(targetId, renderingEngine);
const imageData = this.getTargetImageData(targetId);
新的分割模型
我们有一个新的分割模型,更加灵活且易于使用。
相同术语,不同架构
在 Cornerstone3D 版本 2 中,我们对分割模型进行了重大架构更改,同时保持了熟悉的术语。此重新设计旨在为在不同视口中处理分割提供更灵活和直观的方法。以下是主要更改及其背后的原因:
-
视口特定,而非基于工具组:
- 以前:分割与工具组绑定,工具组通常由多个视口组成。当用户希望在同一工具组内为某些视口添加分割而不是其他视口时,这会带来复杂性。
- 现在:分割现在是视口特定的。用户可以直接向视口添加分割,而不是向工具组添加或移除表示。这为每个视口渲染的内容提供了更细致的控制。
- 为什么:我们发现将渲染绑定到工具组并不是一种有效的方法。它通常需要为特定视口创建额外的工具组以进行自定义或防止渲染。
-
简化分割表示的识别:
- 以前:需要一个唯一的
segmentationRepresentationUID进行识别。 - 现在:分割表示通过
segmentationId和表示type的组合进行识别。这允许每个视口对同一分割有不同的表示。 - 为什么:这种简化使得在不同视口中管理和引用分割表示更加容易。
- 以前:需要一个唯一的
-
数据与可视化的解耦:
- 以前:分割渲染与工具组紧密耦合。
- 现在:分割现在纯粹作为数据处理,与用于交互的工具分离。
- 为什么:虽然将工具绑定到工具组是合适的,但像分割渲染这样的视口特定功能应该由各个视口负责。这种分离允许在不同视口中有更灵活的渲染和交互选项。
-
多态分割支持:
- 新架构更好地支持多态分割的概念,即单个分割可以有多个表示(例如,标签图、轮廓、表面),并且可以在它们之间高效地转换。
- 为什么:这种灵活性允许更高效地存储、分析和实时可视化分割。
-
跨表示类型的一致 API:
- 新的 API 提供了一种统一的方式来处理不同的分割表示,使得管理涉及多个视口和表示类型的复杂场景更加容易。
- 为什么:这种一致性简化了开发,并减少了在处理不同分割类型时出错的可能性。
这些架构更改为处理分割提供了更坚实的基础,特别是在复杂的多视口场景中。新方法已被证明非常有效,并为未来的增强功能打开了可能性。虽然核心概念保持相似,但您在代码中与分割交互的方式将会显著改变。本迁移指南将引导您完成这些更改,提供前后示例,帮助您将现有代码库更新到新架构。
分割状态
Segmentation 类型已被重组,以更好地组织分割信息和表示数据。在讨论迁移指南之前,让我们先看看更改。
- 之前 📦
- 之后 🚀🚀
type Segmentation = {
segmentationId: string;
type: Enums.SegmentationRepresentations;
label: string;
activeSegmentIndex: number;
segmentsLocked: Set<number>;
cachedStats: { [key: string]: number };
segmentLabels: { [key: string]: string };
representationData: SegmentationRepresentationData;
};
type Segmentation = {
segmentationId: string;
label: string;
segments: {
[segmentIndex: number]: Segment;
};
representationData: RepresentationsData;
};
type Segment = {
segmentIndex: number;
label: string;
locked: boolean;
cachedStats: { [key: string]: unknown };
active: boolean;
};
新的分割状态模型提供了更有组织的数据结构。以前分散的信息,如 cachedStats、segmentLabels 和 activeSegmentIndex,已被整合到 segments 属性下。这种重组增强了清晰度和效率。在接下来的部分中,我们将讨论迁移指南,解释如何在新结构中访问和修改这些属性。这种重组主要影响分割存储级别。
表示数据键
SegmentationRepresentations 枚举已更新为使用标题大小写而不是全大写,以使其与其他枚举保持一致。
- 之前 📦
- 之后 🚀🚀
enum SegmentationRepresentations {
Labelmap = 'LABELMAP',
Contour = 'CONTOUR',
Surface = 'SURFACE',
}
enum SegmentationRepresentations {
Labelmap = 'Labelmap',
Contour = 'Contour',
Surface = 'Surface',
}
这项更改影响了表示数据的访问方式:
- 之前 📦
- 之后 🚀🚀
const representationData = segmentation.representationData.SURFACE;
const representationData = segmentation.representationData.LABELMAP;
const representationData = segmentation.representationData.CONTOUR;
const representationData = segmentation.representationData.Surface;
const representationData = segmentation.representationData.Labelmap;
const representationData = segmentation.representationData.Contour;
分割表示
表示结构已被简化,现在是视口特定的。
- 之前 📦
- 之后 🚀🚀
type ToolGroupSpecificRepresentation =
| ToolGroupSpecificLabelmapRepresentation
| ToolGroupSpecificContourRepresentation;
type ToolGroupSpecificRepresentationState = {
segmentationRepresentationUID: string;
segmentationId: string;
type: Enums.SegmentationRepresentations;
active: boolean;
segmentsLocked: Set<number>;
colorLUTIndex: number;
};
type SegmentationState = {
toolGroups: {
[key: string]: {
segmentationRepresentations: ToolGroupSpecificRepresentations;
config: SegmentationRepresentationConfig;
};
};
};
type SegmentationRepresentation =
| LabelmapRepresentation
| ContourRepresentation
| SurfaceRepresentation;
type BaseSegmentationRepresentation = {
colorLUTIndex: number;
segmentationId: string;
type: Enums.SegmentationRepresentations;
visible: boolean;
active: boolean;
segments: {
[segmentIndex: number]: {
visible: boolean;
};
};
};
type SegmentationState = {
viewportSegRepresentations: {
[viewportId: string]: Array<SegmentationRepresentation>;
};
};
以前,分割表示是基于工具组的,这导致了一些问题。在新的结构中,分割表示是视口特定的。它现在由 segmentationId、type 以及该分割的各种设置组成。由于这一变化,几个函数被移除或修改。以下是更改的总结:
移除的函数
getDefaultSegmentationStateManagergetSegmentationRepresentationsgetAllSegmentationRepresentationsgetSegmentationIdRepresentationsfindSegmentationRepresentationByUIDgetToolGroupIdsWithSegmentationgetToolGroupSpecificConfigsetToolGroupSpecificConfiggetGlobalConfigsetGlobalConfigsetSegmentationRepresentationSpecificConfiggetSegmentationRepresentationSpecificConfiggetSegmentSpecificRepresentationConfigsetSegmentSpecificRepresentationConfiggetToolGroupIdFromSegmentationRepresentationUIDaddSegmentationRepresentationgetSegmentationRepresentationByUID
新的函数
addSegmentations(segmentationInputArray)removeSegmentation(segmentationId)getSegmentation(segmentationId)getSegmentations()getSegmentationRepresentation(viewportId, specifier)getSegmentationRepresentations(viewportId, specifier)removeSegmentationRepresentation(viewportId, specifier, immediate)removeAllSegmentationRepresentations()removeLabelmapRepresentation(viewportId, segmentationId, immediate)removeContourRepresentation(viewportId, segmentationId, immediate)removeSurfaceRepresentation(viewportId, segmentationId, immediate)getViewportSegmentations(viewportId, type)getViewportIdsWithSegmentation(segmentationId)getCurrentLabelmapImageIdForViewport(viewportId, segmentationId)updateLabelmapSegmentationImageReferences(segmentationId, imageIds)getStackSegmentationImageIdsForViewport(viewportId, segmentationId)destroy()
移除 SegmentationDisplayTool
不再需要将 SegmentationDisplayTool 添加到 toolGroup。
之前
toolGroup2.addTool(SegmentationDisplayTool.toolName);
toolGroup1.setToolEnabled(SegmentationDisplayTool.toolName);
现在
// 无需任何操作
堆栈标签图
要创建堆栈标签图,您不再需要手动在标签图 imageIds 和视口 imageIds 之间创建引用。我们现在为您自动处理此过程。
这需要一个长篇的为什么...
以前的模型要求用户提供一个 imageIdReferenceMap,将标签图 imageIds 链接到视口 imageIds。这种方法在实现高级分割用例时带来了几个挑战:
-
手动创建映射容易出错,特别是在 imageIds 的顺序方面。
-
一旦分割与特定的视口 imageIds 相关联,就很难在其他地方渲染。例如:
- 在单个关键图像上渲染 CT 图像堆栈分割。
- 在包含 CT 和其他图像的堆栈上渲染 CT 图像堆栈分割。
- 在能量 1 上渲染 DX 双能分割到能量 2。
- 在同一空间的 PT 标签图上从堆栈视口渲染 CT 标签图。
这些场景突显了以前模型的局限性。
我们现在已经过渡到一个系统,用户只需提供 imageIds。在渲染过程中,我们将视口的当前 imageId 与标签图 imageIds 进行匹配,如果有匹配项,则渲染分割。这个匹配过程发生在 SegmentationStateManager 中,条件是分割必须与引用的视口处于同一平面。
这种新方法启用了许多额外的用例,并为分割渲染提供了更大的灵活性。
- 之前 📦
- 之后 🚀🚀
segmentation.addSegmentations([
{
segmentationId,
representation: {
type: csToolsEnums.SegmentationRepresentations.Labelmap,
data: {
imageIdReferenceMap:
cornerstoneTools.utilities.segmentation.createImageIdReferenceMap(
imageIds,
segmentationImageIds
),
},
},
},
]);
// 在这里填写“之后”部分的代码
triggerAnnotationRenderForViewportIds
现在只需要 viewportIds,不再需要 renderingEngine。
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIds) ---> triggerAnnotationRenderForViewportIds(viewportIds)
Details
为什么?
因为每个视口都有一个渲染引擎,因此不需要将渲染引擎作为参数传递。工具
StackScrollMouseWheelTool -> StackScrollTool
我们已经将鼠标滚轮与工具本身解耦,使其可以像其他鼠标绑定一样应用为绑定。
此更改带来了多个优势:
- 它可以与其他鼠标绑定组合使用
- 它可以与键盘绑定配对使用
- 之前 📦
- 之后 🚀🚀
cornerstoneTools.addTool(StackScrollMouseWheelTool);
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
cornerstoneTools.addTool(StackScrollTool);
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel,
},
],
});
BaseTool
getTargetVolumeId 方法已被移除,取而代之的是 getTargetId,而 getTargetIdImage 已重命名为 getTargetImageData,以更清楚地表明它是图像数据。
使用示例
- 之前 📦
- 之后 🚀
const volumeId = this.getTargetVolumeId(viewport);
const imageData = this.getTargetIdImage(targetId, renderingEngine);
const imageData = this.getTargetImageData(targetId);
新的分割模型
我们有一个新的分割模型,更加灵活且易于使用。
相同术语,不同架构
在 Cornerstone3D 版本 2 中,我们对分割模型进行了重大架构更改,同时保持了熟悉的术语。此重新设计旨在为在不同视口中处理分割提供更灵活和直观的方法。以下是主要更改及其背后的原因:
-
视口特定,而非基于工具组:
- 以前:分割与工具组绑定,工具组通常由多个视口组成。当用户希望在同一工具组内为某些视口添加分割而不是其他视口时,这会带来复杂性。
- 现在:分割现在是视口特定的。用户可以直接向视口添加分割,而不是向工具组添加或移除表示。这为每个视口渲染的内容提供了更细致的控制。
- 为什么:我们发现将渲染绑定到工具组并不是一种有效的方法。它通常需要为特定视口创建额外的工具组以进行自定义或防止渲染。
-
简化分割 表示的识别:
- 以前:需要一个唯一的
segmentationRepresentationUID进行识别。 - 现在:分割表示通过
segmentationId和表示type的组合进行识别。这允许每个视口对同一分割有不同的表示。 - 为什么:这种简化使得在不同视口中管理和引用分割表示更加容易。
- 以前:需要一个唯一的
-
数据与可视化的解耦:
- 以前:分割渲染与工具组紧密耦合。
- 现在:分割现在纯粹作为数据处理,与用于交互的工具分离。
- 为什么:虽然将工具绑定到工具组是合适的,但像分割渲染这样的视口特定功能应该由各个视口负责。这种分离允许在不同视口中有更灵活的渲染和交互选项。
-
多态分割支持:
- 新架构更好地支持多态分割的概念,即单个分割可以有多个表示(例如,标签图、轮廓、表面),并且可以在它们之间高效地转换。
- 为什么:这种灵活性允许更高效地存储、分析和实时可视化分割。
-
跨表示类型的一致 API:
- 新的 API 提供了一种统一的方式来处理不同的分割表示,使得管理涉及多个视口和表示类型的复杂场景更加容易。
- 为什么:这种一致性简化了开发,并减少了在处理不同分割类型时出错的可能性。
这些架构更改为处理分割提供了更坚实的基础,特别是在复杂的多视口场景中。新方法已被证明非常有效,并为未来的增强功能打开了可能性。虽然核心概念保持相似,但您在代码中与分割交互的方式将会显著改变。本迁移指南将引导您完成这些更改,提供前后示例,帮助您将现有代码库更新到新架构。
分割状态
Segmentation 类型已被重组,以更好地组织分割信息和表示数据。在讨论迁移指南之前,让我们先看看更改。
- 之前 📦
- 之后 🚀🚀
type Segmentation = {
segmentationId: string;
type: Enums.SegmentationRepresentations;
label: string;
activeSegmentIndex: number;
segmentsLocked: Set<number>;
cachedStats: { [key: string]: number };
segmentLabels: { [key: string]: string };
representationData: SegmentationRepresentationData;
};
type Segmentation = {
segmentationId: string;
label: string;
segments: {
[segmentIndex: number]: Segment;
};
representationData: RepresentationsData;
};
type Segment = {
segmentIndex: number;
label: string;
locked: boolean;
cachedStats: { [key: string]: unknown };
active: boolean;
};
新的分割状态模型提供了更有组织的数据结构。以前分散的信息,如 cachedStats、segmentLabels 和 activeSegmentIndex,已被整合到 segments 属性下。这种重组增强了清晰度和效率。在接下来的部分中,我们将讨论迁移指南,解释如何在新结构中访问和修改这些属性。这种重组主要影响分割存储级别。
表示数据键
SegmentationRepresentations 枚举已更新为使用标题大小写而不是全大写,以使其与其他枚举保持一致。
- 之前 📦
- 之后 🚀🚀
enum SegmentationRepresentations {
Labelmap = 'LABELMAP',
Contour = 'CONTOUR',
Surface = 'SURFACE',
}
enum SegmentationRepresentations {
Labelmap = 'Labelmap',
Contour = 'Contour',
Surface = 'Surface',
}
这项更改影响了表示数据的访问方式:
- 之前 📦
- 之后 🚀🚀
const representationData = segmentation.representationData.SURFACE;
const representationData = segmentation.representationData.LABELMAP;
const representationData = segmentation.representationData.CONTOUR;
const representationData = segmentation.representationData.Surface;
const representationData = segmentation.representationData.Labelmap;
const representationData = segmentation.representationData.Contour;
分割表示
表示结构已被简化,现在是视口特定的。
- 之前 📦
- 之后 🚀🚀
type ToolGroupSpecificRepresentation =
| ToolGroupSpecificLabelmapRepresentation
| ToolGroupSpecificContourRepresentation;
type ToolGroupSpecificRepresentationState = {
segmentationRepresentationUID: string;
segmentationId: string;
type: Enums.SegmentationRepresentations;
active: boolean;
segmentsLocked: Set<number>;
colorLUTIndex: number;
};
type SegmentationState = {
toolGroups: {
[key: string]: {
segmentationRepresentations: ToolGroupSpecificRepresentations;
config: SegmentationRepresentationConfig;
};
};
};
type SegmentationRepresentation =
| LabelmapRepresentation
| ContourRepresentation
| SurfaceRepresentation;
type BaseSegmentationRepresentation = {
colorLUTIndex: number;
segmentationId: string;
type: Enums.SegmentationRepresentations;
visible: boolean;
active: boolean;
segments: {
[segmentIndex: number]: {
visible: boolean;
};
};
};
type SegmentationState = {
viewportSegRepresentations: {
[viewportId: string]: Array<SegmentationRepresentation>;
};
};
以前,分割表示是基于工具组的,这导致了一些问题。在新的结构中,分割表示是视口特定的。它现在由 segmentationId、type 以及该分割的各种设置组成。由于这一变化,几个函数被移除或修改。以下是更改的总结:
移除的 函数
getDefaultSegmentationStateManagergetSegmentationRepresentationsgetAllSegmentationRepresentationsgetSegmentationIdRepresentationsfindSegmentationRepresentationByUIDgetToolGroupIdsWithSegmentationgetToolGroupSpecificConfigsetToolGroupSpecificConfiggetGlobalConfigsetGlobalConfigsetSegmentationRepresentationSpecificConfiggetSegmentationRepresentationSpecificConfiggetSegmentSpecificRepresentationConfigsetSegmentSpecificRepresentationConfiggetToolGroupIdFromSegmentationRepresentationUIDaddSegmentationRepresentationgetSegmentationRepresentationByUID
新的函数
addSegmentations(segmentationInputArray)removeSegmentation(segmentationId)getSegmentation(segmentationId)getSegmentations()getSegmentationRepresentation(viewportId, specifier)getSegmentationRepresentations(viewportId, specifier)removeSegmentationRepresentation(viewportId, specifier, immediate)removeAllSegmentationRepresentations()removeLabelmapRepresentation(viewportId, segmentationId, immediate)removeContourRepresentation(viewportId, segmentationId, immediate)removeSurfaceRepresentation(viewportId, segmentationId, immediate)getViewportSegmentations(viewportId, type)getViewportIdsWithSegmentation(segmentationId)getCurrentLabelmapImageIdForViewport(viewportId, segmentationId)updateLabelmapSegmentationImageReferences(segmentationId, imageIds)getStackSegmentationImageIdsForViewport(viewportId, segmentationId)destroy()
移除 SegmentationDisplayTool
不再需要将 SegmentationDisplayTool 添加到 toolGroup。
之前
toolGroup2.addTool(SegmentationDisplayTool.toolName);
toolGroup1.setToolEnabled(SegmentationDisplayTool.toolName);
现在
// 无需任何操作
堆栈标签图
要创建堆栈标签图,您不再需要手动在标签图 imageIds 和视口 imageIds 之间创建引用。我们现在为您自动处理此过程。
这需要一个长篇的为什么...
以前的模型要求用户提供一个 imageIdReferenceMap,将标签图 imageIds 链接到视口 imageIds。这种方法在实现高级分割用例时带来了几个挑战:
-
手动创建映射容易出错,特别是在 imageIds 的顺序方面。
-
一旦分割与特定的视口 imageIds 相关联,就很难在其他地方渲染。例如:
- 在单个关键图像上渲染 CT 图像堆栈分割。
- 在包含 CT 和其他图像的堆栈上渲染 CT 图像堆栈分割。
- 在能量 1 上渲染 DX 双能分割到能量 2。
- 在同一空间的 PT 标签图上从堆栈视口渲染 CT 标签图。
这些场景突显了以前模型的局限性。
我们现在已经过渡到一个系统,用户只需提供 imageIds。在渲染过程中,我们将视口的当前 imageId 与标签图 imageIds 进行匹配,如果有匹配项,则渲染分割。这个匹配过程发生在 SegmentationStateManager 中,条件是分割必须与引用的视口处于同一平面。
这种新方法启用了许多额外的用例,并为分割渲染提供了更大的灵活性。
- 之前 📦
- 之后 🚀🚀
segmentation.addSegmentations([
{
segmentationId,
representation: {
type: csToolsEnums.SegmentationRepresentations.Labelmap,
data: {
imageIdReferenceMap:
cornerstoneTools.utilities.segmentation.createImageIdReferenceMap(
imageIds,
segmentationImageIds
),
},
},
},
]);
// 在这里填写“之后”部分的代码
迁移步骤:
- 将通用的
addSegmentationRepresentations调用替换为适当的特定表示函数。 - 更新输入数组以匹配新的
RepresentationPublicInput类型。 - 从代码中移除任何特定类型的逻辑,因为现在这些逻辑由这些新函数处理。
多视口函数
版本 2 引入了新的函数,用于同时向多个视口添加分割表示。
- 之前 📦
- 之后 🚀🚀
// 版本 1 中没有等效的函数
function addContourRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
function addLabelmapRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
function addSurfaceRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
迁移步骤:
- 如果您之前向多个工具组添加表示,请重构代码以使用这些新的多视口函数。
- 创建一个
viewportInputMap对象,将视口 ID 作为键,RepresentationPublicInput数组作为值。 - 根据表示类型调用适当的多视口函数。
事件
由于我们从工具组转向视口,许多事件已被重命名,以包含 viewportId 而不是 toolGroupId,并且
一些事件详情已更改为包含 segmentationId 而不是 segmentationRepresentationUID 或 toolGroupId。
移除工具组特定事件
triggerSegmentationRepresentationModified 和 triggerSegmentationRepresentationRemoved 函数已被移除。取而代之的是,库现在使用更通用的方法来处理分割事件。
- 之前 📦
- 之后 🚀🚀
function triggerSegmentationRepresentationModified(
toolGroupId: string,
segmentationRepresentationUID?: string
): void {
// ...
}
function triggerSegmentationRepresentationRemoved(
toolGroupId: string,
segmentationRepresentationUID: string
): void {
// ...
}
function triggerSegmentationRepresentationModified(
viewportId: string,
segmentationId: string,
type?: SegmentationRepresentations
): void {
// ...
}
function triggerSegmentationRepresentationRemoved(
viewportId: string,
segmentationId: string,
type: SegmentationRepresentations
): void {
// ...
}
迁移步骤:
- 在函数调用中将
toolGroupId替换为viewportId。 - 将
segmentationRepresentationUID替换为segmentationId。 - 添加
type参数以指定分割表示类型。
简化的分割修改事件
triggerSegmentationModified 函数已简化,始终需要一个 segmentationId。
- 之前 📦
- 之后 🚀🚀
function triggerSegmentationModified(segmentationId?: string): void {
// ...
}
function triggerSegmentationModified(segmentationId: string): void {
// ...
}
迁移步骤:
- 确保在调用
triggerSegmentationModified时始终提供segmentationId。 - 移除任何处理
segmentationId未定义情况的逻辑。
更新的事件详情类型
几个事件详情类型已更新,以反映分割系统中的更改:
- 之前 📦
- 之后 🚀🚀
type SegmentationRepresentationModifiedEventDetail = {
toolGroupId: string;
segmentationRepresentationUID: string;
};
type SegmentationRepresentationRemovedEventDetail = {
toolGroupId: string;
segmentationRepresentationUID: string;
};
type SegmentationRenderedEventDetail = {
viewportId: string;
toolGroupId: string;
};
type SegmentationRepresentationModifiedEventDetail = {
segmentationId: string;
type: string;
viewportId: string;
};
type SegmentationRepresentationRemovedEventDetail = {
segmentationId: string;
type: string;
viewportId: string;
};
type SegmentationRenderedEventDetail = {
viewportId: string;
segmentationId: string;
type: string;
};
triggerAnnotationRenderForViewportIds
现在只需要 viewportIds,不再需要 renderingEngine。
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIds) ---> triggerAnnotationRenderForViewportIds(viewportIds)
Details
为什么?
因为每个视口都有一个渲染引擎,因此不需要将渲染引擎作为参数传递。工具
StackScrollMouseWheelTool -> StackScrollTool
我们已经将鼠标滚轮与工具本身解耦,使其可以像其他鼠标绑定一样应用为绑定。
此更改带来了多个优势:
- 它可以与其他鼠标绑定组合使用
- 它可以与键盘绑定配对使用
- 之前 📦
- 之后 🚀🚀
cornerstoneTools.addTool(StackScrollMouseWheelTool);
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
cornerstoneTools.addTool(StackScrollTool);
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel,
},
],
});
BaseTool
getTargetVolumeId 方法已被移除,取而代之的是 getTargetId,而 getTargetIdImage 已重命名为 getTargetImageData,以更清楚地表明它是图像数据。
使用示例
- 之前 📦
- 之后 🚀
const volumeId = this.getTargetVolumeId(viewport);
const imageData = this.getTargetIdImage(targetId, renderingEngine);
const imageData = this.getTargetImageData(targetId);
新的分割模型
我们有一个新的分割模型,更加灵活且易于使用。
相同术语,不同架构
在 Cornerstone3D 版本 2 中,我们对分割模型进行了重大架构更改,同时保持了熟悉的术语。此重新设计旨在为在不同视口中处理分割提供更灵活和直观的方法。以下是主要更改及其背后的原因:
-
视口特定,而非基于工具组:
- 以前:分割与工具组绑定,工具组通常由多个视口组成。当用户希望在同一工具组内为某些视口添加分割而不是其他视口时,这会带来复杂性。
- 现在:分割现在是视口特定的。用户可以直接向视口添加分割,而不是向工具组添加或移除表示。这为每个视口渲染的内容提供了更细致的控制。
- 为什么:我们发现将渲染绑定到工具组并不是一种有效的方法。它通常需要为特定视口创建额外的工具组以进行自定义或防止渲染。
-
简化分割表示的识别:
- 以前:需要一个唯一的
segmentationRepresentationUID进行识别。 - 现在:分割表示通过
segmentationId和表示type的组合进行识别。这允许每个视口对同一分割有不同的表示。 - 为什么:这种简化使得在不同视口中管理和引用分割表示更加容易。
- 以前:需要一个唯一的
-
数据与可视化的解耦:
- 以前:分割渲染与工具组紧密耦合。
- 现在:分割现在纯粹作为数据处理,与用于交互的工具分离。
- 为什么:虽然将工具绑定到工具组是合适的,但像分割渲染这样的视口特定功能应该由各个视口负责。这种分离允许在不同视口中有更灵活的渲染和交互选项。
-
多态分割支持:
- 新架构更好地支持多态分割的概念,即单个分割可以有多个表示(例如,标签图、轮廓、表面),并且可以在它们之间高效地转换。
- 为什么:这种灵活性允许更高效地存储、分析和实时可视化分割。
-
跨表示类型的一致 API:
- 新的 API 提供了一种统一的方式来处理不同的分割表示,使得管理涉及多个视口和表示类型的复杂场景更加容易。
- 为什么:这种一致性简化了开发,并减少了在处理不同分割类型时出错的可能性。
这些架构更改为处理分割提供了更坚实的基础,特别是在复杂的多视口场景中。新方法已被证明非常有效,并为未来的增强功能打开了可能性。虽然核心概念保持相似,但您在代码中与分割交互的方式将会显 著改变。本迁移指南将引导您完成这些更改,提供前后示例,帮助您将现有代码库更新到新架构。
分割状态
Segmentation 类型已被重组,以更好地组织分割信息和表示数据。在讨论迁移指南之前,让我们先看看更改。
- 之前 📦
- 之后 🚀🚀
type Segmentation = {
segmentationId: string;
type: Enums.SegmentationRepresentations;
label: string;
activeSegmentIndex: number;
segmentsLocked: Set<number>;
cachedStats: { [key: string]: number };
segmentLabels: { [key: string]: string };
representationData: SegmentationRepresentationData;
};
type Segmentation = {
segmentationId: string;
label: string;
segments: {
[segmentIndex: number]: Segment;
};
representationData: RepresentationsData;
};
type Segment = {
segmentIndex: number;
label: string;
locked: boolean;
cachedStats: { [key: string]: unknown };
active: boolean;
};
新的分割状态模型提供了更有组织的数据结构。以前分散的信息,如 cachedStats、segmentLabels 和 activeSegmentIndex,已被整合到 segments 属性下。这种重组增强了清晰度和效率。在接下来的部分中,我们将讨论迁移指南,解释如何在新结构中访问和修改这些属性。这种重组主要影响分割存储级别。
表示数据键
SegmentationRepresentations 枚举已更新为使用标题大小写而不是全大写,以使其与其他枚举保持一致。
- 之前 📦
- 之后 🚀🚀
enum SegmentationRepresentations {
Labelmap = 'LABELMAP',
Contour = 'CONTOUR',
Surface = 'SURFACE',
}
enum SegmentationRepresentations {
Labelmap = 'Labelmap',
Contour = 'Contour',
Surface = 'Surface',
}
这项更改影响了表示数据的访问方式:
- 之前 📦
- 之后 🚀🚀
const representationData = segmentation.representationData.SURFACE;
const representationData = segmentation.representationData.LABELMAP;
const representationData = segmentation.representationData.CONTOUR;
const representationData = segmentation.representationData.Surface;
const representationData = segmentation.representationData.Labelmap;
const representationData = segmentation.representationData.Contour;
分割表示
表示结构已被简化,现在是视口特定的。
- 之前 📦
- 之后 🚀🚀
type ToolGroupSpecificRepresentation =
| ToolGroupSpecificLabelmapRepresentation
| ToolGroupSpecificContourRepresentation;
type ToolGroupSpecificRepresentationState = {
segmentationRepresentationUID: string;
segmentationId: string;
type: Enums.SegmentationRepresentations;
active: boolean;
segmentsLocked: Set<number>;
colorLUTIndex: number;
};
type SegmentationState = {
toolGroups: {
[key: string]: {
segmentationRepresentations: ToolGroupSpecificRepresentations;
config: SegmentationRepresentationConfig;
};
};
};
type SegmentationRepresentation =
| LabelmapRepresentation
| ContourRepresentation
| SurfaceRepresentation;
type BaseSegmentationRepresentation = {
colorLUTIndex: number;
segmentationId: string;
type: Enums.SegmentationRepresentations;
visible: boolean;
active: boolean;
segments: {
[segmentIndex: number]: {
visible: boolean;
};
};
};
type SegmentationState = {
viewportSegRepresentations: {
[viewportId: string]: Array<SegmentationRepresentation>;
};
};
以前,分割表示是基于工具组的,这导致了一些问题。在新的结构中,分割表示是视口特定的。它现在由 segmentationId、type 以及该分割的各种设置组成。由于这一变化,几个函数被移除或修改。以下是更改的总结:
移除的函数
getDefaultSegmentationStateManagergetSegmentationRepresentationsgetAllSegmentationRepresentationsgetSegmentationIdRepresentationsfindSegmentationRepresentationByUIDgetToolGroupIdsWithSegmentationgetToolGroupSpecificConfigsetToolGroupSpecificConfiggetGlobalConfigsetGlobalConfigsetSegmentationRepresentationSpecificConfiggetSegmentationRepresentationSpecificConfiggetSegmentSpecificRepresentationConfigsetSegmentSpecificRepresentationConfiggetToolGroupIdFromSegmentationRepresentationUIDaddSegmentationRepresentationgetSegmentationRepresentationByUID
新的函数
addSegmentations(segmentationInputArray)removeSegmentation(segmentationId)getSegmentation(segmentationId)getSegmentations()getSegmentationRepresentation(viewportId, specifier)getSegmentationRepresentations(viewportId, specifier)removeSegmentationRepresentation(viewportId, specifier, immediate)removeAllSegmentationRepresentations()removeLabelmapRepresentation(viewportId, segmentationId, immediate)removeContourRepresentation(viewportId, segmentationId, immediate)removeSurfaceRepresentation(viewportId, segmentationId, immediate)getViewportSegmentations(viewportId, type)getViewportIdsWithSegmentation(segmentationId)getCurrentLabelmapImageIdForViewport(viewportId, segmentationId)updateLabelmapSegmentationImageReferences(segmentationId, imageIds)getStackSegmentationImageIdsForViewport(viewportId, segmentationId)destroy()
移除 SegmentationDisplayTool
不再需要将 SegmentationDisplayTool 添加到 toolGroup。
之前
toolGroup2.addTool(SegmentationDisplayTool.toolName);
toolGroup1.setToolEnabled(SegmentationDisplayTool.toolName);
现在
// 无需任何操作
堆栈标签图
要创建堆栈标签图,您不再需要手动在标签图 imageIds 和视口 imageIds 之间创建引用。我们现在为您自动处理此过程。
这需要一个长篇的为什么...
以前的模型要求用户提供一个 imageIdReferenceMap,将标签图 imageIds 链接到视口 imageIds。这种方法在实现高级分割用例时带来了几个挑战:
-
手动创建映射容易出错,特别是在 imageIds 的顺序方面。
-
一旦分割与特定的视口 imageIds 相关联,就很难在其他地方渲染。例如:
- 在单个关键图像上渲染 CT 图像堆栈分割。
- 在包含 CT 和其他图像的堆栈上渲染 CT 图像堆栈分割。
- 在能量 1 上渲染 DX 双能分割到能量 2。
- 在同一空间的 PT 标签图上从堆栈视口渲染 CT 标签图。
这些场景突显了以前模型的局限性。
我们现在已经过渡到一个系统,用户只需提供 imageIds。在渲染过程中,我们将视口的当前 imageId 与标签图 imageIds 进行匹配,如果有匹配项,则渲染分割。这个匹配过程发生在 SegmentationStateManager 中,条件是分割必须与引用的视口处于同一平面。
这种新方法启用了许多额外的用例,并为分割渲染提供了更大的灵活性。
- 之前 📦
- 之后 🚀🚀
segmentation.addSegmentations([
{
segmentationId,
representation: {
type: csToolsEnums.SegmentationRepresentations.Labelmap,
data: {
imageIdReferenceMap:
cornerstoneTools.utilities.segmentation.createImageIdReferenceMap(
imageIds,
segmentationImageIds
),
},
},
},
]);
// 在这里填写“之后”部分的代码
迁移步骤:
- 将通用的
addSegmentationRepresentations调用替换为适当的特定表示函数。 - 更新输入数组以匹配新的
RepresentationPublicInput类型。 - 从代码中移除任何特定类型的逻辑,因为现在这些逻辑由这些新函数处理。
多视口函数
版本 2 引入了新的函数,用于同时向多个视口添加分割表示。
- 之前 📦
- 之后 🚀🚀
// 版本 1 中没有等效的函数
function addContourRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
function addLabelmapRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
function addSurfaceRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
迁移步骤:
- 如果您之前向多个工具组添加表示,请重构代码以使用这些新的多视口函数。
- 创建一个
viewportInputMap对象,将视口 ID 作为键,RepresentationPublicInput数组作为值。 - 根据表示类型调用适当的多视口函数。
事件
由于我们从工具组转向视口,许多事件已被重命名,以包含 viewportId 而不是 toolGroupId,并且
一些事件详情已更改为包含 segmentationId 而不是 segmentationRepresentationUID 或 toolGroupId。
移除工具组特定事件
triggerSegmentationRepresentationModified 和 triggerSegmentationRepresentationRemoved 函数已被移除。取而代之的是,库现在使用更通用的方法来处理分割事件。
- 之前 📦
- 之后 🚀🚀
function triggerSegmentationRepresentationModified(
toolGroupId: string,
segmentationRepresentationUID?: string
): void {
// ...
}
function triggerSegmentationRepresentationRemoved(
toolGroupId: string,
segmentationRepresentationUID: string
): void {
// ...
}
function triggerSegmentationRepresentationModified(
viewportId: string,
segmentationId: string,
type?: SegmentationRepresentations
): void {
// ...
}
function triggerSegmentationRepresentationRemoved(
viewportId: string,
segmentationId: string,
type: SegmentationRepresentations
): void {
// ...
}
迁移步骤:
- 在函数调用中将
toolGroupId替换为viewportId。 - 将
segmentationRepresentationUID替换为segmentationId。 - 添加
type参数以指定分割表示类型。
简化的分割修改事件
triggerSegmentationModified 函数已简化,始终需要一个 segmentationId。
- 之前 📦
- 之后 🚀🚀
function triggerSegmentationModified(segmentationId?: string): void {
// ...
}
function triggerSegmentationModified(segmentationId: string): void {
// ...
}
迁移步骤:
- 确保在调 用
triggerSegmentationModified时始终提供segmentationId。 - 移除任何处理
segmentationId未定义情况的逻辑。
更新的事件详情类型
几个事件详情类型已更新,以反映分割系统中的更改:
- 之前 📦
- 之后 🚀🚀
type SegmentationRepresentationModifiedEventDetail = {
toolGroupId: string;
segmentationRepresentationUID: string;
};
type SegmentationRepresentationRemovedEventDetail = {
toolGroupId: string;
segmentationRepresentationUID: string;
};
type SegmentationRenderedEventDetail = {
viewportId: string;
toolGroupId: string;
};
type SegmentationRepresentationModifiedEventDetail = {
segmentationId: string;
type: string;
viewportId: string;
};
type SegmentationRepresentationRemovedEventDetail = {
segmentationId: string;
type: string;
viewportId: string;
};
type SegmentationRenderedEventDetail = {
viewportId: string;
segmentationId: string;
type: string;
};
triggerAnnotationRenderForViewportIds
现在只需要 viewportIds,不再需要 renderingEngine。
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIds) ---> triggerAnnotationRenderForViewportIds(viewportIds)
Details
为什么?
因为每个视口都有一个渲染引擎,因此不需要将渲染引擎作为参数传递。工具
StackScrollMouseWheelTool -> StackScrollTool
我们已经将鼠标滚轮与工具本身解耦,使其可以像其他鼠标绑定一样应用为绑定。
此更改带来了多个优势:
- 它可以与其他鼠标绑定组合使用
- 它可以与键盘绑定配对使用
- 之前 📦
- 之后 🚀🚀
cornerstoneTools.addTool(StackScrollMouseWheelTool);
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
cornerstoneTools.addTool(StackScrollTool);
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel,
},
],
});
BaseTool
getTargetVolumeId 方法已被移除,取而代之的是 getTargetId,而 getTargetIdImage 已重命名为 getTargetImageData,以更清楚地表明它是图像数据。
使用示例
- 之前 📦
- 之后 🚀
const volumeId = this.getTargetVolumeId(viewport);
const imageData = this.getTargetIdImage(targetId, renderingEngine);
const imageData = this.getTargetImageData(targetId);
新的分割模型
我们有一个新的分割模型,更加灵活且易于使用。
相同术语,不同架构
在 Cornerstone3D 版本 2 中,我们对分割模型进行了重大架构更改,同时保持了熟悉的术语。此重新设计旨在为在不同视口中处理分割提供更灵活和直观的方法。以下是主要更改及其背后的原因:
-
视口特定,而非基于工具组:
- 以前:分割与工具组绑定,工具组通常由多个视口组成。当用户希望在同一工具组内为某些视口添加分割而不是其他视口时,这会带来复杂性。
- 现在:分割现在是视口特定的。用户可以直接向视口添加分割,而不是向工具组添加或移除表示。这为每个视口渲染的内容提供了更细致的控制。
- 为什么:我们发现将渲染绑定到工具组并不是一种有效的 方法。它通常需要为特定视口创建额外的工具组以进行自定义或防止渲染。
-
简化分割表示的识别:
- 以前:需要一个唯一的
segmentationRepresentationUID进行识别。 - 现在:分割表示通过
segmentationId和表示type的组合进行识别。这允许每个视口对同一分割有不同的表示。 - 为什么:这种简化使得在不同视口中管理和引用分割表示更加容易。
- 以前:需要一个唯一的
-
数据与可视化的解耦:
- 以前:分割渲染与工具组紧密耦合。
- 现在:分割现在纯粹作为数据处理,与用于交互的工具分离。
- 为什么:虽然将工具绑定到工具组是合适的,但像分割渲染这样的视口特定功能应该由各个视口负责。这种分离允许在不同视口中有更灵活的渲染和交互选项。
-
多态分割支持:
- 新架构更好地支持多态分割的概念,即单个分割可以有多个表示(例如,标签图、轮廓、表面),并且可以在它们之间高效地转换。
- 为什么:这种灵活性允许更高效地存储、分析和实时可视化分割。
-
跨表示类型的一致 API:
- 新的 API 提供了一种统一的方式来处理不同的分割表示,使得管理涉及多个视口和表示类型的复杂场景更加容易。
- 为什么:这种一致性简化了开发,并减少了在处理不同分割类型时出错的可能性。
这些架构更改为处理分割提供了更坚实的基础,特别是在复杂的多视口场景中。新方法已被证明非常有效,并为未来的增强功能打开了可能 性。虽然核心概念保持相似,但您在代码中与分割交互的方式将会显著改变。本迁移指南将引导您完成这些更改,提供前后示例,帮助您将现有代码库更新到新架构。
分割状态
Segmentation 类型已被重组,以更好地组织分割信息和表示数据。在讨论迁移指南之前,让我们先看看更改。
- 之前 📦
- 之后 🚀🚀
type Segmentation = {
segmentationId: string;
type: Enums.SegmentationRepresentations;
label: string;
activeSegmentIndex: number;
segmentsLocked: Set<number>;
cachedStats: { [key: string]: number };
segmentLabels: { [key: string]: string };
representationData: SegmentationRepresentationData;
};
type Segmentation = {
segmentationId: string;
label: string;
segments: {
[segmentIndex: number]: Segment;
};
representationData: RepresentationsData;
};
type Segment = {
segmentIndex: number;
label: string;
locked: boolean;
cachedStats: { [key: string]: unknown };
active: boolean;
};
新的分割状态模型提供了更有组织的数据结构。以前分散的信息,如 cachedStats、segmentLabels 和 activeSegmentIndex,已被整合到 segments 属性下。这种重组增强了清晰度和效率。在接下来的部分中,我们将讨论迁移指南,解释如何在新结构中访问和修改这些属性。这种重组主要影响分割存储级别。
表示数据键
SegmentationRepresentations 枚举已更新为使用标题大小写而不是全大写,以使其与其他枚举保持一致。
- 之前 📦
- 之后 🚀🚀
enum SegmentationRepresentations {
Labelmap = 'LABELMAP',
Contour = 'CONTOUR',
Surface = 'SURFACE',
}
enum SegmentationRepresentations {
Labelmap = 'Labelmap',
Contour = 'Contour',
Surface = 'Surface',
}
这项更改影响了表示数据的访问方式:
- 之前 📦
- 之后 🚀🚀
const representationData = segmentation.representationData.SURFACE;
const representationData = segmentation.representationData.LABELMAP;
const representationData = segmentation.representationData.CONTOUR;
const representationData = segmentation.representationData.Surface;
const representationData = segmentation.representationData.Labelmap;
const representationData = segmentation.representationData.Contour;
分割表示
表示结构已被简化,现在是视口特定的。
- 之前 📦
- 之后 🚀🚀
type ToolGroupSpecificRepresentation =
| ToolGroupSpecificLabelmapRepresentation
| ToolGroupSpecificContourRepresentation;
type ToolGroupSpecificRepresentationState = {
segmentationRepresentationUID: string;
segmentationId: string;
type: Enums.SegmentationRepresentations;
active: boolean;
segmentsLocked: Set<number>;
colorLUTIndex: number;
};
type SegmentationState = {
toolGroups: {
[key: string]: {
segmentationRepresentations: ToolGroupSpecificRepresentations;
config: SegmentationRepresentationConfig;
};
};
};
type SegmentationRepresentation =
| LabelmapRepresentation
| ContourRepresentation
| SurfaceRepresentation;
type BaseSegmentationRepresentation = {
colorLUTIndex: number;
segmentationId: string;
type: Enums.SegmentationRepresentations;
visible: boolean;
active: boolean;
segments: {
[segmentIndex: number]: {
visible: boolean;
};
};
};
type SegmentationState = {
viewportSegRepresentations: {
[viewportId: string]: Array<SegmentationRepresentation>;
};
};
以前,分割表示是基于工具组的,这导致了一些问题。在新的结构中,分割表示是视口特定的。它现在由 segmentationId、type 以及该分割的各种设置组成。由于这一变化, 几个函数被移除或修改。以下是更改的总结:
移除的函数
getDefaultSegmentationStateManagergetSegmentationRepresentationsgetAllSegmentationRepresentationsgetSegmentationIdRepresentationsfindSegmentationRepresentationByUIDgetToolGroupIdsWithSegmentationgetToolGroupSpecificConfigsetToolGroupSpecificConfiggetGlobalConfigsetGlobalConfigsetSegmentationRepresentationSpecificConfiggetSegmentationRepresentationSpecificConfiggetSegmentSpecificRepresentationConfigsetSegmentSpecificRepresentationConfiggetToolGroupIdFromSegmentationRepresentationUIDaddSegmentationRepresentationgetSegmentationRepresentationByUID
新的函数
addSegmentations(segmentationInputArray)removeSegmentation(segmentationId)getSegmentation(segmentationId)getSegmentations()getSegmentationRepresentation(viewportId, specifier)getSegmentationRepresentations(viewportId, specifier)removeSegmentationRepresentation(viewportId, specifier, immediate)removeAllSegmentationRepresentations()removeLabelmapRepresentation(viewportId, segmentationId, immediate)removeContourRepresentation(viewportId, segmentationId, immediate)removeSurfaceRepresentation(viewportId, segmentationId, immediate)getViewportSegmentations(viewportId, type)getViewportIdsWithSegmentation(segmentationId)getCurrentLabelmapImageIdForViewport(viewportId, segmentationId)updateLabelmapSegmentationImageReferences(segmentationId, imageIds)getStackSegmentationImageIdsForViewport(viewportId, segmentationId)destroy()
移除 SegmentationDisplayTool
不再需要将 SegmentationDisplayTool 添加到 toolGroup。
之前
toolGroup2.addTool(SegmentationDisplayTool.toolName);
toolGroup1.setToolEnabled(SegmentationDisplayTool.toolName);
现在
// 无需任何操作
堆栈标签图
要创建堆栈标签图,您不再需要手动在标签图 imageIds 和视口 imageIds 之间创建引用。我们现在为您自动处理此过程。
这需要一个长篇的为什么...
以前的模型要求用户提供一个 imageIdReferenceMap,将标签图 imageIds 链接到视口 imageIds。这种方法在实现高级分割用例时带来了几个挑战:
-
手动创建映射容易出错,特别是在 imageIds 的顺序方面。
-
一旦分割与特定的视口 imageIds 相关联,就很难在其他地方渲染。例如:
- 在单个关键图像上渲染 CT 图像堆栈分割。
- 在包含 CT 和其他图像的堆栈上渲染 CT 图像堆栈分割。
- 在能量 1 上渲染 DX 双能分割到能量 2。
- 在同一空间的 PT 标签图上从堆栈视口渲染 CT 标签图。
这些场景突显了以前模型的局限性。
我们现在已经过渡到一个系统,用户只需提供 imageIds。在渲染过程中,我们将视口的当前 imageId 与标签图 imageIds 进行匹配,如果有匹配项,则渲染分割。这个匹配过程发生在 SegmentationStateManager 中,条件是分割必须与引用的视口处于同一平面。
这种新方法启用了许多额外的用例,并为分割渲染提供了更大的灵活性。
- 之前 📦
- 之后 🚀🚀
segmentation.addSegmentations([
{
segmentationId,
representation: {
type: csToolsEnums.SegmentationRepresentations.Labelmap,
data: {
imageIdReferenceMap:
cornerstoneTools.utilities.segmentation.createImageIdReferenceMap(
imageIds,
segmentationImageIds
),
},
},
},
]);
// 在这里填写“之后”部分的代码
迁移步骤:
- 将通用的
addSegmentationRepresentations调用替换为适当的特定表示函数。 - 更新输入数组以匹配新的
RepresentationPublicInput类型。 - 从代码中移除任何特定类型的逻辑,因为现在这些逻辑由这些新函数处理。
多视口函数
版本 2 引入了新的函数,用于同时向多个视口添加分割表示。
- 之前 📦
- 之后 🚀 🚀
// 版本 1 中没有等效的函数
function addContourRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
function addLabelmapRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
function addSurfaceRepresentationToViewportMap(viewportInputMap: {
[viewportId: string]: RepresentationPublicInput[];
});
迁移步骤:
- 如果您之前向多个工具组添加表示,请重构代码以使用这些新的多视口函数。
- 创建一个
viewportInputMap对象,将视口 ID 作为键,RepresentationPublicInput数组作为值。 - 根据表示类型调用适当的多视口函数。
事件
由于我们从工具组转向视口,许多事件已被重命名,以包含 viewportId 而不是 toolGroupId,并且
一些事件详情已更改为包含 segmentationId 而不是 segmentationRepresentationUID 或 toolGroupId。