cornerstone 基础概念篇(一)

本文根据官网 https://docs.cornerstonejs.org/ 翻译,加上了自己的一些理解

cornerstone 是一个基于web的较全面的影像文件处理组件,使用的是MIT License。 cornerstone 拥有多个组件,如: cornerstone-core cornerstoneTools cornerstone-nifti-image-loader 等等,要想灵活的使用cornerstone,必须先了解cornerstone-core这个组件,它是一个比较轻量的js 库,提供了影像处理的核心类和一些框架代码。

安装

  • 方式1:引入
<script src="/path/to/cornerstone.js"></script>
  • 方式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, 在这个页面元素里展示可交互的影像文件。 具体步骤如下:

  1. 引入cornerstone-core 和 image-loader。
  2. 建一个div, 并给个ID
  3. 通过CSS给这个div 设置好宽高、位置等
  4. 使用cornerstone.enable(element) , 这个API会做一些展示前的准备工作
  5. 使用cornerstone.loadImage(imageId),这个API会加载影像文件(imageId解释见下文)
  6. 在loadImage 后使用cornerstone.displayImage() 展示影像

以下示例代码帮助理解

<body>
...
  <div id='dicomImage'></div>
...
<body>
<script>
import cornerstone from "cornerstone-core";
import * as imageLoader from "cornerstone-nifti-image-loader";

let element = document.getElementById('dicomImage');
let imageId = 'example://1';
cornerstone.enable(element);
cornerstone.loadImage(imageId).then(function(image) {
  cornerstone.displayImage(element, image);
  ...
})
</script>

例子中<div id='dicomImage'></div> 就是一个Enabled Element

Image Ids

Image Id 是一个标记图像的自定义url地址,具体格式如下:

地址说明.png

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);
  ...
})
</script>

当我们的代码调用 cornerstone.loadImage(imageId) 方法时,cornerstone-core首先会根据imageId中的scheme (例子中就是example)找到对应的 image loader 函数,然后执行这个函数返回promise对象,这个promise对象会携带影像文件的像素数据。通过这种方式,cornerstone就可以灵活的扩展不同方式获取影像数据方法,如: wado-uri、wado-rs、qido-rs、stow-rs、nifiti 等。下图是官网给的流程图,参考理解:

image load 流程

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,剩下的概念下篇继续。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容