cornerstone Tools 切换影像篇

1,896 阅读3分钟

cornerstone Tools 切换影像篇

在搭建 Pacs 影像阅片系统时,序列内图像切换使用场景较多,流畅与效率将影响用户使用的直观感受。本篇将详细介绍如何通过 cornerstoneTools 实现影像快速流畅的切换。

切换影像工具

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

切换原理

快速的切换影像,实际就是通过一些机制进行高效、准确、动态的渲染影像。

StackScrollTool 与 StackScrollMouseWheelTool 的主要区别为交互不同,其内部的实现原理是一致的,均是通过调用 scroll 函数进行切换影像。

scroll 函数解析:

/**
 * Scrolls through the stack.
 * @export @public @method
 * @name scroll
 *
 * @param  {HTMLElement} element          The element to scroll.
 * @param  {number} images                The number of images to scroll through.
 * @param  {type} [loop = false]          Whether to loop the scrolling.
 * @param  {type} [allowSkipping = true]  Whether frames can be skipped.
 * @returns {void}
 */
export default function (element, images, loop = false, allowSkipping = true) {
  // 获取 cornerstoneTools.addToolState(element, "stack", stack) 添加的状态
  const toolData = getToolState(element, "stack");

  if (!toolData || !toolData.data || !toolData.data.length) {
    return;
  }

  const stackData = toolData.data[0];

  if (!stackData.pending) {
    stackData.pending = [];
  }

  /**
   * 计算新的影像索引,此处通过 iamges 参数控制向前切换还是向后切换。
   * iamges 参数为 StackScroll 与 StackScrollMouseWheel 工具通过用户交互操作而计算得来。
   * 例如:
   * StackScroll 通过计算鼠标滑动方向判断向前或向后。
   * StackScrollMouseWheel 通过鼠标滚轮方向判断向前或向后。
   */
  let newImageIdIndex = stackData.currentImageIdIndex + images;

  /**
   * 再次计算影像索引,此处通过 loop 参数控制是否循环。
   * 允许循环时,%= 运算符在不存在余数时返回 newImageIdIndex,
   * 当 newImageIdIndex 与 nbImages 相等时,返回 0,通过此方法达到重头开始的效果。
   */
  if (loop) {
    const nbImages = stackData.imageIds.length;

    newImageIdIndex %= nbImages;
  } else {
    newImageIdIndex = clip(newImageIdIndex, 0, stackData.imageIds.length - 1);
  }

  /**
   * 渲染图像,此处通过 allowSkipping 参数控制是否跳帧。
   * 当允许跳帧时,将直接渲染最新的影像,当切换过快时,会跳过未来得及渲染的影像。
   * 当不允许跳帧时,将所有触发的影像索引放入 stackData.pending 数组中,通过递归逐条渲染,直至清空 stackData.pending。
   */
  if (allowSkipping) {
    scrollToIndex(element, newImageIdIndex);
  } else {
    const pendingEvent = {
      index: newImageIdIndex
    };

    stackData.pending.push(pendingEvent);
    scrollWithoutSkipping(stackData, pendingEvent, element);
  }
}

最简单的例子

本篇已详细介绍 cornerstoneTools 的切图工具,已下例子将展现最基本的具有切换影像功能的预览程序。

ps:由于一些原因,网络 dcm 文件资源下载较慢,可先将所有影像滚动切换一遍,等待所有 dcm 文件下载完成后,在体验切换交互效果。