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

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类型

  • Cornerstone WADO Image Loader:支持DICOM第10部分图像;支持WADO-URI和WADO-RS;支持多帧DICOM实例;支持从文件对象读取DICOM文件

  • Cornerstone Web Image Loader: 支持 PNG and JPEG 文件

  • Cornerstone-nifti-image-loader:支持 NifTi格式文件

注册一个ImageLoader

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

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

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

@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显示。

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_第1张图片

总结回顾

ImageId

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_第2张图片

ImageLoader

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_第3张图片

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
}

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,
);

@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();


自定义加载顺序

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

大致的加载流程

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

  2. 实例化一个新的StreamingImageVolume

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

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

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

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

总结回顾

Volume

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_第4张图片

VolumeLoader

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_第5张图片

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);
  • 创建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);
  • 渲染视图
renderingEngine.renderViewports(['ctAxial','ctSagittal','ctStack']);

总结概述

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_第6张图片

Viewport

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

分类

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

StackViewport

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

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

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

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,
};

设置初始化时的缩放

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

 imageArea: [0.5, 0.5],

设置初始化时的平移

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

imageCanvasPoint: {
  imagePoint: [0, 0.5], 
  canvasPoint: [0, 0.5], 
};

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

如何在实际应用中更改

在创建视图时进行初始化

renderingEngine.setViewports([{
    viewportId: 'ctAxial',
    type: ViewportType.ORTHOGRAPHIC,
    element: htmlElement1,
    defaultOptions: {
      orientation: Enums.OrientationAxis.AXIAL,
      displayArea:{
        // 需要更改的配置项
      }
    },
}]);

总结概述

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_第7张图片

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 };

在项目中注册使用

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

  cornerstone.metaData.addProvider(
    ptScalingMetaDataProvider.get.bind(ptScalingMetaDataProvider),
    10000,
  );

优先级

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

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

总结概述

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)_第8张图片

你可能感兴趣的:(CornerStone3D,cornerstone3D,医学影像,前端)