From 327f17a551f869c8f454566782be720367291235 Mon Sep 17 00:00:00 2001 From: rodrigobasilio2022 <114958722+rodrigobasilio2022@users.noreply.github.com> Date: Mon, 16 Jan 2023 17:31:24 -0300 Subject: [PATCH] feat: add multiframe example (#331) * First commit * Add multiframe support to cs3d * Remove unnecessary file and refactoring * Remove unnecessary file and refactoring code * Running required checks * Refactoring convertMultiframeImageIds function * Adding multiframe support for local example * Update cswil version --- packages/docs/package.json | 2 +- .../package.json | 4 +- packages/tools/examples/local/index.ts | 9 ++- .../demo/helpers/convertMultiframeImageIds.js | 64 +++++++++++++++++++ .../helpers/createImageIdsAndCacheMetaData.js | 35 ++++++---- yarn.lock | 8 +-- 6 files changed, 101 insertions(+), 21 deletions(-) create mode 100644 utils/demo/helpers/convertMultiframeImageIds.js diff --git a/packages/docs/package.json b/packages/docs/package.json index 896d08c55..967d4625c 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -38,7 +38,7 @@ "@mdx-js/react": "^1.6.21", "@svgr/webpack": "^6.2.1", "clsx": "^1.1.1", - "cornerstone-wado-image-loader": "^4.7.0", + "cornerstone-wado-image-loader": "^4.8.0", "dcmjs": "0.19.2", "detect-gpu": "^4.0.45", "dicom-parser": "^1.8.11", diff --git a/packages/streaming-image-volume-loader/package.json b/packages/streaming-image-volume-loader/package.json index 08f75791d..b53cfa006 100644 --- a/packages/streaming-image-volume-loader/package.json +++ b/packages/streaming-image-volume-loader/package.json @@ -26,14 +26,14 @@ }, "dependencies": { "@cornerstonejs/core": "^0.26.0", - "cornerstone-wado-image-loader": "^4.7.0" + "cornerstone-wado-image-loader": "^4.8.0" }, "peerDependencies": { "@cornerstonejs/calculate-suv": "1.0.2" }, "devDependencies": { "@cornerstonejs/calculate-suv": "1.0.2", - "cornerstone-wado-image-loader": "^4.7.0" + "cornerstone-wado-image-loader": "^4.8.0" }, "contributors": [ { diff --git a/packages/tools/examples/local/index.ts b/packages/tools/examples/local/index.ts index e830ca5a5..4ff4a8366 100644 --- a/packages/tools/examples/local/index.ts +++ b/packages/tools/examples/local/index.ts @@ -19,6 +19,10 @@ import { initDemo, setTitleAndDescription, } from '../../../../utils/demo/helpers'; +import { + convertMultiframeImageIds, + prefetchMetadataInformation, +} from '../../../../utils/demo/helpers/convertMultiframeImageIds'; // This is for debugging purposes console.warn( @@ -149,8 +153,9 @@ function handleDragOver(evt) { evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy. } -function loadAndViewImage(imageId) { - const stack = [imageId]; +async function loadAndViewImage(imageId) { + await prefetchMetadataInformation([imageId]); + const stack = convertMultiframeImageIds([imageId]); // Set the stack on the viewport viewport.setStack(stack).then(() => { // Set the VOI of the stack diff --git a/utils/demo/helpers/convertMultiframeImageIds.js b/utils/demo/helpers/convertMultiframeImageIds.js new file mode 100644 index 000000000..9ea1296f1 --- /dev/null +++ b/utils/demo/helpers/convertMultiframeImageIds.js @@ -0,0 +1,64 @@ +import { metaData } from '@cornerstonejs/core'; +import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'; + +/** + * preloads imageIds metadata in memory + **/ +async function prefetchMetadataInformation(imageIdsToPrefetch) { + for (let i = 0; i < imageIdsToPrefetch.length; i++) { + await cornerstoneWADOImageLoader.wadouri.loadImage(imageIdsToPrefetch[i]) + .promise; + } +} + +function getFrameInformation(imageId) { + if (imageId.includes('wadors:')) { + const frameIndex = imageId.indexOf('/frames/'); + const imageIdFrameless = + frameIndex > 0 ? imageId.slice(0, frameIndex + 8) : imageId; + return { + frameIndex, + imageIdFrameless, + }; + } else { + const frameIndex = imageId.indexOf('&frame='); + let imageIdFrameless = + frameIndex > 0 ? imageId.slice(0, frameIndex + 7) : imageId; + if (!imageIdFrameless.includes('&frame=')) { + imageIdFrameless = imageIdFrameless + '&frame='; + } + return { + frameIndex, + imageIdFrameless, + }; + } +} +/** + * Receives a list of imageids possibly referring to multiframe dicom images + * and returns a list of imageid where each imageid referes to one frame. + * For each imageId representing a multiframe image with n frames, + * it will create n new imageids, one for each frame, and returns the new list of imageids + * If a particular imageid no refer to a mutiframe image data, it will be just copied into the new list + * @returns new list of imageids where each imageid represents a frame + */ +function convertMultiframeImageIds(imageIds) { + const newImageIds = []; + imageIds.forEach((imageId) => { + const { imageIdFrameless } = getFrameInformation(imageId); + const instanceMetaData = metaData.get('multiframeModule', imageId); + if ( + instanceMetaData && + instanceMetaData.NumberOfFrames && + instanceMetaData.NumberOfFrames > 1 + ) { + const NumberOfFrames = instanceMetaData.NumberOfFrames; + for (let i = 0; i < NumberOfFrames; i++) { + const newImageId = imageIdFrameless + (i + 1); + newImageIds.push(newImageId); + } + } else newImageIds.push(imageId); + }); + return newImageIds; +} + +export { convertMultiframeImageIds, prefetchMetadataInformation }; diff --git a/utils/demo/helpers/createImageIdsAndCacheMetaData.js b/utils/demo/helpers/createImageIdsAndCacheMetaData.js index 940f436fa..4c6d36635 100644 --- a/utils/demo/helpers/createImageIdsAndCacheMetaData.js +++ b/utils/demo/helpers/createImageIdsAndCacheMetaData.js @@ -7,10 +7,12 @@ import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'; import ptScalingMetaDataProvider from './ptScalingMetaDataProvider'; import getPixelSpacingInformation from './getPixelSpacingInformation'; +import convertMultiframeImageIds from './convertMultiframeImageIds'; const { DicomMetaDictionary } = dcmjs.data; const { calibratedPixelSpacingMetadataProvider } = utilities; +/** /** * Uses dicomweb-client to fetch metadata of a study, cache it in cornerstone, * and return a list of imageIds for the frames. @@ -39,7 +41,7 @@ export default async function createImageIdsAndCacheMetaData({ const instances = await client.retrieveSeriesMetadata(studySearchOptions); const modality = instances[0][MODALITY].Value[0]; - const imageIds = instances.map((instanceMetaData) => { + let imageIds = instances.map((instanceMetaData) => { const SeriesInstanceUID = instanceMetaData[SERIES_INSTANCE_UID].Value[0]; const SOPInstanceUID = instanceMetaData[SOP_INSTANCE_UID].Value[0]; @@ -60,19 +62,28 @@ export default async function createImageIdsAndCacheMetaData({ imageId, instanceMetaData ); - - // Add calibrated pixel spacing - const m = JSON.parse(JSON.stringify(instanceMetaData)); - const instance = DicomMetaDictionary.naturalizeDataset(m); - const pixelSpacing = getPixelSpacingInformation(instance); - - calibratedPixelSpacingMetadataProvider.add( - imageId, - pixelSpacing.map((s) => parseFloat(s)) - ); - return imageId; }); + // if the image ids represent multiframe information, creates a new list with one image id per frame + // if not multiframe data available, just returns the same list given + imageIds = convertMultiframeImageIds(imageIds); + imageIds.forEach((imageId) => { + let instanceMetaData = + cornerstoneWADOImageLoader.wadors.metaDataManager.get(imageId); + instanceMetaData = JSON.parse(JSON.stringify(instanceMetaData)); + if (instanceMetaData) { + // Add calibrated pixel spacing + const metadata = DicomMetaDictionary.naturalizeDataset(instanceMetaData); + const pixelSpacing = getPixelSpacingInformation(metadata); + + if (pixelSpacing) { + calibratedPixelSpacingMetadataProvider.add( + imageId, + pixelSpacing.map((s) => parseFloat(s)) + ); + } + } + }); // we don't want to add non-pet // Note: for 99% of scanners SUV calculation is consistent bw slices diff --git a/yarn.lock b/yarn.lock index 689af0362..76c4d7342 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8199,10 +8199,10 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cornerstone-wado-image-loader@^4.7.0: - version "4.7.0" - resolved "https://registry.npmjs.org/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-4.7.0.tgz#0b36ea3e8b0f4554234e3fc6db6260e95afea545" - integrity sha512-tq8YhiS4eQ9FmmxSCuoFmntSPzM9rccPq3WkeHbfglAEwmQjRaZMOR9r0MQaT0se/A9hY37q3xRfe9inhK1Ezw== +cornerstone-wado-image-loader@^4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-4.8.0.tgz#64237a807227815bd117af839db4c05ab028d5f0" + integrity sha512-pvAAeH9tzL9PZydYOQqRSmIVoWiaxGDdgp0l/U8KafC0UNTiRyB6Sk0pTXTQqvGK2ZnwM4evjSDS9zyD3PxCmg== dependencies: "@babel/eslint-parser" "^7.19.1" "@cornerstonejs/codec-charls" "^0.1.1"