cornerstoneTools模块 corner module

cornerstoneTools模块 corner module

编码文章call10242024-12-17 11:43:4822A+A-

模块是 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;

但是,只有在你知道自己在做什么的情况下才能修改这些内容,因为你可能会通过不干净地删除/修改内容来破坏你的应用程序。

本系列文章持续更新中,如果你觉得有帮助到你,请点赞收藏分享,感谢!

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4