cornerstoneTools模块 corner module
模块是 store 中的命名空间存储对象,其中包含以下属性:
Property | Requirement | Description |
configuration | 必须 | 一个对象,用于配置模块以供应用程序范围使用。配置旨在通过 cornerstoneTools 内部和外部的方法进行操作。 |
state | 可选 | 存储模块当前状态的对象。State 只能由 cornerstoneTools 和 cornerstoneTools 插件操作。没有必须遵循严格的结构状态,因为它将特定于模块尝试完成的任务。 |
getters | 可选 | 由查询状态的函数组成的对象。getter 可用于更复杂的状态查询(例如,生成引用特定 cornerstone enabled 元素的值)。不需要计算的顶级原语应该由 const property = state.property 访问,因为这减少了实现代码中的样板。 |
setters | 可选 | 由修改状态的函数组成的对象。Setter 可用于更复杂的输入(例如,将对象 x 推送到数组 y)。顶级原语应该由 state.property = value 设置,因为这可以减少实现代码中的样板。 |
onRegisterCallback (name) | 可选 | 当模块注册到 cornerstoneTools 存储区时,将调用此函数。它用于执行 module 所需的任何全局初始化。注册时为模块指定的名称将传递给回调。 |
enabledElementCallback (enabledElement) | 可选 | 在注册模块时,将为每个 Enabled 元素调用一次此函数,并在向 cornerstoneTools 实例添加新的 Enabled 元素时再次调用此函数。将 Enabled 元素传递给回调。 |
removeEnabledElementCallback (enabledElement) | 可选 | 每当从 cornerstoneTools 实例中删除 Enabled 元素时,都会调用此函数,从而允许清理不需要的数据。将 Enabled 元素传递给回调。 |
分割模块
分割模块处理每堆基石图像的 3D 标签图数据的集中化。数据集中有两个原因:
- 每个 imageId 存储的 Cornerstone 数据是 2D 格式,而标签映射在很大程度上是 3D 结构。将整个 3D 标签图的数据集中起来,并且 cornerstone 可以根据需要访问其 2D 和 3D 表示,从而可以灵活地执行 2D 和 3D 操作,而无需每次都重新聚合数据。
- 将 3D 标签映射存储在一个 ArrayBuffer 中可以实现更好的互操作性以及与 3D 平台(如 vtk-js)的和谐集成。如果使用 vtk-js 等 WebGL 库,建议在切分模块配置中设置使用 Float32Array 切分。
数据结构
数据以自上而下的分层方式存储,最粗糙的级别是整个研究范围。类型本身在 src/store/modules/segmentationModule/index.js 中进行了 JSDoc 处理,但此处给出了更深入的概述:
Series
state.series 下的 series 对象是最高级别的对象,包含一组 BrushStackState,与 Cornerstone 堆栈具有 1:1 的相关性。每个 BrushStackState 当前都按堆栈中第一个图像的 imageId 为每个堆栈编制索引。
BrushStackState
BrushStackState 对象包含两个属性:
- labelmaps3D: 一个或多个 Labelmap3D 对象的数组,这些对象的尺寸与堆栈相同(宽度 * 高度 * numberOfFrames)。
- 'activeLabelmapIndex`: 活动标签映射的数组索引。
每个 Labelmap3D 对象可以包含多个不重叠的片段。要在应用程序中使用重叠的 Segment,请使用多个 Labelmap3D。
Labelmap3D
Labelmap3D 是具有以下属性的单个、不重叠的 Labelmap:
- buffer: 一个 ArrayBuffer,其中每个体素的标签按递增的 x、y、z 顺序存储。它与堆栈的方向相同,因此增加 z 会增加堆栈中的 imageIdIndex。默认情况下,每个标签存储为 2 个字节(编码为 Uint16Array),最多允许 65535 个唯一段(+ 0 为空/未标记)。通过将 segmentation 配置属性 arrayType 设置为 Float32Array,你可以将 labelmap 存储为与 vtkjs 更和谐的格式。vtkjs 在生成要传递给 GPU 的纹理时必须转换非 Float32Arrays,这非常昂贵。如果你在应用程序中使用 vtkjs,强烈建议使用 Float32Arrays 进行分割,以减少纹理构建时间。例如,在使用 vtkjs 绘制小部件时,这一点尤其明显。
- labelmaps2D: 缓冲区上的 labelmap2D 视图数组,由堆栈内 imageIdIndex 索引。labelmaps2D 数组最初是空的,每个视图只有在添加数据时才被定义,如果设置为空(所有标签为零),则应将其删除。这使得分段渲染器、I/O 库(如 dcmjs)和工具可以轻松定位段的范围,而无需扫描堆栈中的每个标签。
- metadata: 每个区段的元数据对象数组。元数据是可选的,其形式是特定于应用程序的。你可能希望在 UI 中为标签提供一个简单的名称,或者你可能希望引用解剖区域标准库的更复杂的对象。元数据不由 cornerstoneTools 在内部使用,但充当一个可选的逻辑位置,用于为应用程序保留此类数据。
- activeSegmentIndex: 当 labelmap 处于活动状态时,使用 BaseBrushTools 或分段工具时要创建/修改的区段的索引。
- colorLUTIndex: 渲染 labelmap 时使用的 colorLUT 的索引。
- segmentsHidden: 要在画布中隐藏的区段数组。最初为空以节省空间,将索引设置为 true 以隐藏相应的段。
Labelmap2D
Labelmap2D 是分割的一帧的 2D 视图,它是主要从基石画布与之交互的对象,并具有以下属性:
- pixelData: 父级 Labelmap3D 缓冲区的一部分的 Uint16Array 或 Float32Array 视图,对应于帧。数组类型取决于 segmentation 模块的 arrayType 设置。
- segmentsOnLabelmap: pixelData 中存在的段数组。每当工具或外部操作 Labelmap2D 查看的 pixelData 时,此列表应更新。这通常非常便宜,例如在剪刀操作之后或完整的画笔笔触之后,但是在将数据压缩到 DICOMSEG 时,例如,你可以显着加快 IO 速度,因为你可以通过非常简单的检查计算出需要分配多少内存。
在 cornerstoneTools 中的使用
在基石图像上使用工具时,你可以轻松访问活动 Labelmap3D 的 Labelmap2D。通过查询 segmentationModule:
在文件顶部导入 segmentationModule getter。
import { getModule } from './store'; //appropriate relative path
// Destructure the parts of the module you require from the getters, setters, state and configuration. Here we only need the getters.
const { getters } = getModule('segmentation');
然后在 cornerstoneTools 事件回调期间,其中 evt 是事件:
const eventData = evt.detail;
const element = eventData.element;
const {
labelmap2D, // The `Labelmap2D` for this imageId.
labelmap3D, // The `Labelmap3D` for this stack.
currentImageIdIndex, // The currentImageIdIndex of this image in the stack.
activeLabelmapIndex, // The labelmapIndex of this active labelmap.
} = getters.labelmap2D(element);
这为你提供了你需要的所有信息。通过适当的解构,你可以根据需要获取此对象的元素。
直接编辑 labelmap
然后,你可以直接编辑 labelmap:
const { pixelData } = labelmap2D;
pixelData[1337] = 9001;
使用drawBrushPixels 帮助
或者,如果使用画笔,则可以使用 drawBrushPixels 辅助函数将 2D 坐标数组绘制到 Labelmap2D。
const { rows, columns } = eventData.image;
const pointerArray = [[0, 0], [1, 0], [2, 0]]; // Something grabbed by your brush.
const shouldErase = false;
// Imported from src/util/segmentation
drawBrushPixels(
pointerArray,
labelmap2D.pixelData,
labelmap3D.activeSegmentIndex,
columns,
shouldErase
);
要获取堆栈中当前未显示的另一张图像的 labelmap2D:
const imageIdIndex = 99;
const labelmap2DofImageIdIndex99 = getters.labelmap2DByImageIdIndex(
labelmap3D,
imageIdIndex,
rows,
columns
);
可以在 SphericalBrushTool 中找到此用法的示例。getters.labelmap2D 和 getters.labelmap2DByImageIdIndex 都将返回 Labelmap2D(如果存在),如果不存在,则创建并返回它。
此外,getters.labelmap2D 将初始化并创建 BrushStackState 和 Labelmap3D(如果尚不存在)。
更新 labelmap 占用率
在 Labelmap2D 上完成一个操作后,该工具/操作应该在 Labelmap2D 上调用 setters.updateSegmentsOnLabelmap2D(labelmap2D) 来更新其 segmentsOnLabelmap 属性。此属性是最新的正确状态对于其他工具和 I/O 库非常有用。
此操作不会自动完成,因为 operationn 的结束可能取决于任务。例如,在使用一次 CircleScissorsTool 后,我们将调用 updateSegmentsOnLabelmap2D,但是我们只在 BrushTool 笔触结束时执行此操作。你可能有一个更复杂的过程,即在人工干预下执行一系列迭代 growcuts,并且只想在最后更新 labelmap 占用率。
由应用使用
父应用程序可以通过查询 API 来检索有关标签映射的信息。从元数据到每个 Labelmap3D 的 colorLUT。最有用的 getter 函数可能是 getLabelmaps3D(elementOrEnabledElementUID),当给定基石元素或基石 enabledElement 的 UUID 时,它会返回一个 Labelmap3D 对象列表,可以解析这些对象以生成 UI。可以将一组 setter 链接到 UI 组件以更改活动段、设置段颜色等,请参阅 API 文档以获取帮助程序的完整列表。
由第三方库使用
你可能希望从外部 cornerstoneTools 访问一个或多个 Labelmap3D 缓冲区,以便将它们导出到持久存储,或将它们显示在另一个框架中,例如 vtkjs。要从 cornerstoneTools 外部检索元素上 activeLabelmap 的 ArrayBuffer:
const { getters } = cornerstoneTools.getModule('segmentation');
// Active buffer:
const { buffer, labelmapIndex, colorLUT } = getters.activeLabelmapBuffer(
element
);
// All labelmap buffers:
const bufferInfoArray = getters.labelmapBuffers(element);
// A specific buffer:
const { buffer7, colorLUT7 } = getters.labelmapBuffers(element, 3);
这些函数还返回 colorLUT(不仅仅是其索引),以便你可以将另一个渲染器映射到相同的颜色方案。请注意,这些只是辅助函数,你确实可以使用 getters.labelmaps3D 获取所有 Labelmap3D 对象,并根据需要提取这些信息。
后门
请注意,除了从 API 之外,你始终可以访问全局序列对象:
const { state } = getModule('segmentation');
const series = state.series;
但是,只有在你知道自己在做什么的情况下才能修改这些内容,因为你可能会通过不干净地删除/修改内容来破坏你的应用程序。
本系列文章持续更新中,如果你觉得有帮助到你,请点赞收藏分享,感谢!