当前位置:   article > 正文

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_cornerstone 标尺

cornerstone 标尺

Cornerstone3D介绍

Cornerstone3D是一个专门为处理三维医学影像而设计的JavaScript库

它是Cornerstone项目的一部分,旨在为医学影像社区提供高性能、可扩展且易于使用的开源Web工具,专注于提供交互式的3D医学图像浏览体验,适用于多种医学影像格式。

特性

  • 健壮的DICOM解析:能够处理和显示各种3D医学影像格式,如CT、MRI和PET扫描等,支持Dicom格式、NifTi格式的影像加载

  • 高性能渲染:使用WebGL进行图像渲染、使用多线程进行图像编码,优化了图像的加载和显示速度,从而提供了流畅的用户体验

  • 模块化设计:设计了灵活的架构,允许开发者扩展自己的工具和定制功能,以适应特定的医学影像应用需求。

版本对比

Cornerstone3D 和 Cornerstone 版本对比,主要在图像处理、渲染性能上进行了提升。2D版本无法更好的支持复杂场景的使用,例如多平面重建、Series融合等等。以下是从不同场景进行的对比:

Cornerstone(2D版本)

  • 图像处理: 专注于处理和展示二维医学影像数据

  • 渲染性能:针对2D图像优化,提供高效的加载和显示性能。

  • 工具和功能:包括基本的图像操作工具,如缩放、平移、旋转、亮度/对比度调节,以及医学影像中常用的注释和测量工具。

Cornerstone3D(3D版本)

  • 图像处理:能够处理和展示三维医学影像数据,如从CT或MRI扫描得到的体积数据集。

  • 渲染性能:使用WebGL等技术进行3D渲染,优化了处理和显示大型体积数据集的能力。

  • 工具和功能:除了包含2D版本的基本功能外,还提供3D特有的功能,如多平面重建(MPR)、体积渲染、图像分割等等。

依赖项

vtk.js

vtk.js是一个开源的javascript库,用于3D计算机图形,图像处理和可视化。Cornerstone3D的渲染引擎被设计为使用vtk.js进行3D渲染

浏览器支持

Cornerstone3D使用HTML5 canvas元素和WebGL 2.0 GPU渲染来渲染所有现代浏览器都支持的图像。但是并不是所有浏览器都支持volume streaming(使用SharedArrayBuffer创建共享内存)等高级功能。

  • Chrome > 68

  • Firefox > 79

  • Edge > 79

SharedArrayBuffer

需要重点关注 SharedArrayBuffer 问题,如果项目是部署在非安全上下文的环境中,SharedArrayBuffer 默认是不支持的,需要设置跨域隔离。

作用范围

原文:https://www.cornerstonejs.org/docs/getting-started/scope

Cornerstone3D是一个Javascript库,它利用纯粹的网络标准实现医学图像的3D渲染。该库在可能的情况下采用WebGL进行GPU加速渲染。Cornerstone3DTools是Cornerstone3D的同级库,包含了多种操控和注释工具,用于与图像进行交互。

Cornerstone3D的范围 并不包括 处理图像/体积的加载和元数据解析,只作用于图像渲染和缓存。应该使用imageLoader.registerImageLoader和volumeLoader.registerVolumeLoader注册到Cornerstone3D来使用图像/体积的请求加载。

在Cornerstone3D中,发布了第一个volumeLoader,streaming-image-volume-loader,它能够逐个流式传输体积图像。

总结回顾

在这里插入图片描述

ImageId及ImageLoader

ImageId

Cornerstone3D中的ImageId是一个用于识别单个图像以供Cornerstone显示的URL。该URL被Cornerstone用来确定调用哪个图像加载器插件来实际加载图像。

值得注意的是,Cornerstone3D将图像的加载工作委托给已注册的图像加载器。

这种策略允许Cornerstone同时显示从不同服务器通过不同协议获取的多个图像。

Image Loader

ImageLoader是一个JavaScript函数,负责接收ImageId并返回一个图像对象。由于加载图像通常需要调用服务器,因此图像加载的API需要是异步的。Cornerstone要求Image Loaders返回一个包含Promise的对象,Cornerstone将使用这个Promise异步接收图像对象,或者在发生错误时接收一个错误信息

支持的Loader类型

注册一个ImageLoader

使用registerImageLoader注册指定协议的加载器,以下示例表示:wadouri 协议的图像使用 cornerstoneDICOMImageLoader加载器加载

cornerstone.imageLoader.registerImageLoader('wadouri', cornerstoneDICOMImageLoader.wadouri.loadImage);

cornerstoneDICOMImageLoader.wadouri.register(cornerstone);  
cornerstoneDICOMImageLoader.external.cornerstone = cornerstone;
cornerstoneDICOMImageLoader.external.dicomParser = dicomParser;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

@cornerstonejs/dicom-image-loader

为Cornerstone3D库提供了一个DICOM图像加载器。这是[cornerstoneDICOMImageLoader]的后继产品,支持在3D库中使用。主要提供了以下新增特性:

  • Typescript支持(及类型定义)

  • 更佳的开发体验(例如,单一仓库、代码规范检查等)

主要特点为:

  • 实现了一个通过HTTP GET请求加载DICOM实例的Cornerstone图像加载器。

    • 可与WADO-URI服务器一起使用

    • 可与Orthanc的文件终端一起使用

    • 可与任何通过HTTP GET返回DICOM P10实例的服务器一起使用

  • 实现了一个用于WADO-RS(DICOMWeb)的Cornerstone图像加载器

  • 支持许多流行的传输语法和光度解释

  • 动态地利用WebAssembly(WASM)构建每个编解码器,显著提高图像解码性能,并使我们能够在需要时动态加载编解码器,从而减少构建时间和复杂性

  • 用于在Web Workers中执行CPU密集型任务的框架

    • 用于图像解码

    • 用于CPU密集型任务(例如,图像处理)

图像加载流程

  1. ImageLoader使用registerImageLoader API在Cornerstone中注册,以加载特定的ImageId URL方案

  2. 应用程序使用loadImage API来加载堆栈中的图像,或者使用createAndCacheVolume API来加载体积数据。

  3. Cornerstone将加载图像的请求委托给已注册相应ImageId URL方案的ImageLoader。

  4. ImageLoader将返回一个包含Promise的图像加载对象,

  5. 一旦获取到像素数据,它将用相应的图像对象解析这个Promise。获取像素数据可能需要通过XMLHttpRequest调用远程服务器,对像素数据进行解压(例如,来自JPEG 2000的数据),并将像素数据转换成Cornerstone能够理解的格式(例如,RGB与YBR颜色)。通过解析Promise返回的图像对象,随后将通过renderingEngine API显示。

在这里插入图片描述

总结回顾

ImageId

在这里插入图片描述

ImageLoader

在这里插入图片描述

Volume及VolumeLoader

Volume

Volume是一个具有空间物理大小和方向的3D数据阵列

它可以通过组合3D成像系列的像素数据元数据来构建,或者可以由应用程序从头定义。一个Volume拥有FrameOfReferenceUID、体素间距(x, y, z)、体素尺寸(x, y, z)、原点和方向向量,这些特征唯一地定义了它相对于患者坐标系统的坐标系统

ImageVolume

在Cornerstone3D中,使用ImageVolume基类来表示3D图像体积。所有的Volume都是从这个类派生的。例如StreamingImageVolume,它被用来表示一个图像被流式传输的Volume。

所以所有的Volume都实现了以下这个接口:

interface IImageVolume {
  /** 【缓存中Volume的唯一标识符】unique identifier of the volume in the cache */
  readonly volumeId: string
  /** volume dimensions */
  dimensions: Point3
  /** volume direction */
  direction: Float32Array
  /** volume metadata */
  metadata: Metadata
  /** volume origin - set to the imagePositionPatient of the last image in the volume */
  origin: Point3
  /** volume scalar data */
  scalarData: any
  /** volume scaling metadata */
  scaling?: {
    PET?: {
      SUVlbmFactor?: number
      SUVbsaFactor?: number
      suvbwToSuvlbm?: number
      suvbwToSuvbsa?: number
    }
  }
  /** volume size in bytes */
  sizeInBytes?: number
  /** volume spacing */
  spacing: Point3
  /** number of voxels in the volume */
  numVoxels: number
  /** volume image data as vtkImageData */
  imageData?: vtkImageData
  /** openGL texture for the volume */
  vtkOpenGLTexture: any
  /** loading status object for the volume containing loaded/loading statuses */
  loadStatus?: Record<string, any>
  /** imageIds of the volume (if it is built of separate imageIds) */
  imageIds?: Array<string>
  /** volume referencedVolumeId (if it is derived from another volume) */
  referencedVolumeId?: string // if volume is derived from another volume
  /** method to convert the volume data in the volume cache, to separate images in the image cache */
  convertToCornerstoneImage?: (
    imageId: string,
    imageIdIndex: number
  ) => IImageLoadObject
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

Volume Loader

与ImageLoader类似,VolumeLoader接收一个Volume ID和加载Volume所需的其他信息,并返回一个解析为体积的Promise。

这个Volume可以由一组2D图像(例如,imageIds)构建,也可以由一个3D数组对象(如NIFTI格式)构建。我们添加了cornerstoneStreamingImageVolumeLoader库来支持将2D图像(imageIds)流式传输成3D体积。

注册一个Volume Loader

使用registerVolumeLoader来定义一个Volume Loader

import {
  cornerstoneStreamingImageVolumeLoader,
  cornerstoneStreamingDynamicImageVolumeLoader,
} from '@cornerstonejs/streaming-image-volume-loader';

 // 注册体积加载器 => 当 CornerstoneJS 需要加载一个类型为 'cornerstoneStreamingImageVolume' 的体积数据时,它将会使用这个加载器。
  volumeLoader.registerVolumeLoader(
  'cornerstoneStreamingImageVolume',
  cornerstoneStreamingImageVolumeLoader,
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

@cornerstonejs/streaming-image-volume-loader

从图片中创建Volume

由于在StreamingImageVolume中3D Volume是由2D图像组成的,它的体积元数据来源于2D图像的元数据,所以这个loader需要在初始时调用来获取图像元数据。这样做不仅可以在内存中预分配和缓存Volume,还可以在加载2D图像时渲染Volume(渐进式加载)

通过预先从所有图像(imageId)中获取元数据,不需要为每个imageId创建Image对象,将图像的pixelData直接插入到正确位置的volume中即可,这样保证了速度和内存效率。

Volume与Image之间的转换

StreamingImageVolume基于一系列获取的图像(2D)加载Volume,Volume可以实现将其3D像素数据转换为2D图像的功能,而无需通过网络重新请求它们。

同样的,如果一组imageid具有Volume的属性(相同的FromOfReference, origin, dimension, direction和pixelSpacing),那么Cornerstone3D可以从一组imageid创建一个Volume。

使用streaming-image-volume-loader

const imageIds = ['wadors:imageId1', 'wadors:imageId2'];

const ctVolumeId = 'cornerstoneStreamingImageVolume:CT_VOLUME';

const ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, {
  imageIds: ctImageIds,
});

await ctVolume.load();


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

自定义加载顺序

由于Volume的创建和缓存(createAndCacheVolume)与图像数据(load)的加载是分离的。所以支持以任何顺序加载图像,以及重新排序图像请求以正确顺序加载图像的能力。

大致的加载流程

  1. 根据一组imageIds,计算Volume的元数据,如间距、原点、方向等。

  2. 实例化一个新的StreamingImageVolume

  3. StreamingImageVolume实现了加载方法(.load)

    1. 通过使用imageLoadPoolManager来实现加载请求

    2. 每个加载的帧(imageId)被放置在3D体积中的正确切片位置

    3. 返回一个体积加载对象,该对象包含一个解析为体积的promise。

总结回顾

Volume

在这里插入图片描述

VolumeLoader

在这里插入图片描述

RenderingEngine

RenderingEngine允许用户创建Viewports,将这些viewport与屏幕上的 HTML 元素关联,并使用WebGL 画布将数据渲染到这些元素上。

值得注意的是,RenderingEngine 能够渲染多个viewport,而无需创建多个引擎。

在Cornerstone3D 中,从零开始构建了 RenderingEngine,并使用 vtk.js 作为渲染的支撑,vtk.js 是一个 3D 渲染库,能够利用 WebGL 进行 GPU 加速渲染。

特性

1. 渲染优化

在 Cornerstone(2D)中,每个viewport都使用 WebGL 画布处理数据。随着viewport数量的增加,尤其在复杂的影像应用场景中(例如,同步视窗),因为会导致屏幕上画布的大量更新,以及随着视窗数量增加而性能下降。

在 Cornerstone3D 中,在屏幕外处理数据。这意味着我们有一个大型的不可见画布(离屏),它内部包含了所有屏幕上的画布。当用户操纵数据时,离屏画布中相应的像素会被更新,在渲染时,将数据从离屏画布复制到每个视窗的屏幕上画布。由于复制过程比重新渲染每个视窗上的操纵更快,因此解决了性能下降的问题。

2. 共享Volume Mapper

vtk.js 提供了用于渲染的标准渲染功能。此外,在 Cornerstone3D 中,引入了共享体积映射器(Shared Volume Mappers),以便在任何可能需要的视窗中重用数据,而无需复制数据。

使用

  • 初始化一个renderingEngine
import { RenderingEngine } from '@cornerstonejs/core';

const renderingEngineId = 'myEngine';
const renderingEngine = new RenderingEngine(renderingEngineId);
  • 1
  • 2
  • 3
  • 4
  • 创建viewport并绑定视图
const viewportInput = [
  // CT Volume Viewport - Axial
  {
    viewportId: 'ctAxial',
    type: ViewportType.ORTHOGRAPHIC,
    element: htmlElement1,
    defaultOptions: {
      orientation: Enums.OrientationAxis.AXIAL,
    },
  },
  // CT Volume Viewport - Sagittal
  {
    viewportId: 'ctSagittal',
    type: ViewportType.ORTHOGRAPHIC,
    element: htmlElement2,
    defaultOptions: {
      orientation: Enums.OrientationAxis.SAGITTAL,
    },
  },
  // CT Axial Stack Viewport
  {
    viewportId: 'ctStack',
    type: ViewportType.STACK,
    element: htmlElement3,
    defaultOptions: {
      orientation: Enums.OrientationAxis.AXIAL,
    },
  },
];

renderingEngine.setViewports(viewportInput);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 渲染视图
renderingEngine.renderViewports(['ctAxial','ctSagittal','ctStack']);
  • 1

总结概述

在这里插入图片描述

Viewport

在 Cornerstone3D 中,viewport是由 HTML 元素创建的,我们需要提供用于创建viewport的元素。然后进行初始化绑定

分类

viewport主要分为 2D栈视图、Volume体视图、3D视图,根据不同的渲染需求进行选择。无论是哪一种视图类型,都是通过 RenderingEngine API 进行创建

StackViewport

  • 适用于呈现一堆图像,这些图像可能属于也可能不属于同一图像。

  • Stack可以包含各种形状、大小和方向的2D图像

const viewport = renderingEngine.getViewport('stackId');
await viewport.setStack(imageIds);
  • 1
  • 2

VolumeViewport

  • 适合于渲染被认为是一个3D图像的体积数据。

  • 使用VolumeViewport可以通过设计实现多平面重组或重建(MPR),可以从不同的方向进行体积可视化,而不会增加性能成本。

  • 用于两个series之间的图像融合

3D Viewport

  • 适用于实际的三维立体数据渲染。

  • 有不同类型的预设,如骨,软组织,肺等。

初始化

所有的Viewport都继承自Viewport类,它提供了一个displayArea字段。该字段可用于以编程方式设置图像的初始缩放/平移。

默认情况下,视口将使dicom图像适合屏幕。displayArea字段支持以下配置内容:

type DisplayArea = {
  imageArea: [number, number], // areaX, areaY
  imageCanvasPoint: {
    imagePoint: [number, number], // imageX, imageY
    canvasPoint: [number, number], // canvasX, canvasY
  },
  storeAsInitialCamera: boolean,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

设置初始化时的缩放

在初始化时,如果想要设置图像为200%,则设置如下

 imageArea: [0.5, 0.5],
  • 1

设置初始化时的平移

在初始化时,如果想要左对齐图像,则设置如下

imageCanvasPoint: {
  imagePoint: [0, 0.5], 
  canvasPoint: [0, 0.5], 
};
  • 1
  • 2
  • 3
  • 4

这意味着画布上的左(0)中间(0.5)点需要与图像上的左(0)中间(0.5)点对齐。数值基于完整图像的%大小。

如何在实际应用中更改

在创建视图时进行初始化

renderingEngine.setViewports([{
    viewportId: 'ctAxial',
    type: ViewportType.ORTHOGRAPHIC,
    element: htmlElement1,
    defaultOptions: {
      orientation: Enums.OrientationAxis.AXIAL,
      displayArea:{
        // 需要更改的配置项
      }
    },
}]);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

总结概述

在这里插入图片描述

MetaData

医学影像通常附带大量非像素级的元数据,例如图像的像素间距、患者 ID 或扫描获取日期等等。对于某些文件类型(例如 DICOM),这些信息存储在文件头中,可以被读取、解析并在应用程序中传递。而对于其他类型(例如 JPEG、PNG),这些信息需要独立于实际像素数据提供。

元数据提供器是一个 JavaScript 函数,作为访问 Cornerstone 中与图像相关元数据的接口。用户也可以定义自己的提供器函数,以返回他们希望的每个特定图像的任何元数据。

自定义元数据提供器

提供器需要实现一个get函数,该函数接收一个 type 和 ImageId, 返回当前ImageId中业务需要的数据

function addInstance(imageId, scalingMetaData) {
  const imageURI = csUtils.imageIdToURI(imageId);
  scalingPerImageId[imageURI] = scalingMetaData;
}

function get(type, imageId) {
  if (type === 'scalingModule') {
    const imageURI = csUtils.imageIdToURI(imageId);
    return scalingPerImageId[imageURI];
  }
}

export default { addInstance, get };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在项目中注册使用

通过addProvider函数,在CornerStone中添加提供器

  cornerstone.metaData.addProvider(
    ptScalingMetaDataProvider.get.bind(ptScalingMetaDataProvider),
    10000,
  );
  • 1
  • 2
  • 3
  • 4

优先级

由于可以注册多个元数据提供器,因此在添加提供器时,可以为其定义一个优先级数字。

当需要请求元数据时,Cornerstone 将按照提供器的优先级顺序请求图像的元数据(如果提供器对于图像 ID 返回未定义,则 Cornerstone 将转向下一个提供器)。例如,如果 provider1 注册时优先级为 10,而 provider2 注册时优先级为 100,则首先向 provider2 请求图像 ID 的元数据。上面的示例中我们定义了一个优先级为10000的provider

总结概述

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/397654
推荐阅读
相关标签
  

闽ICP备14008679号