深入理解Cornerstone3D.js渲染Stack影像数据

下面是对使用Cornerstone3D.js浏览我的第一个医学影像的深入分析,结合Cornerstone3D.js的源码来了解一下Cornerstone3D.js是如何渲染stack类型的影像数据的,完整代码请参见这篇文章,下面是分析:
1.初始化cornerstone-core

await coreInit();

这个是调用的core\src下的init.ts的init方法,作用是:初始化cornerstone-core,检测客户端是否支持WebGL,默认情况下是支持中等GPU层级.另外一个重要的是实例化WebWorkManager.WebWorkManager类似于Java的线程池,是用来对Web Worker进行管理的管理器。可以看下init.ts的init源码(看英文注释):

/**
 * Initialize the cornerstone-core. This function checks for WebGL context availability
 * to determine if GPU rendering is possible. By default, it assumes a medium GPU tier.
 *
 * It's the responsibility of the consumer application to provide accurate GPU tier information
 * if needed. Libraries like 'detect-gpu' can be used for this purpose, and the result can be
 * passed in the configuration object.
 *
 * If a WebGL context is available, GPU rendering will be used. Otherwise, it will fall back
 * to CPU rendering for supported operations.
 *
 * @param configuration - A configuration object, which can include GPU tier information
 * @returns A promise that resolves to true if cornerstone has been initialized successfully.
 * @category Initialization
 */
function init(configuration = config): boolean {
  if (csRenderInitialized) {
    return csRenderInitialized;
  }

  canUseNorm16Texture = _hasNorm16TextureSupport();

  // merge configs
  config = deepMerge(defaultConfig, configuration);

  // mobile safe
  if (config.isMobile) {
    config.rendering.webGlContextCount = 1;
  }

  if (isIOS()) {
    if (configuration.rendering?.preferSizeOverAccuracy) {
      config.rendering.preferSizeOverAccuracy = true;
    } else {
      console.log(
        'norm16 texture not supported, you can turn on the preferSizeOverAccuracy flag to use native data type, but be aware of the inaccuracy of the rendering in high bits'
      );
    }
  }

  const hasWebGLContext = _hasActiveWebGLContext();
  if (!hasWebGLContext) {
    console.log('CornerstoneRender: GPU not detected, using CPU rendering');
    config.rendering.useCPURendering = true;
  } else {
    console.log('CornerstoneRender: using GPU rendering');
  }

  csRenderInitialized = true;

  //实例化WebWorkManager
  if (!webWorkerManager) {
   
   
    webWorkerManager = new CentralizedWebWorkerManager();
  }

  return csRenderInitialized;
}

2.dicomImageLoader初始化

await dicomImageLoaderInit();

初始化dicomImageLoader,对应dicomImageLoader\src下的init.ts的init(),在这里主要注册了wadors和wadouri两个dicomImageLoader,其次是获取上面的WebWorkerManager。init()方法如下:


function init(options: LoaderOptions = {
   
   }): void {
   
   
  // setting options should happen first, since we use the options in the
  // cornerstone set
  // DO NOT CHANGE THE ORDER OF THESE TWO LINES!
  setOptions(options);
  //注册imageLoader
  registerLoaders();

  //获取WebWorkerManager
  //这就可以理解为什么在Core中要调用init()初始化的原因了:)
  const workerManager = getWebWorkerManager();
  const maxWorkers = options?.maxWebWorkers || getReasonableWorkerCount();
  workerManager.registerWorker('dicomImageLoader', workerFn, {
   
   
    maxWorkerInstances: maxWorkers,
  });
}

3.实例化RenderingEngine

  renderingEngine = new RenderingEngine(renderingEngineId);

  const viewportId = 'CT_AXIAL_STACK';

  const viewportInput = {
   
   
    viewportId,
    element,
    type: ViewportType.STACK,
     defaultOptions: {
   
   
      background: [0.4, 0, 0.4]
    },
  };

  renderingEngine.enableElement(viewportInput);

实例化RenderingEngine,指定渲染的类型为 ViewportType.STACK,背景颜色为[0.4, 0, 0.4]

4.请求元数据

const studySearchOptions = {
   
   
  studyInstanceUID: '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463',
  seriesInstanceUID: '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561'
};

const  wadoRsRoot='https://d14fa38qiwhyfd.cloudfront.net/dicomweb';
const StudyInstanceUID='1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463';
const SeriesInstanceUID= '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561';
const SOP_INSTANCE_UID = '00080018';
const SERIES_INSTANCE_UID = '0020000E';
const MODALITY = '00080060';
const SOPInstanceUID = null;


const client = new api.DICOMwebClient({
   
   
  url: "https://d14fa38qiwhyfd.cloudfront.net/dicomweb"
});

let instances = await client.retrieveSeriesMetadata(studySearchOptions);
console.log("instances[0]",instances[0]);
console.log("----------------------------");
console.log("instances[1]",instances[1]);

//return imagesId
 let imageIds= instances .map((instanceMetaData) => {
   
   
    const SeriesInstanceUID = instanceMetaData[SERIES_INSTANCE_UID].Value[0];

    const SOPInstanceUIDToUse =
      SOPInstanceUID || instanceMetaData[SOP_INSTANCE_UID].Value[0];

    const prefix = 'wadors:';

    const imageId =
      prefix +
      wadoRsRoot +
      '/studies/' +
      StudyInstanceUID.trim() +
      '/series/' +
      SeriesInstanceUID.trim() +
      '/instances/' +
      SOPInstanceUIDToUse.trim() +
      '/frames/1';
      
       cornerstoneDICOMImageLoader.wadors.metaDataManager.add(
      imageId,
      instanceMetaData
    );
   
    return imageId;
  });

使用DICOMwebClient获取影像文件的元数据,然后将它们缓存到imageLoader的metaDataManager中,在上面我们可以输出元数据看一下是什么。metaDataManager的add方法如下:

function add(imageId: string, metadata: WADORSMetaData) {
   
   
  const imageURI = imageIdToURI(imageId);

  Object.defineProperty(metadata, 'isMultiframe', {
   
   
    value: isMultiframe(metadata),
    enumerable: false,
  });

  metadataByImageURI[imageURI] = metadata;
}

4.异步请求和渲染

viewport = renderingEngine.getViewport(viewportId);
//console.log("viewport is: ",viewport);

//做了很多事情,最重要的是,获取image的metaData,也就是上面cornerstoneDICOMImageLoader.wadors.metaDataManager.add的元数据,这样才能正确显示出image
//在setStack里,才真正使用dicom-image-loader异步获取影像数据,具体在_loadAndDisplayImage方法里
await viewport.setStack(imageIds);

setStack方法调用的是core\src\RenderingEngine\StackViewport.ts的setStack方法,这个方法是一个异步方法,默认显示的是第一张stack影像,代码如下:

/**
   * Sets the imageIds to be visualized inside the stack viewport. It accepts
   * list of imageIds, the index of the first imageId to be viewed. It is a
   * asynchronous function that returns a promise resolving to imageId being
   * displayed in the stack viewport.
   *
   *
   * @param imageIds - list of strings, that represents list of image Ids
   * @param currentImageIdIndex - number representing the index of the initial image to be displayed
   */
  public async setStack(
    imageIds: string[],
    currentImageIdIndex = 0
  ): Promise<string> {
   
   
    this._throwIfDestroyed();

    this.imageIds = imageIds;

    if (currentImageIdIndex > imageIds.length) {
   
   
      throw new Error(
        'Current image index is greater than the number of images in the stack'
      );
    }

    this.imageKeyToIndexMap.clear();
    imag
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值