本文根据官网 https://docs.cornerstonejs.org/ 翻译,加上了自己的一些理解
cornerstone 是一个基于web的较全面的影像文件处理组件,使用的是MIT License。 cornerstone 拥有多个组件,如: cornerstone-core cornerstoneTools cornerstone-nifti-image-loader 等等,要想灵活的使用cornerstone,必须先了解cornerstone-core这个组件,它是一个比较轻量的js 库,提供了影像处理的核心类和一些框架代码。
安装
- 方式1:引入
- 方式2:npm安装
npm install cornerstone-core --save
- 方式3: 编译
git clone https://github.com/cornerstonejs/cornerstone.git node_modules/cornerstone
cd node_modules/cornerstone
npm install
npm run build
概念解释
要想弄清cornerstone, 必须先弄清楚它定义一些概念,这样才能事半功倍。
- Enabled Elements
- Image Ids
- Image Loaders
- Viewport
- Images
- Pixel Coordinate System
- Rendering Loop
- Libraries
- Rendering Pipeline
- Metadata Providers
Enabled Elements
可用页面元素,典型就是使用div, 在这个页面元素里展示可交互的影像文件。 具体步骤如下:
- 引入cornerstone-core 和 image-loader。
- 建一个div, 并给个ID
- 通过CSS给这个div 设置好宽高、位置等
- 使用cornerstone.enable(element) , 这个API会做一些展示前的准备工作
- 使用cornerstone.loadImage(imageId),这个API会加载影像文件(imageId解释见下文)
- 在loadImage 后使用cornerstone.displayImage() 展示影像
以下示例代码帮助理解
...
...
例子中 就是一个Enabled Element
Image Ids
Image Id 是一个标记图像的自定义url地址,具体格式如下:
cornerstone-core只定义了Image Id的结构,具体的内容是由各自的image loader实现的。
- scheme name: 决定使用什么image loader 来加载图像, 在注册image loader时指定;比如niftiImageLoader 首先调用 registerImageLoader API进行注册:
cornerstone.registerImageLoader('nifti', (imageId) => this.cornerstoneLoader(imageId, this));
这样,当调用cornerstone.loadImage(imageId)加载影像时,先会根据imageId的scheme name选择image loader, 如果是 nifti:xxx
, 则就会使用niftiImageLoader 。
- hierarchical: 英文翻译过来是层次结构的意思,就是路径的层次,类比http url, 如:
aaa/bbb/ccc
这样的多层路径, 当然这里其实是没有什么限制的,具体什么值完全取决于image loader自己的实现。 - query、#fragment : 类比http url 中 参数和锚点,具体什么值完全取决于image loader自己的实现。
Image Loaders
Image Loader 其实就是一个定义好的函数 , 形式如下:
function loadImage(imageId) {
...
const promise = new Promise((resolve, reject) => {});
return {promise};
}
回顾 Enabled Elements中的示例, 我们的代码调用加载影像文件时是这样写的
...
let element = document.getElementById('dicomImage');
let imageId = 'example://1';
cornerstone.enable(element);
cornerstone.loadImage(imageId).then(function(image) {
cornerstone.displayImage(element, image);
...
})
当我们的代码调用 cornerstone.loadImage(imageId) 方法时,cornerstone-core首先会根据imageId中的scheme (例子中就是example
)找到对应的 image loader 函数,然后执行这个函数返回promise对象,这个promise对象会携带影像文件的像素数据。通过这种方式,cornerstone就可以灵活的扩展不同方式获取影像数据方法,如: wado-uri、wado-rs、qido-rs、stow-rs、nifiti 等。下图是官网给的流程图,参考理解:
Viewport
Viewport 直译是视口,可以理解为图像的显示区域,窗口。 这里的Viewport是指窗口对象,包含了一些设置参数。
以下是对LUT知识扩展,不感兴趣可直接略过。
DICOM医学图像显示转换过程需要经过Modality LUT、VOI LUT、Presentation LUT三个转换过程,最终输出的P Values才是可以直接显示的图像数据
Modality LUT转换(数据规范化转换通常不同生产厂商的设备很难保证在一种设备上生成的图像和其他生产厂商的同类型设备上生成的图像在度量上是一致的,为此就需要将不同设备厂家产生的图像的原始数据转换到一个标准的度量空间,转换就是完成这个功能的。医疗设备的生产厂商都会在自己的图像中采用DICOM标准规定的格式说明如何将自己的数据转换为标准图像数据,中规定可以使用通过查找表(Look Up Table,简称LUn查找和通过斜率/截距(RescaIe/转换两种方法中的一种。
VOl(Value Of Interest)LUT转换(感兴趣区转换,由于医学图像数据动态范围大(像素深度通常不低于4096个灰度级),因此一般显示器很难提供如此高的动态范围一次显示整幅图像的全部信息细节,在图像的处理中一般都是先选择一个操作者感兴趣的区域,然后将该区域的图像信息映射到显示器能显示的整个数据范围,这样就增加了该区域的图像信息的对比度。这个过程DICOM标准中称之为感兴趣区简称VOI)LUT(Look Up Table)转换。临床医生感兴趣的窗宽、窗位调节功能就是VOI LUT转换的一种算法实现。VOI LUT转换可以使用设置窗宽、窗位的线性转换算法和通过查找查找表(LUT)转换的非线性算法两种算法中的一种,且只能使用其中的一种,具体使用哪种算法在DICOM文件中有专门的标记来设置。
Presentation LUT转换是对图像像素要做的最后一个变换,它用于特定图像的显示。这一模块转换完成后的输出值为P—Values,P-Values是独立于任何显示设备的特性曲线,与人的视觉反应近似相关的值,可直接作为已经校正的软拷贝设备或硬拷贝的输入。转换也有两种转换方法,一种是通过Presentation LUT进行转换的非线性转换方法,一种是通过Presentation Shape的转换方法,这两种转换方法只能使用一种。Presentation LUT转换的过程基本同上面介绍的两种LUT算法。
以上摘抄自 DICOM医学图像显示算法改进与实现——LUT
下面简单示例了设置viewport后的效果
const viewport = cornerstone.getDefaultViewportForImage(element, image);
viewport.scale = 0.5;
viewport.translation = {x:100,y:0}; // 左移100
viewport.invert = true; // 灰度倒置
// cornerstone.setViewport(element,viewport)
cornerstone.displayImage(element, image, viewport);
// 打开resize后,会根据图片自动调整图像,上面的scale、translation 会失效
// cornerstone.resize(element, true);
可以看出图片的灰度、大小、位置都发生了改变。
本篇介绍了Enabled Elements、Image Ids、Image Loaders、Viewport,剩下的概念下篇继续。