@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,以更清楚地表明它是 image data。
用法示例
- 迁移前 📦
- 迁移后 🚀
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 属性之下。这次重组提升了清晰度和效率。
后面几节会给出迁移做法,说明在新结构中如何访问和修改这些属性。
这次重组主要影响的是分割 store 这一层。
表示形式数据的键
SegmentationRepresentations 枚举已从全大写改为首字母大写,
以与库中其余的 Enums 保持一致。
- 迁移前 📦
- 迁移后 🚀🚀
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;
segmentsHidden: 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 添加到工具组里了。
迁移前
toolGroup2.addTool(SegmentationDisplayTool.toolName);
toolGroup1.setToolEnabled(SegmentationDisplayTool.toolName);
现在
// 什么都不用做
堆栈标签图
要创建堆栈标签图,你不再需要手工在标签图 imageId 与视口 imageId 之间建立引用关系。这个过程现在由我们自动处理。
下面这段「为什么」比较长……
旧模型要求用户提供一个 imageIdReferenceMap,把标签图 imageId
与视口 imageId 关联起来。在实现一些进阶分割场景时,这种做法带来了若干挑战:
-
手工创建这个映射容易出错,尤其是在 imageId 顺序这件事上。
-
一旦某份分割与特定的视口 imageId 绑定了,想在别处渲染它就很麻烦。例如:
- 把一套 CT 影像堆栈的分割渲染到单张关键图像上。
- 把一套 CT 影像堆栈的分割渲染到一个同时包含 CT 和其他影像的堆栈上。
- 把 DX 双能量中能量 1 的分割渲染到能量 2 上。
- 把堆栈视口的 CT 标签图渲染到同一空间的 PT 标签图上。
这些场景凸显了旧模型的局限。
我们现在改成了这样:用户只需提供 imageIds。渲染时, 我们把视口当前的 imageId 与那些标签图 imageId 做匹配,匹配上就渲染该分割。 这个匹配过程发生在 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,
},
},
},
]);
添加分割
函数签名的更新
addSegmentations 函数现在接受一个可选的 suppressEvents 参数。
- 迁移前 📦
- 迁移后 🚀🚀
function addSegmentations(
segmentationInputArray: SegmentationPublicInput[]
): void;
function addSegmentations(
segmentationInputArray: SegmentationPublicInput[],
suppressEvents?: boolean
): void;
迁移步骤:
- 如有需要,更新
addSegmentations的调用以传入suppressEvents参数。 - 如果不想抑制事件,可以省略第二个参数。
SegmentationPublicInput 类型的更新
SegmentationPublicInput 类型已扩展,增加了一个可选的 config 属性。
- 迁移前 📦
- 迁移后 🚀🚀
type SegmentationPublicInput = {
segmentationId: string;
representation: {
type: Enums.SegmentationRepresentations;
data?: RepresentationData;
};
};
type SegmentationPublicInput = {
segmentationId: string;
representation: {
type: Enums.SegmentationRepresentations;
data?: RepresentationData;
};
config?: {
segments?: {
[segmentIndex: number]: Partial<Segment>;
};
label?: string;
};
};
迁移步骤:
- 如有需要,更新那些创建或操作
SegmentationPublicInput对象的代码, 加入新的config属性。 - 把具体的分割数据类型替换为通用的
RepresentationData类型。
添加分割表示形式
以视口为中心的方式
该 API 现在聚焦于视口而不是工具组,对分割表示形式提供了更细的控制粒度。
- 迁移前 📦
- 迁移后 🚀🚀
function addSegmentationRepresentations(
toolGroupId: string,
representationInputArray: RepresentationPublicInput[],
toolGroupSpecificRepresentationConfig?: SegmentationRepresentationConfig
): Promise<string[]>;
function addSegmentationRepresentations(
viewportId: string,
segmentationInputArray: RepresentationPublicInput[]
);
迁移步骤:
- 把函数调用中的
toolGroupId换成viewportId。 - 去掉
toolGroupSpecificRepresentationConfig参数。 - 更新那些依赖「返回分割表示形式 UID 的 Promise」的代码。
RepresentationPublicInput 的变化
RepresentationPublicInput 类型被简化了,有些属性被改名或移除。
- 迁移前 📦
- 迁移后 🚀🚀
type RepresentationPublicInput = {
segmentationId: string;
type: Enums.SegmentationRepresentations;
options?: {
segmentationRepresentationUID?: string;
colorLUTOrIndex?: Types.ColorLUT | number;
polySeg?: {
enabled: boolean;
options?: any;
};
};
};
type RepresentationPublicInput = {
segmentationId: string;
type?: Enums.SegmentationRepresentations;
config?: {
colorLUTOrIndex?: Types.ColorLUT[] | number;
};
};
迁移步骤:
- 去掉
options属性,把colorLUTOrIndex移到config对象里。 - 如果用过
segmentationRepresentationUID和polySeg属性,请去掉—— polySEG 现在默认启用。 - 把
colorLUTOrIndex的类型改为接受Types.ColorLUT数组,而不是单个值。
各表示形式专用的新函数
版本 2 引入了一批新函数,用于向视口添加特定类型的分割表示形式。
- 迁移前 📦
- 迁移后 🚀🚀
// 版本 1 中没有对应的函数
function addContourRepresentationToViewport(
viewportId: string,
contourInputArray: RepresentationPublicInput[]
);
function addLabelmapRepresentationToViewport(
viewportId: string,
labelmapInputArray: RepresentationPublicInput[]
);
function addSurfaceRepresentationToViewport(
viewportId: string,
surfaceInputArray: RepresentationPublicInput[]
);
迁移步骤:
- 把通用的
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数组为值。 - 依据表示形式类型调用相应的多视口函数。
事件
由于我们从工具组转向了视口,许多事件被改名,把 toolGroupId 换成了
viewportId;一些事件详情也做了改动,用 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为 undefined 情形的逻辑。
更新后的事件详情类型
若干事件详情类型已更新,以反映分割体系的变化:
- 迁移前 📦
- 迁移后 🚀🚀
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;
};
迁移步骤:
- 更新事件监听器以使用新的事件详情类型。
- 在适用处把
toolGroupId换成viewportId。 - 用
segmentationId取代segmentationRepresentationUID。 - 为事件详情中新增的
type字段加上相应处理。
分割的配置 / 样式
在 Cornerstone3D 2.x 中,我们对分割配置的 API 做了大幅重构, 以提供一种更灵活、更统一的方式来管理不同表示形式 (标签图、轮廓、曲面)的分割样式。旧的那套获取和设置分割配置的 API 已被新函数取代——新函数通过一个 specifier 对象来定位 具体的分割、视口和分段。
被移除的函数
getGlobalConfigsetGlobalConfiggetGlobalRepresentationConfigsetGlobalRepresentationConfiggetToolGroupSpecificConfigsetToolGroupSpecificConfiggetSegmentSpecificConfigsetSegmentSpecificConfiggetSegmentationRepresentationSpecificConfigsetSegmentationRepresentationSpecificConfig
新增的函数
getStyle(specifier)setStyle(specifier, style)setRenderInactiveSegmentations(viewportId, renderInactiveSegmentations)getRenderInactiveSegmentations(viewportId)resetToGlobalStyle()hasCustomStyle(specifier)
获取全局分割配置
- 迁移前 📦
- 迁移后 🚀
// 取得全局分割配置
const globalConfig = getGlobalConfig();
// 取得某种表示形式类型的全局表示形式配置
const labelmapConfig = getGlobalRepresentationConfig(
SegmentationRepresentations.Labelmap
);
// 取得某种表示形式类型的全局样式
const labelmapStyle = getStyle({ type: SegmentationRepresentations.Labelmap });
设置全局分割配置
- 迁移前 📦
- 迁移后 🚀
// 设置全局分割配置
setGlobalConfig(newGlobalConfig);
// 设置某种表示形式类型的全局表示形式配置
setGlobalRepresentationConfig(
SegmentationRepresentations.Labelmap,
newLabelmapConfig
);
// 设置某种表示形式类型的全局样式
setStyle({ type: SegmentationRepresentations.Labelmap }, newLabelmapStyle);
获取与设置工具组专属配置
工具组专属的配置已被移除,取而代之的是按视口的样式。 下面的写法会为某个特定视口中的某份特定分割设置样式。
- 迁移前 📦
- 迁移后 🚀
// 取得工具组专属配置
const toolGroupConfig = getToolGroupSpecificConfig(toolGroupId);
// 设置工具组专属配置
setToolGroupSpecificConfig(toolGroupId, newToolGroupConfig);
// 为某个特定视口中的某个分割表示形式设置样式
setStyle(
{
viewportId: 'viewport1',
segmentationId: 'segmentation1',
type: SegmentationRepresentations.Labelmap,
},
newLabelmapStyle
);
// 取得某个特定视口中某个分割表示形式的样式
const style = getStyle({
viewportId: 'viewport1',
segmentationId: 'segmentation1',
type: SegmentationRepresentations.Labelmap,
});
获取与设置分割表示形式专属配置
在 Cornerstone3D 2.x 中,用于获取和设置「分割表示形式专属配置」的函数 已被统一的样式管理 API 取代。以下这两个旧函数:
getSegmentationRepresentationSpecificConfig
setSegmentationRepresentationSpecificConfig
已不再可用。请改用 getStyle 和 setStyle,并通过 specifier 对象 来定位具体的分割和表示形式。
- 迁移前 📦
- 迁移后 🚀🚀
// 取得分割表示形式专属配置
const representationConfig = getSegmentationRepresentationSpecificConfig(
toolGroupId,
segmentationRepresentationUID
);
// 设置分割表示形式专属配置
setSegmentationRepresentationSpecificConfig(
toolGroupId,
segmentationRepresentationUID,
{
LABELMAP: {
renderOutline: true,
outlineWidth: 2,
},
}
);
// 取得某个分割表示形式的样式
const style = getStyle({
segmentationId: 'segmentation1',
type: SegmentationRepresentations.Labelmap,
});
// 在所有视口中为某个分割表示形式设置样式
setStyle(
{
segmentationId: 'segmentation1',
type: SegmentationRepresentations.Labelmap,
},
{
renderOutline: true,
outlineWidth: 2,
}
);
获取与设置分段专属配置
- 迁移前 📦
- 迁移后 🚀
// 取得分段专属配置
const segmentConfig = getSegmentSpecificConfig(
toolGroupId,
segmentationRepresentationUID,
segmentIndex
);
// 设置分段专属配置
setSegmentSpecificConfig(
toolGroupId,
segmentationRepresentationUID,
segmentIndex,
newSegmentConfig
);
// 为某个特定分段设置样式
setStyle(
{
segmentationId: 'segmentation1',
type: SegmentationRepresentations.Labelmap,
segmentIndex: 1,
},
newSegmentStyle
);
// 取得某个特定分段的样式
const segmentStyle = getStyle({
segmentationId: 'segmentation1',
type: SegmentationRepresentations.Labelmap,
segmentIndex: 1,
});
设置是否渲染非活动分割
用于开启或关闭「渲染非活动分割」的函数已更新。
迁移前
它此前是分割配置的一部分:
setGlobalConfig({ renderInactiveSegmentations: true });
迁移后
改用 setRenderInactiveSegmentations:
// 设置某个视口中是否渲染非活动分割
setRenderInactiveSegmentations(viewportId, true);
// 取得某个视口中非活动分割是否被渲染
const renderInactive = getRenderInactiveSegmentations(viewportId);
重置为全局样式
要把所有分割样式重置为全局样式:
resetToGlobalStyle();
迁移示例
- 迁移前 📦
- 迁移后 🚀🚀
import {
getGlobalConfig,
getGlobalRepresentationConfig,
getToolGroupSpecificConfig,
setGlobalConfig,
setGlobalRepresentationConfig,
setToolGroupSpecificConfig,
setSegmentSpecificConfig,
getSegmentSpecificConfig,
setSegmentationRepresentationSpecificConfig,
getSegmentationRepresentationSpecificConfig,
} from './segmentationConfig';
// 取得全局分割配置
const globalConfig = getGlobalConfig();
// 设置全局表示形式配置
setGlobalRepresentationConfig(SegmentationRepresentations.Labelmap, {
renderOutline: true,
outlineWidth: 2,
});
// 设置工具组专属配置
setToolGroupSpecificConfig(toolGroupId, {
representations: {
LABELMAP: {
renderOutline: false,
},
},
});
// 设置分段专属配置
setSegmentSpecificConfig(
toolGroupId,
segmentationRepresentationUID,
segmentIndex,
{
LABELMAP: {
renderFill: false,
},
}
);
import {
getStyle,
setStyle,
setRenderInactiveSegmentations,
getRenderInactiveSegmentations,
resetToGlobalStyle,
hasCustomStyle,
} from '@cornerstonejs/core';
// 取得标签图表示形式的全局样式
const labelmapStyle = getStyle({ type: SegmentationRepresentations.Labelmap });
// 设置标签图表示形式的全局样式
setStyle(
{ type: SegmentationRepresentations.Labelmap },
{
renderOutline: true,
outlineWidth: 2,
}
);
// 为某个特定视口与分割设置样式
setStyle(
{
viewportId: 'viewport1',
segmentationId: 'segmentation1',
type: SegmentationRepresentations.Labelmap,
},
{
renderOutline: false,
}
);
// 为某个特定分段设置样式
setStyle(
{
segmentationId: 'segmentation1',
type: SegmentationRepresentations.Labelmap,
segmentIndex: segmentIndex,
},
{
renderFill: false,
}
);
// 设置某个视口是否渲染非活动分割
setRenderInactiveSegmentations('viewport1', true);
// 取得某个视口「是否渲染非活动分割」的设置
const renderInactive = getRenderInactiveSegmentations('viewport1');
// 把所有样式重置为全局样式
resetToGlobalStyle();
小结
- 统一的样式管理:新的
getStyle和setStyle函数提供了一种统一的方式, 来管理不同层级的分割样式——全局、按分割、按视口、按分段。 - specifier 对象:
specifier对象让你可以定位具体的视口、分割和分段。type是必需的- 若提供了
segmentationId,样式会应用到所有视口中该分割的表示形式上 - 若同时提供了
segmentationId和segmentIndex, 样式会应用到该分割表示形式中那个特定分段上 - 若提供了
viewportId,样式会应用到该特定视口中的所有分割上 - 若同时提供了
viewportId、segmentationId和segmentIndex, 样式会应用到该特定视口中、该特定分割的那个特定分段上
- 样式的层级关系:最终生效的样式由一套层级决定, 它会综合考虑全局样式、按分割的样式和按视口的样式。
活动分割
基于视口的操作
该 API 现在用视口 ID 而不是工具组 ID 来标识分割操作的上下文。
- 迁移前 📦
- 迁移后 🚀🚀
function getActiveSegmentationRepresentation(toolGroupId: string);
function getActiveSegmentation(toolGroupId: string);
function setActiveSegmentationRepresentation(
toolGroupId: string,
segmentationRepresentationUID: string
);
function getActiveSegmentation(viewportId: string);
function setActiveSegmentation(
viewportId: string,
segmentationId: string,
suppressEvent: boolean = false
);
迁移步骤
- 把函数调用中所有的
toolGroupId换成viewportId。 - 把对
getActiveSegmentationRepresentation和getActiveSegmentation的调用统一改为使用新的getActiveSegmentation函数。 - 把
setActiveSegmentationRepresentation的调用改为setActiveSegmentation, 并使用新的参数结构。
返回类型的变化
getActiveSegmentation 的返回类型从隐式的 undefined
改为了显式的 Segmentation 类型。
- 迁移前 📦
- 迁移后 🚀🚀
function getActiveSegmentation(toolGroupId: string);
function getActiveSegmentation(viewportId: string): Segmentation;
迁移步骤
- 把所有对
getActiveSegmentationRepresentation的调用改为getActiveSegmentation。 - 更新那些依赖
ToolGroupSpecificRepresentation类型的代码, 改为处理Segmentation类型。
这些改动的目标是简化 API、让它更直观。通过聚焦于基于视口的操作、 并取消「分割表示形式」与「分割」之间的区分, 新 API 在保留库核心功能的同时应当更好用。
可见性
以视口为中心
该 API 现在关注的是视口而不是工具组,这反映了库架构上的转变。
- 迁移前 📦
- 迁移后 🚀🚀
function setSegmentationVisibility(
toolGroupId: string,
segmentationRepresentationUID: string,
visibility: boolean
): void {
// ...
}
function setSegmentationRepresentationVisibility(
viewportId: string,
specifier: {
segmentationId: string;
type?: SegmentationRepresentations;
},
visibility: boolean
): void {
// ...
}
迁移步骤:
- 把函数调用中的
toolGroupId换成viewportId。 - 用一个
specifier对象替代segmentationRepresentationUID。 - 在
specifier对象中带上segmentationId。 - 可以选择性地指定分割表示形式的
type。
分割表示形式的类型
版本 2 引入了「分割表示形式类型」这一概念, 使你能更细粒度地控制不同的表示形式样式。
- 迁移前 📦
- 迁移后 🚀🚀
function getSegmentationVisibility(
toolGroupId: string,
segmentationRepresentationUID: string
): boolean | undefined {
// ...
}
function getSegmentationRepresentationVisibility(
viewportId: string,
specifier: {
segmentationId: string;
type: SegmentationRepresentations;
}
): boolean | undefined {
// ...
}
迁移步骤:
- 把函数名从
getSegmentationVisibility改为getSegmentationRepresentationVisibility。 - 把
toolGroupId换成viewportId。 - 用带
segmentationId和type的specifier对象替代segmentationRepresentationUID。
分段级别的可见性控制
控制单个分段可见性的 API 也做了更新,以与新的「以视口为中心」保持一致。
- 迁移前 📦
- 迁移后 🚀🚀
function setSegmentVisibility(
toolGroupId: string,
segmentationRepresentationUID: string,
segmentIndex: number,
visibility: boolean
): void {
// ...
}
function setSegmentIndexVisibility(
viewportId: string,
specifier: {
segmentationId: string;
type?: SegmentationRepresentations;
},
segmentIndex: number,
visibility: boolean
): void {
// ...
}
迁移步骤:
- 把函数名从
setSegmentVisibility改为setSegmentIndexVisibility。 - 把
toolGroupId换成viewportId。 - 用带
segmentationId和可选type的specifier对象替代segmentationRepresentationUID。
新增的工具函数
版本 2 新增了几个用于管理分割可见性的工具函数。
function getHiddenSegmentIndices(
viewportId: string,
specifier: {
segmentationId: string;
type: SegmentationRepresentations;
}
): Set<number> {
// ...
}
这个新函数让你可以取得某个特定分割表示形式中被隐藏的分段索引集合。
被移除的函数
以下函数在版本 2 中被移除:
setSegmentsVisibilitygetSegmentVisibility
请改用上面介绍的那套新 API。
为什么?
因为可见性应当设置在「表示形式」上——分割本身并不是可见性的归属者。 同一份分割可以有两种表示形式,并且在各个视口中拥有各自不同的可见性。
加锁
取得被锁定的分段
用于取得被锁定分段的函数已改名,其实现也有变化:
- 迁移前 📦
- 迁移后 🚀🚀
function getLockedSegments(segmentationId: string): number[] | [];
function getLockedSegmentIndices(segmentationId: string): number[] | [];
迁移步骤:
- 把所有
getLockedSegments调用改为getLockedSegmentIndices。 - 注意其实现现在用的是
Object.keys加filter, 而不是把 Set 转成数组。
颜色
以视口为中心
该 API 已从「基于工具组」转为「以视口为中心」。 这项改动影响了若干函数签名,也改变了分割的引用方式。
- 迁移前 📦
- 迁移后 🚀🚀
function setColorLUT(
toolGroupId: string,
segmentationRepresentationUID: string,
colorLUTIndex: number
): void {
// ...
}
function setColorLUT(
viewportId: string,
segmentationId: string,
colorLUTsIndex: number
): void {
// ...
}
迁移步骤:
- 把函数调用中的
toolGroupId换成viewportId。 - 把
segmentationRepresentationUID换成segmentationId。 - 把依赖「基于工具组」管理分割的代码,改为按视口管理。
颜色 LUT 的管理
addColorLUT 现在会返回所添加颜色 LUT 的索引,
并且 colorLUTIndex 参数变为可选。
- 迁移前 📦
- 迁移后 🚀🚀
function addColorLUT(colorLUT: Types.ColorLUT, colorLUTIndex: number): void {
// ...
}
function addColorLUT(colorLUT: Types.ColorLUT, colorLUTIndex?: number): number {
// ...
}
迁移步骤:
- 如有需要,修改
addColorLUT的调用以接收它返回的索引。 - 在调用时把
colorLUTIndex参数当作可选参数处理。
分段颜色的读取与设置
获取和设置分段颜色的函数已改名,签名也做了更新, 以与新的「以视口为中心」保持一致。
- 迁移前 📦
- 迁移后 🚀🚀
function getColorForSegmentIndex(
toolGroupId: string,
segmentationRepresentationUID: string,
segmentIndex: number
): Types.Color {
// ...
}
function setColorForSegmentIndex(
toolGroupId: string,
segmentationRepresentationUID: string,
segmentIndex: number,
color: Types.Color
): void {
// ...
}
function getSegmentIndexColor(
viewportId: string,
segmentationId: string,
segmentIndex: number
): Types.Color {
// ...
}
function setSegmentIndexColor(
viewportId: string,
segmentationId: string,
segmentIndex: number,
color: Types.Color
): void {
// ...
}
迁移步骤:
- 把
getColorForSegmentIndex改名为getSegmentIndexColor。 - 把
setColorForSegmentIndex改名为setSegmentIndexColor。 - 修改函数调用,用
viewportId替代toolGroupId。 - 在函数调用中把
segmentationRepresentationUID换成segmentationId。
其他变化
重命名
getSegmentAtWorldPoint-- > getSegmentIndexAtWorldPoint;
getSegmentAtLabelmapBorder-- > getSegmentIndexAtLabelmapBorder;
上游文档里这段代码块被代码格式化工具处理过,把箭头 --> 拆成了
-- >。它表达的意思是「改名为」,即
getSegmentAtWorldPoint → getSegmentIndexAtWorldPoint、
getSegmentAtLabelmapBorder → getSegmentIndexAtLabelmapBorder,
并不是真的要你写这样的代码。
getToolGroupIdsWithSegmentation
- 迁移前 📦
- 迁移后 🚀🚀
function getToolGroupIdsWithSegmentation(segmentationId: string): string[];
function getViewportIdsWithSegmentation(segmentationId: string): string[];
迁移步骤:
- 把
getToolGroupIdsWithSegmentation换成getViewportIdsWithSegmentation。
分割表示形式的管理
添加、获取、移除分割表示形式的方式有了显著变化。
- 迁移前 📦
- 迁移后 🚀🚀
function addSegmentationRepresentation(
toolGroupId: string,
segmentationRepresentation: ToolGroupSpecificRepresentation,
suppressEvents?: boolean
): void;
function getSegmentationRepresentationByUID(
toolGroupId: string,
segmentationRepresentationUID: string
): ToolGroupSpecificRepresentation | undefined;
function removeSegmentationRepresentation(
toolGroupId: string,
segmentationRepresentationUID: string
): void;
function addSegmentationRepresentation(
viewportId: string,
segmentationRepresentation: SegmentationRepresentation,
suppressEvents?: boolean
): void;
function getSegmentationRepresentation(
viewportId: string,
specifier: {
segmentationId: string;
type: SegmentationRepresentations;
}
): SegmentationRepresentation | undefined;
function removeSegmentationRepresentation(
viewportId: string,
specifier: {
segmentationId: string;
type: SegmentationRepresentations;
},
immediate?: boolean
): void;
迁移步骤:
- 把所有
addSegmentationRepresentation调用改为传viewportId而不是toolGroupId。 - 把
getSegmentationRepresentationByUID换成getSegmentationRepresentation,并使用新的 specifier 对象。 - 修改
removeSegmentationRepresentation的调用, 用新的 specifier 对象替代segmentationRepresentationUID。
PolySEG
引入
PolySEG 已从主包中剥离,放进了一个独立的外部包。
要使用它,请在 Cornerstone Core 的 init 调用中加上 peerImport 函数。
async function peerImport(moduleId) {
if (moduleId === '@icr/polyseg-wasm') {
return import('@icr/polyseg-wasm');
}
}
import { init } from '@cornerstonejs/core';
await init({ peerImport });
配置项
你不再需要为分割表示形式提供 polyseg 配置项。 当指定的表示形式不可用时,它会自动使用 PolySeg。
- 迁移前 📦
- 迁移后 🚀🚀
await segmentation.addSegmentationRepresentations(toolGroupId2, [
{
segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
options: {
polySeg: {
enabled: true,
},
},
},
]);
await segmentation.addSegmentationRepresentations(viewportId2, [
{
segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
},
]);
标签图的 Actor UID
actorUID 的生成方式变了,现在由 segmentationId 与
SegmentationRepresentations.Labelmap 组合而成。
- 迁移前 📦
- 迁移后 🚀
const volumeInputs: Types.IVolumeInput[] = [
{
volumeId: labelMapData.volumeId,
actorUID: segmentationRepresentationUID,
visibility,
blendMode: Enums.BlendModes.MAXIMUM_INTENSITY_BLEND,
},
];
const volumeInputs: Types.IVolumeInput[] = [
{
volumeId,
actorUID: `${segmentationId}-${SegmentationRepresentations.Labelmap}`,
visibility,
blendMode: Enums.BlendModes.MAXIMUM_INTENSITY_BLEND,
},
];
我们把 actorUID 改成了
${segmentationId}-${SegmentationRepresentations.Labelmap}。
这项改动让我们能唯一地标识表示形式,而不必依赖
segmentationRepresentationUID。
为此我们新增了 getSegmentationActor,供你取得某个标签图对应的 actor:
export function getSegmentationActor(
viewportId: string,
specifier: {
segmentationId: string;
type: SegmentationRepresentations;
}
): Types.VolumeActor | Types.ImageActor | undefined;
新增的工具函数
新增了 clearSegmentValue,用于清除分割中某个特定分段的值——
它会把该分段的值置为 0。
function clearSegmentValue(
segmentationId: string,
segmentIndex: number
)
重命名与命名规范
类型
PointsManager 现在叫 IPointsManager。
迁移做法:
import { IPointsManager } from '@cornerstonejs/tools/types';
单位
getCalibratedLengthUnitsAndScale 的签名
你直接调用这个函数的可能性很小,不过如果你确实用到了,迁移做法如下:
该函数的返回类型略有变化,units 和 areaUnits 分别改名为
unit 和 areaUnit。
- 迁移前 📦
- 迁移后 🚀
const getCalibratedLengthUnitsAndScale = (image, handles) => {
// ...
return { units, areaUnits, scale };
};
const getCalibratedLengthUnitsAndScale = (image, handles) => {
// ...
return { unit, areaUnit, scale };
};
getModalityUnit 改名为 getPixelValueUnits
这样命名更合理。
Details
为什么?
库里各处用到的「单位」此前太不统一了。我们有unit、areaUnits、
modalityUnit 以及其他各种叫法。现在这些单位已经被整合到一起。
如果你要在 Cornerstone3D 中水合(hydrate)标注,
就需要更新代码以适配新的单位体系。另外,modalityUnit 现在叫 pixelValueUnits,这个术语更准确—— 因为同一个模态可以对应多种像素值(例如 PT SUV、PT RAW、PT PROC)。
BasicStatsCalculator
配置项 noPointsCollection 已改名为 storePointData。
getSegmentAtWorldPoint 改名为 getSegmentIndexAtWorldPoint
getSegmentAtLabelmapBorder 改名为 getSegmentIndexAtLabelmapBorder
其他
roundNumber
该工具函数已从 @cornerstonejs/tools 的 utilities 移到了
@cornerstonejs/core/utilities。
迁移做法:
import { roundNumber } from '@cornerstonejs/core/utilities';
jumpToSlice
该工具函数已从 @cornerstonejs/tools 的 utilities 移到了
@cornerstonejs/core/utilities。
迁移做法:
import { jumpToSlice } from '@cornerstonejs/core/utilities';
pointInShapeCallback
1. 新的引入路径
pointInShapeCallback 函数换了位置。请按下面的方式更新 import:
import { pointInShapeCallback } from '@cornerstonejs/core/utilities';
2. 用法的变化
为了更清晰、也更灵活,该函数的签名改为使用一个 options 对象。 下面说明用法上的变化。
旧写法:
const pointsInShape = pointInShapeCallback(
imageData,
shapeFnCriteria,
(point) => {
// 针对每个点的回调逻辑
},
boundsIJK
);
新写法:
const pointsInShape = pointInShapeCallback(imageData, {
pointInShapeFn: shapeFnCriteria,
callback: (point) => {
// 针对每个点的回调逻辑
},
boundsIJK: boundsIJK,
returnPoints: true, // 可选,用于返回形状内部的那些点
});
主要变化
- options 对象:
pointInShapeFn、callback、boundsIJK、returnPoints等配置参数现在都通过一个 options 对象传入。 - 返回点集:用
returnPoints选项来指定是否返回形状内部的点; 此前它总是会返回这些点。如果你依赖它直接返回点集, 请确保在激活工具时于工具配置中带上storePointData: true。