Cornerstone介绍
概述
Cornerstone,这是一个轻量级的 JavaScript 库,用于在支持 HTML5 canvas 元素的现代 Web 浏览器中显示医学图像。Cornerstone 本身并不是一个完整的应用程序,而是一个可以用作更大、更复杂的应用程序一部分的组件,需要搭配多个库进行配合,Cornerstone已经提供了一整套的生态系统。
依赖库
| 库名 | 作用 |
|---|---|
| cornerstone Core | 提供图像渲染、加载、缓存和视口转换的中央库 |
| cornerstone Tools | 用于帮助注释、分割和测量医学图像 |
| cornerstone WADO Image Loader | 用于通过 HTTP(WADO-URI)或 DICOMWeb(WADO-RS)的 DICOM P10 实例的图像加载器 |
| cornerstone Web Image Loader | 用于 web 图像(PNG、JPEG)的图像加载器 |
| dicom Parser | 用于在现代基于 HTML5 的 web 浏览器(IE10+)节点中解析 DICOM P10 字节流以及原始(未封装在第 10 部分中)字节流 |
| cornerstone Math | 数学和计算几何 |
| Hammer | 添加对触摸事件和手势的跨浏览器支持(移动端) |
特性
- 基于 HTML5/Javascript 的库,可以轻松地将交互式医学图像添加到 web 应用程序中
- 作为从企业查看器、报告查看器等构建更复杂医疗成像应用程序的基础
- 支持所有基于 HTML5 的浏览器,包括手机、平板电脑和台式机
- 显示所有常见的医学图像格式(例如 8 位灰度、16 位灰度、RGB 颜色)
- 高性能图像显示(例如通过 webworker)
- 通过图像加载器插件设计从具有不同协议的不同系统检索图像
- API 支持更改视口属性(例如 ww/wc、缩放、平移、反转)
- 支持 3D 成像
cornerstone Core使用
启用的元素
cornerstone.enable(HTMLElement)
加载影像
const imageId = 'wadouri:http://182.92.128.9/file-system-online/dicom/CT/chest_lung/1/1.dcm'
//下载影像
cornerstone.loadImage(imageId).then(image=>{})
或
//下载并缓存影像
cornerstone.loadAndCacheImage(imageId).then(image=>{})
image对象包含信息
| 属性名称 | 描述 | 值类型 |
|---|---|---|
| imageId | 与此图像对象关联的 imageId。 | String |
| minPixelValue | 图像中存储的最小像素值,一般来自 tag 信息(0028, 0106),如果未指定,则会根据 PixelData 重新计算。 | Number |
| maxPixelValue | 图像中存储的最大像素值,一般来自 tag 信息(0028, 0107),如果未指定,则会根据 PixelData 重新计算。 | Number |
| slope | 重新缩放斜率,将存储的像素值转换为模态像素值,一般来自 tag 信息(0028, 1053),如果未指定,则为 1。 | Number |
| intercept | 重缩放截距用于将存储的像素值转换为模态值,一般来自 tag 信息(0028, 1052),如果未指定,则为 0 | Number |
| windowCenter | 图像的默认窗位,一般来自 tag 信息(0028, 1050)。 | Number |
| windowWidth | 图像的默认窗宽,一般来自 tag 信息(0028, 1050)。 | Number |
| getPixelData | 底层像素数据的函数,返回灰度的整数数组和颜色的 RGBA 数组。 | Function |
| rows | 图像中的行数,一般指图像的高度,一般来自 tag 信息(0028, 0010)。 | Number |
| columns | 图像中的列数,一般指图像的宽度,一般来自 tag 信息(0028, 0011)。 | Number |
| height | 图像的高度,与 rows 同值。 | Number |
| width | 图像的宽度,与 columns 同值。 | Number |
| color | 是否为彩色图像,一般来自 tag 信息(0028, 0004),对值进行多项判断得到。 | Boolean |
| columnPixelSpacing | 垂直像素间距,一般来自 tag 信息(0028, 0030),如果未指定,则为 1 | Number |
| rowPixelSpacing | 水平像素间距,一般来自 tag 信息(0028, 0030),如果未指定,则为 1。 | Number |
| invert | 是否反转显示,一般来自 tag 信息(0028, 0004),值为 MONOCHROME1 则为 true,反之为 false。 | Boolean |
| sizeInBytes | 用于存储此图像像素的字节数。 | Number |
| stats | 上次重新绘制图像关于性能的统计信息,主要是一些耗时统计,例如:上一次获取像素数据的时间等。 | Object |
| cachedLut | 图像的缓存信息,主要是一些初始的信息,例如:原始窗宽窗位等。 | Object |
展示影像
cornerstone.displayImage(HTMLElement,image,viewport)
//image:由基石图像加载器加载的图像
//viewport:基石视口参数 可选
Image Ids
cornerstone Image Id 是标识基石要显示的单个图像的 URL。也是图像的唯一标识,所有的操作处理都是基于这个唯一标识进行识别。
Cornerstone 使用图像 Id 中的 URL 方案来确定要调用哪个图像加载程序插件来实际加载图像。该策略允许 Cornerstone 同时显示使用不同协议从不同服务器获得的多个图像。
imageLoader 通过识别 scheme name 选择加载器,常见 imageId:
// wadouri - HTTP GET
const wadouriImageId =
"wadouri:http://localhost:3333/wado?requestType=WADO&studyUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075541.1&seriesUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075541.2&objectUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075557.1&contentType=application%2Fdicom&transferSyntax=1.2.840.10008.1.2.1";
// dicomweb - HTTP GET
const dicomwebImageId =
"dicomweb:http://localhost:3333/wado?requestType=WADO&studyUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075541.1&seriesUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075541.2&objectUID=1.3.6.1.4.1.25403.166563008443.5076.20120418075557.1&contentType=application%2Fdicom&transferSyntax=1.2.840.10008.1.2.1";
// wadors - RESTful 风格 HTTP GET
const wadorsImageId =
"wadors:https://api.hackathon.siim.org/dicomweb/studies/1.3.6.1.4.1.14519.5.2.1.7777.9002.198875685720513246512710453733/series/1.3.6.1.4.1.14519.5.2.1.7777.9002.207203214132667549392101803048/instances/1.3.6.1.4.1.14519.5.2.1.7777.9002.327873213718058651550666129029/frames/1";
// dicomfile - 本地文件
const dicomfileImageId = "dicomfile:1";
viewport
每个启用的元素都有一个视口,用于描述渲染图像。
启用元素的视口参数可以通过getViewport()函数获取,并使用setViewport()函数进行设置。
// 获取当前的视口对象
const viewport = cornerstone.getViewport(element);
// 设置视口对象
cornerstone.setViewport(element, viewport);
viewport 对象包含信息:
| 属性名称 | 描述 | 值类型 |
|---|---|---|
| scale | 图像的缩放比例。等于 1 时不进行缩放,一个图像像素占用一个屏幕像素,大于 1 时将放大,小于 1 时缩小。 | Number |
| translation | 在像素坐标系中的平移距离,具有属性 x 和 y 的对象。 | Object |
| voi | 窗宽窗位,具有属性 windowWidth 和 windowCenter 的对象。 | Object |
| invert | 图像是否进行颜色反转。 | Boolean |
| pixelReplication | 放大图像时是否使用图像平滑 | Boolean |
| hflip | 图像是否水平翻转 | Boolean |
| vflip | 图像是否垂直翻转 | Boolean |
| rotation | 图像的旋转角度 | Number |
| modalityLUT | 应用的 modalityLUT | Array |
| voiLUT | 应用的 voiLUT | Array |
| colormap | 应用的伪彩色对象 | Object |
| labelmap | 将此图像渲染为 labelmap,直接跳过 modalityLUT 和 voiLUT | Boolean |
| displayedArea | 显示区域信息,具有属性 tlhc、brhc(显示区域位于启用元素的坐标)、columnPixelSpacing、rowPixelSpacing(像素间距)的对象。 | Object |
影像翻转
加载完成影像后,可以通过 viewport 视口中的 hflip 与 vflip 属性,对影像进行水平或者垂直方向的翻转。两个属性默认值均为 false ,也就是不进行翻转。
// 获取当前的视口对象
const element = document.getElementById("enabledElement");
const viewport = cornerstone.getViewport(element);
// 切换水平方向的翻转
viewport.hflip = !viewport.hflip;
// 切换垂直方向的翻转
viewport.vflip = !viewport.vflip;
// 设置新的视口
cornerstone.setViewport(element, viewport);
影像反色
在 viewport 视口中,可以通过 invert 属性进行影像的反色。默认值为 false ,不进行反色。
const element = document.getElementById("enabledElement");
const viewport = cornerstone.getViewport(element);
// 切换反色
viewport.invert = !viewport.invert;
// 设置新的视口
cornerstone.setViewport(element, viewport);
影像过渡采用平滑或差值
在影像进行缩放操作时,一般采用会采用平滑的方式,这样渲染出来的图像像素间的过渡会更加的细腻清晰。在 viewport 视口中,通过 pixelReplication 属性进行平滑与差值的切换。实际 pixelReplication 会应用到 canvas 的 imageSmoothingEnabled 属性(抗锯齿)上。默认值为 true ,采用平滑的方式。
const element = document.getElementById("enabledElement");
const viewport = cornerstone.getViewport(element);
// 切换平滑与差值
viewport.pixelReplication = !viewport.pixelReplication;
// 设置新的视口
cornerstone.setViewport(element, viewport);
影像缩放
在 viewport 视口中,通过 scale 属性进行缩放比例的调整。默认值为 cornerstone 自动计算后的适应当前 enabledElement 的尺寸,即铺满的状态的值。
const element = document.getElementById("enabledElement");
const viewport = cornerstone.getViewport(element);
// 调整缩放比例
// 原始大小,一个图像像素占一个屏幕像素
viewport.scale = 1;
// 放大两倍
viewport.scale = 2;
// 缩小两倍
viewport.scale = 0.5;
// 设置新的视口
cornerstone.setViewport(element, viewport);
影像旋转
在 viewport 视口中,通过 rotation 属性进行旋转的调整。默认值为 0,不进行旋转。
const element = document.getElementById("enabledElement");
const viewport = cornerstone.getViewport(element);
// 调整旋转角度
// 顺时针旋转90度
viewport.rotation = 90;
// 逆时针旋转90度
viewport.rotation = -90;
// 设置新的视口
cornerstone.setViewport(element, viewport);
影像自适应
在实际的使用过程中,在调整布局方式后,会影响 enabledElement 的尺寸,但是实际渲染的影像并没有进行相应的调整,导致出现了滚动条或者部分留白。在 cornerstone 中,可以通过 resize 方法不断调整实际影像的尺寸来解决这个问题。
const element = document.getElementById("enabledElement");
// 执行自适应方法
cornerstone.resize(element);
//注下面代码有个坑
<div class='box'>
<aside></aside>
<main></main>
</div>
.box{
display:flex;
aside{
width:200px;
height:100%;
}
main{
flex-grow:1; // 这个main里面放影像容器的话这个resize()无法生效,需要使用width:calc(100vw - 200px);
}
}
//目前解决方案就是这样
另外,当窗口变化时,可以通过监听 resize 事件,进行自动调整。
const element = document.getElementById("enabledElement");
// 通过监听 window 的 resize 事件,进行适应操作
window.addEventListener("resize", () => {
cornerstone.resize(element);
});
影像视口重置
当需要把调整的 viewport 视口恢复到初始状态,即重置影像,可以通过 reset 方法。
const element = document.getElementById("enabledElement");
// 执行重置方法
cornerstone.reset(element);
viewport 视口的重置,还可以通过设置默认视口的方式实现:
const element = document.getElementById("enabledElement");
// 获取已装载元素的对象
const enabledElement = cornerstone.getEnabledElement(element);
// 获取默认的视口对象赋值给当前的视口
enabledElement.viewport = cornerstone.getDefaultViewport(
enabledElement.canvas,
enabledElement.image
);
// 刷新影像
cornerstone.updateImage(element);
本地文件渲染
const element = document.getElementById("enabledElement");
const file = e.target.files[0]
const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(file)
cornerstone.loadAndCacheImage(imageId).then(image=>{
cornerstone.displayImage(image)
})
cornerstoneimagerendered事件
每当图像被cornerstone重新绘制时,都会发出CornerstoneImageRendered事件调用。 该事件包含可用于使用HTML5 canvas context(例如geometry或text)在图像上绘制的信息。您还可以使用各种viewport属性(如窗宽,窗位和zoom属性)更新HTML。
const element = document.getElementById("enabledElement");
element.addEventListener('cornerstoneimagerendered',(e)=>{})
cornerstoneviewportupdate事件
这个事件会在每次cornerstone影像的 Viewport 更新之后触发。
const element = document.getElementById("enabledElement");
element.addEventListener('cornerstoneviewportupdate',(e)=>{})
CornerstoneNewImage
每次在enable的element上显示新图像时,都会发出CornerstoneNewImage事件。 请注意,该事件后面总是跟着一个CornerstoneImageRendered事件。
事件属性:
frameRate - 基于自上次新图像渲染以来更新图像的帧率。 viewport - 此图像的当前视口属性 element - 为此图像启用的DOM元素 image - cornerstone影像的image对象 enabledElement - cornerstone内部使用的一个数据结构
CornerstoneImageLoaded
每次加载图像时都会发出CornerstoneImageLoaded事件。
事件属性:
image - 加载的image对象
CornerstoneImageLoadProgress
CornerstoneImageLoadProgress事件会在加载图像时发出。对此事件的支持取决于正在使用的image loader,因此是可选的,不保证支持。
活动属性:
imageId - 正在加载的imageId percentComplete - 加载的图像的百分比 loaded - 到目前为止加载的字节数 total - 要加载的总字节数
cornerstone.events.addEventListener(
'cornerstoneimageloadprogress',
function (event) {
const eventData = event.detail;
const progress = `Dicom加载: ${eventData.percentComplete}%`;
}
);
CornerstoneElementDisabled
当在已启用的element上调用disable()时,将发出CornerstoneElementDisabled事件。这个事件存在的目的是当一个element被disable的时候,可以通知清理外层DOM
事件属性:
element - 被禁用的DOM元素
下载影像
目前没有看到下载影像的函数
const element = document.getElementById("enabledElement");
const url = element.children[0].toDataURL()
const a = document.createElement('a')
a.href = url
a.download = '1.png'
a.style.display = 'none'
document.body.append(a)
a.click()
后续:这个保存API,cornerstoneTools提供了(SaveAs(element, filename))
//保存函数
//element:是一个HTML元素,表示要保存的图像所在的容器
//filename:是要保存的文件名
function save(){
cornerstoneTools.SaveAs(container,'1.png')
}
cornerstone Tools
概述
Cornerstone Tools 是一个 JavaScript 库,可帮助注释、分割和测量医学图像。该库还提供了一个框架,用于创建新工具、以一致、有凝聚力的方式管理所有工具以及导入/导出工具测量数据。
此库不是独立的。它建立在基石之上;一个符合标准、快速且可扩展的 JavaScript 库,可显示交互式医学图像
工具类型
Cornerstone Tools 所有的工具均为 class 类,都是通过继承 class 类的方式实现统一的集成。根据工具的使用方式主要分为三个父类:BaseTool、BaseAnnotationTool、BaseBrushTool。
Base Tool
BaseTool 是 Cornerstone Tools 中所有工具的顶级父级。它负责初始化工具的配置,应用 mixin,并为激活状态工具的鼠标/触摸交互提供 @virtual 函数。
继承于 BaseTool 类的工具类:
| 工具类名 | 工具名称 | 说明 |
|---|---|---|
| CrosshairsTool | 十字线 | 用于在与同步图像序列中的图像位置相对应的另一元素中查找切片的工具。 |
| DragProbeTool | 探针 | 拖动时在输入位置提供图像数据探测的工具,主要用于测量 CT 值。 |
| MagnifyTool | 放大镜 | 用于放大检查区域的工具。 |
| OrientationMarkersTool | 方向标记 | 用于在图像上显示方向标记的工具,激活后无需操作自动显示。 |
| PanTool | 平移 | 用于平移图像的工具。 |
| RotateTool | 旋转 | 用于旋转图像的工具。 |
| ScaleOverlayTool | 比例尺 | 用于在图像上显示比例覆盖的工具,激活后无需操作自动显示。 |
| WWWCTool | 调窗 | 通过拖动设置窗宽窗位的工具。 |
| ZoomTool | 缩放 | 用于调整缩放比例的工具。 |
| StackScrollTool | 浏览序列 | 用于滚动系列的工具。 |
| StackScrollMouseWheelTool | 鼠标滚轮浏览序列 | 使用鼠标滚轮滚动序列的工具。 |
| OverlayTool | 覆盖层 | 用于在图像上显示覆盖信息或图形的工具。 |
| ReferenceLinesTool | 参考线 | 用于显示其他已启用元素的参考线的工具。 |
| EraserTool | 橡皮擦 | 用于删除其他注释工具的数据的工具。 |
BaseAnnotationTool
BaseAnnotationTool 继承于 BaseTool ,扩展了部分方法,主要用于创建和操作注释数据。
继承于 BaseAnnotationTool 类的工具类:
| 工具类名 | 工具名称 | 说明 |
|---|---|---|
| AngleTool | 角度 | 通过放置三个连续点创建和定位角度。 |
| ArrowAnnotateTool | 箭头标注 | 创建并定位箭头和标签。 |
| BidirectionalTool | 双向 | 创建并定位测量区域长度和宽度的注释。 |
| CircleRoiTool | 圆形 | 用于绘制感兴趣的圆形区域并测量封闭像素的统计信息的工具。 |
| CobbAngleTool | cobb 角 | 用于测量两条直线之间的角度的工具。 |
| EllipticalRoiTool | 椭圆 | 用于绘制感兴趣的椭圆区域并测量封闭像素的统计信息的工具。 |
| FreehandRoiTool | 多边形 | 用于绘制任意多边形感兴趣区域并测量封闭像素统计信息的工具。 |
| LengthTool | 长度 | 测量距离的工具。 |
| ProbeTool | 探针 | 在所需位置提供图像数据探测的工具,与 DragProbeTool 使用方式一致,区别在于 ProbeTool 会保存测量数据。 |
| RectangleRoiTool | 矩形 | 用于绘制感兴趣的矩形区域并测量封闭像素的统计信息的工具。 |
| TextMarkerTool | 文字标注 | 用于使用文本标记注释图像的工具。 |
工具模式
工具的模式决定了如何渲染工具,以及如何与其交互。包含四种标准模式,分别是:
| 工具类名 | 说明 |
|---|---|
| Active | 激活模式的工具将渲染并响应用户输入,并且能够创建新的注释或测量。 |
| Passive | 被动模式的工具将渲染并被动响应用户输入,数据可以被操纵,但不能被创建。 |
| Enabled | 启用模式的工具将渲染,但不会响应输入,本质上是只读状态。 |
| Disabled | 工具的默认状态。禁用模式的工具无法交互,也不会在启用的元素上呈现。 |
事件
CornelstoneTools 事件将本地事件捕获,并进行规范化,并使用 cornerstonetools 前缀重新触发,这样在不同的浏览器中可以一致地处理事件。
| 事件分类 | 事件名称 | 说明 |
|---|---|---|
| 鼠标事件 | cornerstonetoolsmousedown | 鼠标按下时触发 |
| cornerstonetoolsmouseup | 鼠标抬起时触发 | |
| cornerstonetoolsmousedownactivate | 鼠标按下 ,并且 cornerstonetoolsmousedown 事件回调函数不使用 stopPropagation 时触发。此时触发 createNewMeasurement 函数。 | |
| cornerstonetoolsmousedrag | 鼠标拖拽时触发 | |
| cornerstonetoolsmousemove | 鼠标移动时触发 | |
| cornerstonetoolsmouseclick | 鼠标点击时触发 | |
| cornerstonetoolsmousedoubleclick | 鼠标双击时触发 | |
| cornerstonetoolsmousewheel | 鼠标滚轮滚动时触发 | |
| 键盘事件 | cornerstonetoolskeydown | 键盘按下时触发 |
| cornerstonetoolskeyup | 键盘抬起时触发 | |
| cornerstonetoolskeypress | 键盘按下生字符值的键时触发 | |
| cornerstonetoolsmeasurementadded | 增加测量数据时触发 | |
| cornerstonetoolsmeasurementmodified | 修改测量数据时触发 | |
| cornerstonetoolsmeasurementcompleted | 测量停止时触发 | |
| cornerstonetoolsmeasurementremoved | 移除测量数据时触发 | |
| 自定义事件 | cornerstonetoolstooldeactivated | 工具停用时触发,工具模式被修改为 passive |
| cornerstonetoolsclipstopped | 播放工具停止时触发 | |
| cornerstonetoolsstackscroll | scroll stack 图像时触发 | |
| cornerstonetoolsstackprefetchimageloaded | stack预加载图像完成时触发 | |
| cornerstonetoolsstackprefetchdone | 全部完成 stack 预加载时触发 | |
| cornersontetoolslabelmapmodified | labelmap 修改时触发 |
事件使用的方法
const element = document.getElementById("enabledElement");
element.addEventListener('cornerstonetoolsmousedoubleclick',(e)=>{})
全局配置项
在初始化 Cornerstone Tools 时,可以通过应用合理的默认值,启用/禁用不同的特性和功能。
cornerstoneTools.init({
// 当元素被启用时,是否监听鼠标事件
mouseEnabled: true,
// 当元素被启用时,是否监听触摸事件
touchEnabled: true,
// 全局工具同步
globalToolSyncEnabled: true,
// 显示svg光标
showSVGCursors: true,
// 自动调整视口大小
autoResizeViewports: true,
// 虚线样式
lineDash: [4, 4]
});
基本使用
使用 cornerstoneTools 需要提前初始化
// 引入 cornerstone.js 相关库
import cornerstone from "cornerstone-core";
import cornerstoneMath from "cornerstone-math";
import cornerstoneTools from "cornerstone-tools";
// cornerstoneTools 指定内部库
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
// 初始化 api
cornerstoneTools.init({
touchEnabled: false,
// 显示光标
showSVGCursors: true
});
版本号
const version = cornerestoneTools.version;
console.log(`当前 cornerstoneTools 的版本号为:${version}`);
添加或清除指定工具
添加指定工具
// 获取指定启用元素
const element = document.getElementById("enabledElement");
// 获取需要添加的工具
const ApiTool = cornerstoneTools.WwwcTool;
// 配置props,具体配置项需参考具体工具
const props = {
configuration: {
orientation: 1
}
};
// 给指定启用元素添加指定工具
cornerstoneTools.addToolForElement(element, ApiTool, props);
// 或者,直接给全部启用元素添加指定工具
cornerstoneTools.addTool(ApiTool, props);
清除指定工具
// 获取指定启用元素
const element = doucment.getElementById("enabledElement");
const toolName = "Wwwc";
// 给指定启用元素清除指定工具
cornerstoneTools.removeToolForElement(element, toolName);
// 或者,直接清除全部启用元素的指定工具
cornerstoneTools.removeTool(toolName);
获取已添加的指定工具
const element = document.getElementById("enabledElement");
const tools = cornerstoneTools.getToolForElement(element, "Wwwc");
修改工具的模式
工具共有四种模式:激活模式、启用模式、禁用模式和被动模式。工具添加后,默认为禁用模式,如果进行交互,则需要将工具模式调整为激活模式。
激活模式
const element = document.getElementById("enabledElement");
const toolName = "Wwwc";
/**
* options 配置如下:
* mouseButtonMask 指定鼠标按键:1-鼠标左键、2-鼠标右键、4-鼠标滚轮
*/
const options = {
mouseButtonMask: 1
};
// 给指定启用元素激活指定工具
cornerstoneTools.setToolActiveForElement(element, toolName, options);
// 或者,直接激活全部启用元素的指定工具
cornerstoneTools.setToolActive(toolName, options);
启用模式
const element = document.getElementById("enabledElement");
const toolName = "Wwwc";
// 给指定启用元素启用指定工具
cornerstoneTools.setToolEnabledForElement(element, toolName);
// 或者,直接启用全部启用元素的指定工具
cornerstoneTools.setToolEnabled(toolName, options);
禁用模式
const element = document.getElementById("enabledElement");
const toolName = "Wwwc";
// 给指定启用元素禁用指定工具
cornerstoneTools.setToolDisabledForElement(element, toolName);
// 或者,直接禁用全部启用元素的指定工具
cornerstoneTools.setToolDisabled(toolName, options);
被动模式
const element = document.getElementById("enabledElement");
const toolName = "Wwwc";
// 给指定启用元素被动指定工具
cornerstoneTools.setToolPassiveForElement(element, toolName);
// 或者,直接被动全部启用元素的指定工具
cornerstoneTools.setToolPassive(toolName, options);
校验指定启用元素指定工具是否为激活模式
const element = document.getElementById("enabledElement");
const toolName = "Wwwc";
const isActive = cornerstoneTools.isToolActiveForElement(element, toolName);
if (isActive) {
console.log("工具已激活");
}
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>最简单的例子</title>
<script src="https://unpkg.com/cornerstone-core@2.6.1/dist/cornerstone.js"></script>
<script src="https://unpkg.com/cornerstone-tools@6.0.8/dist/cornerstoneTools.js"></script>
<script src="https://unpkg.com/cornerstone-wado-image-loader@4.1.5/dist/cornerstoneWADOImageLoader.bundle.min.js"></script>
<script src="https://unpkg.com/dicom-parser@1.8.13/dist/dicomParser.js"></script>
<script src="https://unpkg.com/cornerstone-math@0.1.10/dist/cornerstoneMath.min.js"></script>
<style>
.container {
width: 100%;
height: 100%;
user-select: none;
}
.container #enabledElement {
width: 500px;
height: 500px;
margin-bottom: 10px;
}
.container #operation button {
margin-right: 5px;
font-size: 16px;
}
</style>
</head>
<body>
<div class="container">
<div id="enabledElement"></div>
<div id="operation">
<button data-name="Wwwc">Wwwc</button>
<button data-name="Zoom">Zoom</button>
<button data-name="Pan">Pan</button>
<button data-name="Rotate">Rotate</button>
</div>
</div>
<script>
cornerstoneTools.init({
touchEnabled: false,
showSVGCursors: true
});
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneWADOImageLoader.webWorkerManager.initialize({
maxWebWorkers: navigator.hardwareConcurrency || 1,
startWebWorkersOnDemand: true,
taskConfiguration: {
decodeTask: {
initializeCodecsOnStartup: false
}
},
webWorkerTaskPaths: [
"https://unpkg.com/cornerstone-wado-image-loader@4.1.5/dist/610.bundle.min.worker.js"
]
});
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
const element = document.getElementById("enabledElement");
cornerstone.enable(element);
const imageId =
"dicomweb:https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/1.dcm";
cornerstone.loadImage(imageId).then(function (image) {
cornerstone.displayImage(element, image);
toolInit();
setToolActive("Wwwc");
});
function toolInit() {
const tools = ["Wwwc", "Zoom", "Pan", "Rotate"];
tools.forEach((toolName) => {
addTool(toolName);
});
}
function addTool(toolName) {
const ApiTool = cornerstoneTools[`${toolName}Tool`];
cornerstoneTools.addTool(ApiTool);
}
function setToolActive(toolName, mouseButtonMask) {
const options = {
mouseButtonMask: mouseButtonMask || 1
};
cornerstoneTools.setToolActive(toolName, options);
}
function addEventListener() {
const element = document.getElementById("operation");
for (let i = 0; i < element.children.length; i++) {
const toolName = element.children[i].dataset.name;
element.children[i].addEventListener("click", () =>
setToolActive(toolName)
);
}
}
</script>
</body>
</html>
cornerstone Tools 基础工具配置
DragProbeTool 拖动探针
const props = {
configuration: {
// 是否开启阴影
shadow: false,
// 阴影颜色
shadowColor: "#000000",
// 阴影模糊程度
shadowBlur: 0,
// 阴影偏移量横坐标
shadowOffsetX: 1,
// 阴影偏移量纵坐标
shadowOffsetY: 1
}
};
MagnifyTool 放大镜
const props = {
configuration: {
// 放大将尺寸
magnifySize: 300,
// 放大镜放大倍数
magnificationLevel: 2
}
};
OrientationMarkersTool 方向标记
const props = {
configuration: {
// 是否显示全部方向标记,未开启则只显示上侧与左侧的方向标记
drawAllMarkers: true
}
};
RotateTool 旋转
const props = {
configuration: {
// 是否采用整数的角度,自动取整
roundAngles: false,
// 水平方向翻转角度
flipHorizontal: false,
// 垂直方向翻转角度
flipVertical: false,
// 角度的步长,此项调整旋转的鼠标的控制力度
rotateScale: 1
}
};
ScaleOverlayTool 比例尺
const props = {
configuration: {
// 次要刻度线的长度
minorTickLength: 12.5,
// 主要刻度线的长度
majorTickLength: 25,
// 是否开启阴影
shadow: false,
// 阴影颜色
shadowColor: "#000000",
// 阴影模糊程度
shadowBlur: 0,
// 阴影偏移量横坐标
shadowOffsetX: 1,
// 阴影偏移量纵坐标
shadowOffsetY: 1
}
};
WwwcRegionTool 框选调窗
const props = {
configuration: {
// 最小的窗宽数值
minWindowWidth: 10
}
};
WwwcTool 调窗
const props = {
configuration: {
// 0:窗宽横向调整、窗位纵向调整;1:窗宽纵向调整、窗位横向调整
orientation: 0
}
};
ZoomMouseWheelTool 鼠标滚轮缩放
const props = {
configuration: {
invert: false,
preventZoomOutsideImage: false,
minScale: 0.25,
maxScale: 20.0
}
};
测量工具的通用配置
/**
* 目前支持的工具:
* AngleTool、ArrowAnnotateTool、BidirectionalTool、CircleRoiTool、CobbAngleTool、
* EllipticalRoiTool、LengthTool、ProbeTool、RectangleRoiTool
*/
const props = {
configuration: {
// 在鼠标悬浮状态下是否显示控制柄,开启后鼠标未悬浮时则不显示控制柄,鼠标悬浮后显示控制柄
drawHandlesOnHover: false,
// 在移动时是否隐藏控制柄
hideHandlesIfMoving: false,
// 控制柄半径,默认为6
handleRadius: 6
}
};
/**
* 目前支持的工具:
* AngleTool、ArrowAnnotateTool、BidirectionalTool、CircleRoiTool、CobbAngleTool、
* FreehandRoiTool、EllipticalRoiTool、LengthTool、ProbeTool、RectangleRoiTool
*/
const props = {
configuration: {
// 测量线是否显示为虚线
renderDashed: false
}
};
/**
* 目前支持的工具:
* AngleTool、ArrowAnnotateTool、BidirectionalTool、CobbAngleTool、FreehandRoiTool、
* LengthTool、ProbeTool、RectangleRoiTool
*/
const props = {
configuration: {
// 是否显示控制柄
drawHandles: true
}
};
/**
* shadow 阴影相关的配置项,这些配置项与 css 中的 text-shadow 类似,只不过配置时不需要添加单位
* 目前支持的工具:
* AngleTool、ArrowAnnotateTool、BidirectionalTool、CircleRoiTool、CobbAngleTool、
* EllipticalRoiTool、LengthTool、RectangleRoiTool、TextMarkerTool
*/
const props = {
configuration: {
// 是否开启阴影
shadow: false,
// 阴影颜色
shadowColor: "#000000",
// 阴影模糊程度
shadowBlur: 0,
// 阴影偏移量横坐标
shadowOffsetX: 1,
// 阴影偏移量纵坐标
shadowOffsetY: 1
}
};
ArrowAnnotateTool 箭头注释
const getTextCallback = function (doneChangingTextCallback) {
const text = "要显示的注释";
// 需要将填写的注释在回调函数中传参
doneChangingTextCallback(text);
};
const changeTextCallback = function (
data,
eventData,
doneChangingTextCallback
) {
const text = "修改后的注释";
// 需要将修改后的注释在回调函数中传参
doneChangingTextCallback(text);
};
const props = {
configuration: {
// 测量时是否以箭头开始,关闭时则以箭头结束
arrowFirst: true,
// 注释是否可以为空,开启时未填写注释时工具不会消失
allowEmptyLabel: false,
// 填写注释后的回调函数,通过该函数调整填写注释的页面展示和填写后的注释
getTextCallback,
// 修改注释后的回调函数,通过该函数调整修改注释的页面展示和修改后的注释
changeTextCallback
}
};
CircleRoiTool 圆形测量
const props = {
configuration: {
// 中心操作点的半径
centerPointRadius: 0
}
};
EllipticalRoiTool 椭圆测量
const props = {
configuration: {
// 是否显示椭圆区域内最大与最小的 CT 值
showMinMax: false,
// 是否在 modality 为 CT 时,显示 CT 值的单位
showHounsfieldUnits: false
}
};
FreehandRoiTool 多边形测量
const props = {
configuration: {
// 控制柄的间隔
spacing: 1,
// 控制柄的半径
activeHandleRadius: 3,
// 闭环时的控制柄半径,指的是首尾相接的控制柄半径
completeHandleRadius: 6,
// 是否一直显示控制柄
alwaysShowHandles: false,
// 无效标注时展示的颜色
invalidColor: "crimson"
}
};
LengthTool 长度测量
const props = {
configuration: {
// 测量值保留小数位数
digits: 2
}
};
RectangleRoiTool 矩形测量
const props = {
configuration: {
// 是否显示矩形区域内最大与最小的 CT 值
showMinMax: false,
// 是否在 modality 为 CT 时,显示 CT 值的单位
showHounsfieldUnits: false
}
};
TextMarkerTool 文本标注
const doneChangingTextCallback = function (
data,
eventData,
doneChangingTextCallback
) {
const text = "更新后的文本标注";
// 需要将 data 与修改后的文本标注在回调函数中传参
doneChangingTextCallback(data, text);
};
const props = {
configuration: {
// 文本标注集合,标注的文本需要放入数组中
markers: [],
// 当前的标注
current: "",
// 是否正序切换 markers 中的标注
ascending: true,
// 是否循环 markers 中的标注
loop: false,
// 修改文本标注后的回调函数,通过该函数调整修改文本标注的页面展示和修改后的注释
doneChangingTextCallback
}
};
测量工具 props 配置项的使用
在项目初始化时,需要将测量工具全部添加,当需要使用时某一种测量工具时,需要激活该工具:
// 所有要用的测量工具均需要进行添加
const LengthTool = cornerstoneTools.LengthTool;
const lengthProps = {
configuration: {
drawHandlesOnHover: true,
hideHandlesIfMoving: true,
digits: 1
}
};
cornerstoneTools.addTool(LengthTool, lengthProps);
// 当用户选择要使用的测量工具后,激活改工具即可使用,每个鼠标按键只允许激活一种工具
cornerstoneTools.setToolActive("Length", { mouseButtonMask: 1 });
当需要清除图像上显示的测量标注时,需要先清空工具的 state 记录:
const element = document.getElementById("enabledElement");
cornerstoneTools.clearToolState(element, "Length");
// 刷新图像
cornerstone.updateImage(element);
当需要切换图像上的测量标注进行隐藏或者显示时:
const element = document.getElementById("enabledElement");
// 给指定的启用元素隐藏标注
cornerstoneTools.setToolDisabledForElement(element, "Length");
// 或者,直接将全部的启用元素隐藏标注
cornerstoneTools.setToolDisabled("Length");
// 刷新图像
cornerstone.updateImage(element);
// 给指定的启用元素显示标注
cornerstoneTools.setToolActiveForElement(element, "Length");
// 或者,直接将全部的启用元素显示标注
cornerstoneTools.setToolActive("Length");
// 刷新图像
cornerstone.updateImage(element);
测量工具 options 配置
options 配置项主要为标注数据操作与显示的区域的控制,该配置项不是在添加工具时配置,而需要单独调用 Api 进行配置。
const element = document.getElementById("enabledElement");
const options = {
deleteIfHandleOutsideImage: true,
preventHandleOutsideImage: false
};
// 给指定的启用元素添加工具配置
cornerstoneTools.setToolOptionsForElement(element, "Length", options);
// 或者,直接给全部的启用元素添加工具配置
cornerstoneTools.setToolOptions("Length", options);
cornerstone Tools 切换影像篇
cornerstoneTools 提供了两个切换影像的工具:StackScrollTool、StackScrollMouseWheelTool,分别通过鼠标按钮或鼠标滚轮操作实现切换图像
StackScrollTool
StackScrollTool 通过鼠标滑动,进行序列内影像快速滚动切换,并且支持配置循环与跳帧。
const element = document.getElementById("enabledElement");
// 为启用元素添加 stack 状态管理器
cornerstoneTools.addStackStateManager(element, ["stack"]);
const stack = {
currentImageIdIndex: 0,
// 需要切换的影像 id 集合
imageIds: [
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/1.dcm",
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/2.dcm",
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/3.dcm",
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/4.dcm",
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/5.dcm"
]
};
// 为启用元素添加 stack 工具状态
cornerstoneTools.addToolState(element, "stack", stack);
const StackScrollTool = cornerstoneTools.StackScrollTool;
const props = {
configuration: {
// 是否在序列内循环
loop: true,
// 是否跳帧
allowSkipping: true
}
};
cornerstoneTools.addTool(StackScrollTool, props);
cornerstoneTools.setToolActive("StackScroll", { mouseButtonMask: 1 });
StackScrollMouseWheelTool
StackScrollMouseWheelTool 通过鼠标滚轮滚动,进行序列内影像快速滚动切换,并且支持配置循环、跳帧与滚动方向。
const element = document.getElementById("enabledElement");
// 为启用元素添加 stack 状态管理器
cornerstoneTools.addStackStateManager(element, ["stack"]);
const stack = {
currentImageIdIndex: 0,
// 需要切换的影像 id 集合
imageIds: [
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/1.dcm",
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/2.dcm",
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/3.dcm",
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/4.dcm",
"https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/5.dcm"
]
};
// 为启用元素添加 stack 工具状态
cornerstoneTools.addToolState(element, "stack", stack);
const StackScrollMouseWheelTool = cornerstoneTools.StackScrollMouseWheelTool;
const props = {
configuration: {
// 是否在序列内循环
loop: true,
// 是否跳帧
allowSkipping: true,
// 倒转方向
invert: false
}
};
cornerstoneTools.addTool(StackScrollMouseWheelTool, props);
cornerstoneTools.setToolActive("StackScrollMouseWheel", { mouseButtonMask: 4 });
CrosshairsTool 十字线工具
CornerstoneTools 中的 CrosshairsTool 十字线工具是通过冠状面图像的坐标,寻找相对应的横断面切片位置图像的工具。Pacs 系统中相当于定位的功能,由冠状面(冠状位)的图像,定位显示相应的横断面(轴状位)图像。
工具使用过程
一般在用户使用的场景为:设置一行两列的序列布局,左侧显示冠状面图像,右侧显示横断面图像,通过鼠标选取左侧冠状面图像的坐标,右侧自动显示横断面图像。
我们在使用十字线工具前,需要注意的是,需要将整个横断面序列的图像全部 load 完成,因为十字线工具在确定切片时,会查询所有图像的元数据,如果这时对应横断面的图像并未 load 完成,则未生成元数据,将无法定位到最准确的图像。
定义图像同步器
// 新建图像更新同步器
const synchronizer = new cornerstoneTools.Synchronizer(
"cornerstonenewimage",
cornerstoneTools.updateImageSynchronizer
);
// 获取已绑定的元素
const coronalElement = document.querySelector(".canvasCoronal");
const axialElement = document.querySelector(".canvasAxial");
// 同步器添加需要同步的元素
synchronizer.add(coronalElement);
synchronizer.add(axialElement);
添加十字线工具与堆栈的状态
// 添加状态管理器
cornerstoneTools.addStackStateManager(axialElement, ["stack", "crosshairs"]);
// 堆栈的状态
const axialStack = {
// 当前横断面图像序列的索引
currentImageIdIndex: 0,
// 横断面图像序列的全部图像id集合
imageIds: [
"wadouri:https://example.com/1.dcm",
"wadouri:https://example.com/2.dcm"
]
};
// 此为关键:一定要在使用十字线工具前将全部横断面序列的图像加载完成
axialImageIds.forEach((imageId) => cornerstone.loadAndCacheImage(imageId));
// 添加堆栈状态
cornerstoneTools.addToolState(axialElement, "stack", axialStack);
添加和激活十字线工具
// 添加十字线工具,无 props 配置项
cornerstoneTools.addToolForElement(coronalElement, CrosshairsTool);
// 激活十字线工具
cornerstoneTools.setToolActiveForElement(coronalElement, "Crosshairs", {
mouseButtonMask: 1,
synchronizationContext: synchronizer
});
ReferenceLinesTool 参考线工具使用
CornerstoneTools 中的 ReferenceLinesTool 参考线工具(也叫定位线),可以在显示一幅切片影像的同时,在与该切片正交或斜交的定位图上显示定位线。这样就可以更加直观的表现出影像之间的空间位置关系,临床医生能够更准确的定位病灶的空间位置
工具使用过程
根据两种扫描各自的特征,CT 和 MR 采取不同的显示布局: CT 的定位影像通常只在一个序列中,而且数量较少,所以采取一个显示框内存放定位影像,其他显示框内存放切片影像,来相对定位影像画定位线的布局结构。 MR 则是任何影像都可能是切片影像或是定位影像,注重的是相互定位,除了当前影像,在每个显示框内的影像上都要画上定位线。
定义图像同步器
// 新建图像更新同步器
const synchronizer = new cornerstoneTools.Synchronizer(
"cornerstonenewimage",
cornerstoneTools.updateImageSynchronizer
);
// 获取已启用的元素
const firstElement = document.querySelector(".firstElement");
const secondElement = document.querySelector(".secondElement");
// 更多的已启用元素...
// 同步器添加需要同步的已启用元素
synchronizer.add(firstElement);
synchronizer.add(secondElement);
// 更多的已启用元素...
添加和激活参考线工具
添加参考线工具为了更加方便,不使用具体到已启用元素的 api :addToolForElement,使用可以为所有已启用元素添加工具的 api :addTool。 另外,只需要给需要显示参考线的启用元素装载参考线工具即可
// 添加参考线工具
cornerstoneTools.addTool(cornerstoneTools.ReferenceLinesTool, {
configuration: {
// 渲染参考线的方法,此处一般无需配置,框架已定义 renderActiveReferenceLine 方法可使用
renderer: renderActiveReferenceLine
}
});
// 为需要显示参考线的启用元素激活参考线工具
cornerstoneTools.setToolEnabledForElement(firstElement, "ReferenceLines", {
synchronizationContext: synchronizer
});
playClip(开始播放)
此函数可以自动播放影像
cornerstoneTools.playClip(HTMLElement,framesPerSecond)
//HTMLElement: 容器节点
//framesPerSecond: 播放帧
stopClip(暂停播放)
cornerstoneTools.stopClip(HTMLElement)
//HTMLElement:容器节点
cornerstone Web Image Loader
概述
Web图像(PNG,JPEG)的基石图像加载器。
PS:如果项目中.jpg格式的一定要把这个依赖引入进来,不然会报错
用法
只需在加载 Cornerstone 后将 cornerstoneWebImageLoader.js 包含在 HTML 文件中.js然后将 Cornerstone 实例设置为 cornerstoneWebImageLoader 的外部模块
//下载
yarn add cornerstone-web-image-loader
cornerstoneWebImageLoader.external.cornerstone = cornerstone;
const url = 'https://w.wallhaven.cc/full/1p/wallhaven-1ppld1.jpg';
cornerstone.enable(containerImage.value);
const res = await cornerstone
.loadImage(url)
.then((res) => res)
.catch((err) => console.log(err));
cornerstone.displayImage(containerImage.value, res);
arrayBufferToImage
arrayBufferToImage函数的作用是将二进制数据转换为Image对象。该函数接受一个ArrayBuffer对象作为参数,并返回一个Promise对象,该对象在Image对象创建完成后解析为该对象。搭配着createImage使用
cornerstoneWebImageLoader.arrayBufferToImage.then(res=>{
})
createImage
解析Img标签转换为Image对象,接收一个HTMLElement和一个imageId
PS:这里是我自己理解的
//这里的res参数是通过arrayBufferToImage获取的
const result = cornerstoneWebImageLoader.createImage(res,imageId)
//result:获取image对象
//imageId :imageId 可选
loadImage
loadImage函数的作用是从指定的URL加载图像数据,并返回一个Promise对象,该对象在图像加载完成后解析为一个Image对象
const res = await cornerstoneWebImageLoader.loadImage(
'https://w.wallhaven.cc/full/1p/wallhaven-1ppld1.jpg'
).promise;
configure
configure函数的作用是配置图像加载器的选项。它可以设置图像加载器的默认选项,例如是否启用缓存、缓存大小等。
import cornerstone from 'cornerstone-core';
import cornerstoneWebImageLoader from 'cornerstone-web-image-loader';
// 设置默认选项
cornerstoneWebImageLoader.configure({
maxWebWorkers: navigator.hardwareConcurrency || 1,
startWebWorkersOnDemand: true,
webWorkerPath: 'cornerstoneWADOImageLoaderWebWorker.js',
taskConfiguration: {
decodeTask: {
loadCodecsOnStartup: true,
initializeCodecsOnStartup: false,
codecsPath: 'cornerstoneWADOImageLoaderCodecs.js',
usePDFJS: false,
},
},
});
// 加载图像
cornerstone.loadImage('http://example.com/image.dcm').then(function(image) {
// 处理图像
});
项目中遇到的难题(Vue3)
问题1:无法滚动
菜单栏里渲染影像时,当内容超出浏览器高度时,鼠标移动到影像图片上无法滚动菜单栏问题
//解决:
// 可以给影像容器添加css属性
pointer-events: none
//该元素作用:去掉鼠标事件
问题2:显示问题
在使用element-plus框架的el-collapse时,搭配着computed使用时,第一次显示不出影像,点击折叠后才会显示()
//解决:
//循环出来的DOM元素统一绑定ref,这个ref会返回一个dom数组,把参数通过自定义参数绑定上,然后通过watch去监听这个ref,去循环解析影像
const HTMLElement = ref(null)
watch(HTMLElement,(val)=>{
//逻辑
})
<div v-for='item in xxx' key='' data-xxx='xxx' ref='HTMLElement'> </div>
问题3:无法记载图片格式的影像
在项目使用中,发现后端会返回.jpg格式的影像,然后放到项目中会报错
//解决:
//这是因为没有引入 cornerstone Web Image Loader(图形加载器插件)
import cornerstoneWebImageLoader from 'cornerstone-web-image-loader';
cornerstoneWebImageLoader.external.cornerstone = cornerstone;
//PS: .jpg格式的URL只需要这个URL就行了不需要wadouri这种开头
问题4:某些dicom文件无法加载
在项目使用中,发现文件无法加载,会报有个什么js脚本文件加载失败,这个有点莫名其妙
//解决:
//发现是有个js文件没有引入
// cornerstoneWADOImageLoader.webWorkerManager.initialize({
// maxWebWorkers: navigator.hardwareConcurrency || 1,
// startWebWorkersOnDemand: true,
// taskConfiguration: {
// decodeTask: {
// initializeCodecsOnStartup: true,
// },
// },
// webWorkerTaskPaths: [
// 'https://unpkg.com/cornerstone-wado-image-loader@4.1.5/dist/610.bundle.min.worker.js',
// ],
// });
问题4:自适应问题
因为在菜单栏选择某个序列时会给添加一个边框,这时去看影像会有点突兀
//解决:
1.在最开始就给每个容器添加边框并且隐藏,给激活的class样式添加颜色
或
2.在给动态class时,去调用resize()方法
问题5:影像显示问题
在点击序列切换影像进行显示时,会发现影像变大了,这是因为上一个的视宽视高会给下一个影像
//解决:
//切换是调用resize()方法即可