From e36a6043315e900eeb6ce183772c7f852f478e96 Mon Sep 17 00:00:00 2001 From: Joe Boccanfuso <109477394+jbocce@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:44:56 -0400 Subject: [PATCH] fix(dicom overlay): Handle special cases of ArrayBuffer for various DICOM overlay attributes. (#3684) Co-authored-by: Alireza --- .circleci/config.yml | 91 +++++++++++-------- .../src/tools/ImageOverlayViewerTool.tsx | 2 + modes/basic-test-mode/.webpack/webpack.dev.js | 2 +- .../basic-test-mode/.webpack/webpack.prod.js | 2 +- platform/core/src/classes/MetadataProvider.ts | 55 ++++++++--- 5 files changed, 97 insertions(+), 55 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 90897ae36..96fa185c8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -93,43 +93,6 @@ jobs: file: '/home/circleci/repo/platform/core/coverage/reports' flags: 'core' - ### - # Workflow: PR_OPTIONAL_DOCKER_PUBLISH - ### - # DOCKER_PR_PUBLISH: - # <<: *defaults - # steps: - # # Enable yarn workspaces - # - run: yarn config set workspaces-experimental true - - # # Checkout code and ALL Git Tags - # - checkout - # - restore_cache: - # name: Restore Yarn and Cypress Package Cache - # keys: - # # when lock file changes, use increasingly general patterns to restore cache - # - yarn-packages-{{ checksum "yarn.lock" }} - # - yarn-packages- - - # - run: - # name: Install Dependencies - # command: yarn install --frozen-lockfile - - # - setup_remote_docker: - # docker_layer_caching: false - - # - run: - # name: Build and push Docker image - # command: | - # # Remove npm config - # rm -f ./.npmrc - # # Set our version number using vars - # echo $CIRCLE_BUILD_NUM - # # Build our image, auth, and push - # docker build --tag ohif/app:PR_BUILD-$CIRCLE_BUILD_NUM . - # echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin - # docker push ohif/app:PR_BUILD-$CIRCLE_BUILD_NUM - ### # Workflow: DEPLOY ### @@ -180,6 +143,53 @@ jobs: - commit.txt - version.json + # just to make sure later on we can publish them + BUILD_PACKAGES_QUICK: + <<: *defaults + steps: + - run: yarn -v + # Checkout code and ALL Git Tags + - checkout + - attach_workspace: + at: ~/repo + # Use increasingly general patterns to restore cache + - restore_cache: + name: Restore Yarn and Cypress Package Cache + keys: + - yarn-packages-{{ checksum "yarn.lock" }} + - yarn-packages- + - run: + name: Install Dependencies + command: yarn install --frozen-lockfile + - save_cache: + name: Save Yarn Package Cache + paths: + - ~/.cache/yarn + key: yarn-packages-{{ checksum "yarn.lock" }} + - run: + name: Avoid hosts unknown for github + command: | + rm -rf ~/.ssh + mkdir ~/.ssh/ + echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config + git config --global user.email "danny.ri.brown+ohif-bot@gmail.com" + git config --global user.name "ohif-bot" + - run: + name: Authenticate with NPM registry + command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc + - run: + name: Increase the event emitter limit + command: | + node ./increaseEventEmitterLimit.mjs + - run: + name: build half of the packages (to avoid out of memory in circleci) + command: | + yarn run build:package-all + - run: + name: build the other half of the packages + command: | + yarn run build:package-all-1 + ### # Workflow: RELEASE ### @@ -406,7 +416,10 @@ jobs: workflows: PR_CHECKS: jobs: - - UNIT_TESTS + - BUILD_PACKAGES_QUICK + - UNIT_TESTS: + requires: + - BUILD_PACKAGES_QUICK - CYPRESS_CUSTOM_RUN: name: 'Cypress Tests' @@ -427,7 +440,7 @@ workflows: cypress-cache-path: - '~/.cache/Cypress' requires: - - UNIT_TESTS + - BUILD_PACKAGES_QUICK # PR_OPTIONAL_VISUAL_TESTS: # jobs: diff --git a/extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx b/extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx index e3dead985..20cb8b9ae 100644 --- a/extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx +++ b/extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx @@ -160,6 +160,8 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool { let pixelData = null; if (overlay.pixelData.Value) { pixelData = overlay.pixelData.Value; + } else if (overlay.pixelData instanceof Array) { + pixelData = overlay.pixelData[0]; } else if (overlay.pixelData.retrieveBulkData) { pixelData = await overlay.pixelData.retrieveBulkData(); } diff --git a/modes/basic-test-mode/.webpack/webpack.dev.js b/modes/basic-test-mode/.webpack/webpack.dev.js index 4bf848b6c..1b8e34cfd 100644 --- a/modes/basic-test-mode/.webpack/webpack.dev.js +++ b/modes/basic-test-mode/.webpack/webpack.dev.js @@ -4,7 +4,7 @@ const SRC_DIR = path.join(__dirname, '../src'); const DIST_DIR = path.join(__dirname, '../dist'); const ENTRY = { - app: `${SRC_DIR}/index.js`, + app: `${SRC_DIR}/index.ts`, }; module.exports = (env, argv) => { diff --git a/modes/basic-test-mode/.webpack/webpack.prod.js b/modes/basic-test-mode/.webpack/webpack.prod.js index 25e5a75dd..0a7d7f864 100644 --- a/modes/basic-test-mode/.webpack/webpack.prod.js +++ b/modes/basic-test-mode/.webpack/webpack.prod.js @@ -11,7 +11,7 @@ const SRC_DIR = path.join(__dirname, '../src'); const DIST_DIR = path.join(__dirname, '../dist'); const ENTRY = { - app: `${SRC_DIR}/index.js`, + app: `${SRC_DIR}/index.ts`, }; module.exports = (env, argv) => { diff --git a/platform/core/src/classes/MetadataProvider.ts b/platform/core/src/classes/MetadataProvider.ts index 7b59c3ebd..0ecfae3d8 100644 --- a/platform/core/src/classes/MetadataProvider.ts +++ b/platform/core/src/classes/MetadataProvider.ts @@ -68,7 +68,7 @@ class MetadataProvider { if (!instance) { return; - } + } return (frameNumber && combineFrameInstance(frameNumber, instance)) || instance; } @@ -118,16 +118,15 @@ class MetadataProvider { * Adds a new handler for the given tag. The handler will be provided an * instance object that it can read values from. */ - public addHandler( - wadoImageLoaderTag: string, - handler - ) { + public addHandler(wadoImageLoaderTag: string, handler) { WADO_IMAGE_LOADER[wadoImageLoaderTag] = handler; } _getCornerstoneDICOMImageLoaderTag(wadoImageLoaderTag, instance) { let metadata = WADO_IMAGE_LOADER[wadoImageLoaderTag]?.(instance); - if (metadata) return metadata; + if (metadata) { + return metadata; + } switch (wadoImageLoaderTag) { case WADO_IMAGE_LOADER_TAGS.GENERAL_SERIES_MODULE: @@ -291,12 +290,42 @@ class MetadataProvider { const ROIStandardDeviationTag = `${groupStr}1303`; const OverlayOrigin = instance[OverlayOriginTag]; + let rows = 0; + if (instance[OverlayRowsTag] instanceof Array) { + // The DICOM VR for overlay rows is US (unsigned short). + const rowsInt16Array = new Uint16Array(instance[OverlayRowsTag][0]); + rows = rowsInt16Array[0]; + } else { + rows = instance[OverlayRowsTag]; + } + + let columns = 0; + if (instance[OverlayColumnsTag] instanceof Array) { + // The DICOM VR for overlay columns is US (unsigned short). + const columnsInt16Array = new Uint16Array(instance[OverlayColumnsTag][0]); + columns = columnsInt16Array[0]; + } else { + columns = instance[OverlayColumnsTag]; + } + + let x = 0; + let y = 0; + if (OverlayOrigin.length === 1) { + // The DICOM VR for overlay origin is SS (signed short) with a multiplicity of 2. + const originInt16Array = new Int16Array(OverlayOrigin[0]); + x = originInt16Array[0]; + y = originInt16Array[1]; + } else { + x = OverlayOrigin[0]; + y = OverlayOrigin[1]; + } + const overlay = { - rows: instance[OverlayRowsTag], - columns: instance[OverlayColumnsTag], + rows: rows, + columns: columns, type: instance[OverlayType], - x: OverlayOrigin[0], - y: OverlayOrigin[1], + x, + y, pixelData: OverlayData, description: instance[OverlayDescriptionTag], label: instance[OverlayLabelTag], @@ -444,7 +473,7 @@ class MetadataProvider { imageURI = imageURI.split('&frame=')[0]; const uids = this.imageURIToUIDs.get(imageURI); - let frameNumber = this.getFrameInformationFromURL(imageId) || '1'; + const frameNumber = this.getFrameInformationFromURL(imageId) || '1'; if (uids && frameNumber !== undefined) { return { ...uids, frameNumber }; @@ -489,9 +518,7 @@ const WADO_IMAGE_LOADER = { imageOrientationPatient: toNumber(ImageOrientationPatient), rowCosines: toNumber(rowCosines || [0, 1, 0]), columnCosines: toNumber(columnCosines || [0, 0, -1]), - imagePositionPatient: toNumber( - instance.ImagePositionPatient || [0, 0, 0] - ), + imagePositionPatient: toNumber(instance.ImagePositionPatient || [0, 0, 0]), sliceThickness: toNumber(instance.SliceThickness), sliceLocation: toNumber(instance.SliceLocation), pixelSpacing: toNumber(PixelSpacing || 1),