Help to displaying 3D for NIFTI segmentation

Help to displaying a Volume_3D Viewport of NIFTI

I’m running an example displaying a volume stack for segmentation read from a .nii file. I can currently run this example locally. However, I want to display it in a 3D Viewport, possibly using a tool like RotateTrackball. Can anyone help me with this? Thank you very much.

Here is my current example

import {
  RenderingEngine,
  Enums,
  init as csInit,
  volumeLoader,
  setVolumesForViewports,
  eventTarget,
  imageLoader,
} from '@cornerstonejs/core';

import * as cornerstoneTools from '@cornerstonejs/tools';

import {
  Enums as NiftiEnums,
  cornerstoneNiftiImageLoader,
  createNiftiImageIdsAndCacheMetadata,
} from '@cornerstonejs/nifti-volume-loader';

import { addDropdownToToolbar, initDemo } from '../../../../utils/demo/helpers';

const {
  ToolGroupManager,
  StackScrollTool,
  ZoomTool,
  Enums: csToolsEnums,
  WindowLevelTool,
  PanTool,
} = cornerstoneTools;

const { MouseBindings } = csToolsEnums;

// This is for debugging purposes

console.warn(
  'Click on index.ts to open source code for this example --------->'
);

const size = '500px';

const content = document.getElementById('content');

const viewportGrid = document.createElement('div');

viewportGrid.style.display = 'flex';
viewportGrid.style.display = 'flex';
viewportGrid.style.flexDirection = 'row';
const element1 = document.createElement('div');
const element2 = document.createElement('div');
const element3 = document.createElement('div');
element1.style.width = size;
element1.style.height = size;
element2.style.width = size;
element2.style.height = size;
element3.style.width = size;
element3.style.height = size;

element1.oncontextmenu = () => false;
element2.oncontextmenu = () => false;
element3.oncontextmenu = () => false;

viewportGrid.appendChild(element1);
viewportGrid.appendChild(element2);
viewportGrid.appendChild(element3);

content.appendChild(viewportGrid);

const pr = document.createElement('p');
pr.innerHTML = 'Nifti Loading Progress:';

const progress = document.createElement('progress');
progress.value = 0;
progress.max = 100;

content.appendChild(pr);
content.appendChild(progress);

const viewportId1 = 'CT_NIFTI_AXIAL';
const viewportId2 = 'CT_NIFTI_SAGITTAL';
const viewportId3 = 'CT_NIFTI_CORONAL';

const viewportIds = [viewportId1, viewportId2, viewportId3];

const toolsNames = [
  WindowLevelTool.toolName,
  PanTool.toolName,
  StackScrollTool.toolName,
  ZoomTool.toolName,
];

let selectedToolName = toolsNames[0];

const toolGroupId = 'STACK_TOOL_GROUP_ID';

addDropdownToToolbar({
  options: { values: toolsNames, defaultValue: selectedToolName },
  onSelectedValueChange: (newSelectedToolNameAsStringOrNumber) => {
    const newSelectedToolName = String(newSelectedToolNameAsStringOrNumber);
    const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
    toolGroup.setToolActive(newSelectedToolName, {
      bindings: [
        {
          mouseButton: MouseBindings.Primary, // Left Click
        },
      ],
    });
    toolGroup.setToolPassive(selectedToolName);
    selectedToolName = <string>newSelectedToolName;
  },
});

const niftiURL = 'http://localhost:8001/test-segmentation';

const volumeLoaderScheme = 'cornerstoneStreamingImageVolume'; // Loader id which defines which volume loader to use

const volumeId = `${volumeLoaderScheme}:${niftiURL}`; // VolumeId with loader id + volume id

async function setup() {
  await initDemo();

  imageLoader.registerImageLoader('nifti', cornerstoneNiftiImageLoader);

  const imageIds = await createNiftiImageIdsAndCacheMetadata({ url: niftiURL });

  // Add tools to Cornerstone3D

  cornerstoneTools.addTool(WindowLevelTool);
  cornerstoneTools.addTool(PanTool);
  cornerstoneTools.addTool(StackScrollTool);
  cornerstoneTools.addTool(ZoomTool);

  const toolGroup = ToolGroupManager.createToolGroup(toolGroupId);
  toolGroup.addTool(WindowLevelTool.toolName);
  toolGroup.addTool(PanTool.toolName);
  toolGroup.addTool(ZoomTool.toolName);
  toolGroup.addTool(StackScrollTool.toolName);
  toolGroup.setToolActive(WindowLevelTool.toolName, {
    bindings: [
      {
        mouseButton: MouseBindings.Primary, // Left Click
      },
    ],
  });

  toolGroup.setToolActive(ZoomTool.toolName, {
    bindings: [
      {
        mouseButton: MouseBindings.Secondary, // Right Click
      },
    ],
  });

  toolGroup.setToolActive(PanTool.toolName, {
    bindings: [
      {
        mouseButton: MouseBindings.Auxiliary, // Right Click
      },
    ],
  });
  toolGroup.setToolActive(StackScrollTool.toolName, {
    bindings: [
      {
        mouseButton: MouseBindings.Wheel,
      },
    ],
  });

  const renderingEngineId = 'myRenderingEngine';
  const renderingEngine = new RenderingEngine(renderingEngineId);
  const viewportInputArray = [
    {
      viewportId: viewportId1,
      type: Enums.ViewportType.ORTHOGRAPHIC,
      element: element1,
      defaultOptions: {
        orientation: Enums.OrientationAxis.AXIAL,
      },
    },
    {
      viewportId: viewportId2,
      type: Enums.ViewportType.ORTHOGRAPHIC,
      element: element2,
      defaultOptions: {
        orientation: Enums.OrientationAxis.SAGITTAL,
      },
    },
    {
      viewportId: viewportId3,
      type: Enums.ViewportType.ORTHOGRAPHIC,
      element: element3,
      defaultOptions: {
        orientation: Enums.OrientationAxis.CORONAL,
      },
    },
  ];

  renderingEngine.setViewports(viewportInputArray);

  // Set the tool group on the viewports
  viewportIds.forEach((viewportId) =>
    toolGroup.addViewport(viewportId, renderingEngineId)
  );

  const updateProgress = (evt) => {
    const { data } = evt.detail;
    if (!data) {
      return;
    }
    const { total, loaded } = data;
    if (!total) {
      return;
    }
    const progress = Math.round((loaded / total) * 100);
    const element = document.querySelector('progress');
    element.value = progress;
  };

  eventTarget.addEventListener(
    NiftiEnums.Events.NIFTI_VOLUME_PROGRESS,
    updateProgress
  );

  // This will load the nifti file, no need to call .load again for nifti
  const volume = await volumeLoader.createAndCacheVolume(volumeId, {
    imageIds,
  });

  await volume.load();

  const segmentationId = 'AI_LABELMAP_ID';

  // 2. Thêm volume NIfTI vào hệ thống Segmentation của Cornerstone
  await cornerstoneTools.segmentation.addSegmentations([
    {
      segmentationId,
      representation: {
        // Khai báo đây là kiểu Labelmap (tô màu theo nhãn 1, 2, 3...)
        type: csToolsEnums.SegmentationRepresentations.Labelmap,
        data: {
          volumeId: volumeId,
        },
      },
    },
  ]);

  const segmentationRepresentationRuns = viewportIds.map((vId) =>
    cornerstoneTools.segmentation.addSegmentationRepresentations(vId, [
      {
        segmentationId,
        type: csToolsEnums.SegmentationRepresentations.Labelmap,
      },
    ])
  );
  await Promise.all(segmentationRepresentationRuns);
  setVolumesForViewports(
    renderingEngine,
    [{ volumeId }],
    viewportInputArray.map((v) => v.viewportId)
  );
  renderingEngine.render();
}

setup();

Additionally, I have a question: Does Nifti Volume Loader support reading files that have been compressed into .nii.gz format?

Te puedo ayudar a levantar OHIF con Dcm4chee