From e4cca5ffbba28bf9a11ec8350097217816d69fd3 Mon Sep 17 00:00:00 2001 From: Alireza Date: Wed, 22 Mar 2023 10:26:12 -0400 Subject: [PATCH] fix(rendering): should still use Float32 when not 16 bit for scaling issues --- common/reviews/api/core.api.md | 6 +++++- .../src/RenderingEngine/BaseVolumeViewport.ts | 8 ++++++-- .../core/src/RenderingEngine/StackViewport.ts | 16 +++++++--------- packages/core/src/RenderingEngine/Viewport.ts | 7 +++++++ .../helpers/createVolumeActor.ts | 5 +++-- .../helpers/setDefaultVolumeVOI.ts | 13 ++++++++++--- packages/core/src/loaders/volumeLoader.ts | 7 +++++-- .../core/src/utilities/loadImageToCanvas.ts | 6 ++++++ packages/docs/package.json | 2 +- .../streaming-image-volume-loader/package.json | 4 ++-- .../cornerstoneStreamingImageVolumeLoader.ts | 18 ++++++++++++++++-- .../utilities/stackPrefetch/stackPrefetch.ts | 7 ++++++- yarn.lock | 18 +++++++++++++++++- 13 files changed, 91 insertions(+), 26 deletions(-) diff --git a/common/reviews/api/core.api.md b/common/reviews/api/core.api.md index 85d127171..e7fde345d 100644 --- a/common/reviews/api/core.api.md +++ b/common/reviews/api/core.api.md @@ -98,6 +98,8 @@ export abstract class BaseVolumeViewport extends Viewport implements IVolumeView // (undocumented) setVolumes(volumeInputArray: Array, immediate?: boolean, suppressEvents?: boolean): Promise; // (undocumented) + use16BitTexture: boolean; + // (undocumented) useCPURendering: boolean; // (undocumented) static get useCustomRenderingPipeline(): boolean; @@ -497,7 +499,7 @@ function createUint16SharedArray(length: number): Uint16Array; function createUint8SharedArray(length: number): Uint8Array; // @public (undocumented) -export function createVolumeActor(props: createVolumeActorInterface, element: HTMLDivElement, viewportId: string, suppressEvents?: boolean): Promise; +export function createVolumeActor(props: createVolumeActorInterface, element: HTMLDivElement, viewportId: string, suppressEvents?: boolean, use16BitTexture?: boolean): Promise; // @public (undocumented) export function createVolumeMapper(imageData: any, vtkOpenGLTexture: any): any; @@ -2448,6 +2450,8 @@ export class Viewport implements IViewport { // (undocumented) sHeight: number; // (undocumented) + protected _shouldUse16BitTexture(): boolean; + // (undocumented) readonly suppressEvents: boolean; // (undocumented) sWidth: number; diff --git a/packages/core/src/RenderingEngine/BaseVolumeViewport.ts b/packages/core/src/RenderingEngine/BaseVolumeViewport.ts index e51d0e5ed..5394823f6 100644 --- a/packages/core/src/RenderingEngine/BaseVolumeViewport.ts +++ b/packages/core/src/RenderingEngine/BaseVolumeViewport.ts @@ -52,6 +52,7 @@ import vtkSlabCamera from './vtkClasses/vtkSlabCamera'; */ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport { useCPURendering = false; + use16BitTexture = false; private _FrameOfReferenceUID: string; // Viewport Properties @@ -62,6 +63,7 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport { super(props); this.useCPURendering = getShouldUseCPURendering(); + this.use16BitTexture = this._shouldUse16BitTexture(); if (this.useCPURendering) { throw new Error( @@ -368,7 +370,8 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport { volumeInputArray[i], this.element, this.id, - suppressEvents + suppressEvents, + this.use16BitTexture ); // We cannot use only volumeId since then we cannot have for instance more @@ -433,7 +436,8 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport { volumeInputArray[i], this.element, this.id, - suppressEvents + suppressEvents, + this.use16BitTexture ); if (visibility === false) { diff --git a/packages/core/src/RenderingEngine/StackViewport.ts b/packages/core/src/RenderingEngine/StackViewport.ts index e12720d41..2046cacb2 100644 --- a/packages/core/src/RenderingEngine/StackViewport.ts +++ b/packages/core/src/RenderingEngine/StackViewport.ts @@ -6,7 +6,6 @@ import vtkCamera from '@kitware/vtk.js/Rendering/Core/Camera'; import { vec2, vec3, mat4 } from 'gl-matrix'; import vtkImageMapper from '@kitware/vtk.js/Rendering/Core/ImageMapper'; import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice'; -import { getConfiguration } from '../init'; import * as metaData from '../metaData'; import Viewport from './Viewport'; import eventTarget from '../eventTarget'; @@ -179,7 +178,7 @@ class StackViewport extends Viewport implements IStackViewport { this.scaling = {}; this.modality = null; this.useCPURendering = getShouldUseCPURendering(); - + this.use16BitTexture = this._shouldUse16BitTexture(); this._configureRenderingPipeline(); if (this.useCPURendering) { @@ -1431,7 +1430,6 @@ class StackViewport extends Viewport implements IStackViewport { TypedArray, }): void { let pixelArray; - this.use16BitTexture = this._shouldUse16BitTexture(); switch (bitsAllocated) { case 8: pixelArray = new Uint8Array(numVoxels * numComps); @@ -1739,6 +1737,9 @@ class StackViewport extends Viewport implements IStackViewport { const requestType = RequestType.Interaction; const additionalDetails = { imageId }; const options = { + targetBuffer: { + type: this.use16BitTexture ? undefined : 'Float32Array', + }, preScale: { enabled: true, }, @@ -1823,6 +1824,9 @@ class StackViewport extends Viewport implements IStackViewport { const requestType = RequestType.Interaction; const additionalDetails = { imageId }; const options = { + targetBuffer: { + type: this.use16BitTexture ? undefined : 'Float32Array', + }, preScale: { enabled: true, }, @@ -1992,12 +1996,6 @@ class StackViewport extends Viewport implements IStackViewport { } } - private _shouldUse16BitTexture() { - const { useNorm16Texture, preferSizeOverAccuracy } = - getConfiguration().rendering; - return useNorm16Texture || preferSizeOverAccuracy; - } - private _getInitialVOIRange(image: IImage) { if (this.voiRange && this.voiUpdatedWithSetProperties) { return this.voiRange; diff --git a/packages/core/src/RenderingEngine/Viewport.ts b/packages/core/src/RenderingEngine/Viewport.ts index 73b31ca2f..83aaf47fc 100644 --- a/packages/core/src/RenderingEngine/Viewport.ts +++ b/packages/core/src/RenderingEngine/Viewport.ts @@ -24,6 +24,7 @@ import type { } from '../types'; import type { ViewportInput, IViewport } from '../types/IViewport'; import type { vtkSlabCamera } from './vtkClasses/vtkSlabCamera'; +import { getConfiguration } from '../init'; /** * An object representing a single viewport, which is a camera @@ -1119,6 +1120,12 @@ class Viewport implements IViewport { return { widthWorld: maxX - minX, heightWorld: maxY - minY }; } + protected _shouldUse16BitTexture() { + const { useNorm16Texture, preferSizeOverAccuracy } = + getConfiguration().rendering; + return useNorm16Texture || preferSizeOverAccuracy; + } + _getCorners(bounds: Array): Array[] { return [ [bounds[0], bounds[2], bounds[4]], diff --git a/packages/core/src/RenderingEngine/helpers/createVolumeActor.ts b/packages/core/src/RenderingEngine/helpers/createVolumeActor.ts index 826669081..a40a681ba 100644 --- a/packages/core/src/RenderingEngine/helpers/createVolumeActor.ts +++ b/packages/core/src/RenderingEngine/helpers/createVolumeActor.ts @@ -33,7 +33,8 @@ async function createVolumeActor( props: createVolumeActorInterface, element: HTMLDivElement, viewportId: string, - suppressEvents = false + suppressEvents = false, + use16BitTexture = false ): Promise { const { volumeId, callback, blendMode } = props; @@ -61,7 +62,7 @@ async function createVolumeActor( // types of volumes which might not be composed of imageIds would be e.g., nrrd, nifti // format volumes if (imageVolume.imageIds) { - await setDefaultVolumeVOI(volumeActor, imageVolume); + await setDefaultVolumeVOI(volumeActor, imageVolume, use16BitTexture); } if (callback) { diff --git a/packages/core/src/RenderingEngine/helpers/setDefaultVolumeVOI.ts b/packages/core/src/RenderingEngine/helpers/setDefaultVolumeVOI.ts index 564be4466..9825dbe43 100644 --- a/packages/core/src/RenderingEngine/helpers/setDefaultVolumeVOI.ts +++ b/packages/core/src/RenderingEngine/helpers/setDefaultVolumeVOI.ts @@ -23,12 +23,13 @@ const REQUEST_TYPE = RequestType.Prefetch; */ async function setDefaultVolumeVOI( volumeActor: VolumeActor, - imageVolume: IImageVolume + imageVolume: IImageVolume, + use16BitTexture: boolean ): Promise { let voi = getVOIFromMetadata(imageVolume); if (!voi) { - voi = await getVOIFromMinMax(imageVolume); + voi = await getVOIFromMinMax(imageVolume, use16BitTexture); } if (!voi || voi.lower === undefined || voi.upper === undefined) { @@ -114,7 +115,10 @@ function getVOIFromMetadata(imageVolume: IImageVolume): VOIRange { * @param imageVolume - The image volume that we want to get the VOI from. * @returns The VOIRange with lower and upper values */ -async function getVOIFromMinMax(imageVolume: IImageVolume): Promise { +async function getVOIFromMinMax( + imageVolume: IImageVolume, + use16BitTexture: boolean +): Promise { const { imageIds } = imageVolume; const scalarData = imageVolume.getScalarData(); @@ -152,6 +156,9 @@ async function getVOIFromMinMax(imageVolume: IImageVolume): Promise { const byteOffset = imageIdIndex * bytesPerImage; const options = { + targetBuffer: { + type: use16BitTexture ? undefined : 'Float32Array', + }, priority: PRIORITY, requestType: REQUEST_TYPE, preScale: { diff --git a/packages/core/src/loaders/volumeLoader.ts b/packages/core/src/loaders/volumeLoader.ts index c97d81cdf..a238c2852 100644 --- a/packages/core/src/loaders/volumeLoader.ts +++ b/packages/core/src/loaders/volumeLoader.ts @@ -13,6 +13,7 @@ import eventTarget from '../eventTarget'; import triggerEvent from '../utilities/triggerEvent'; import { uuidv4 } from '../utilities'; import { Point3, Metadata, EventTypes, Mat3 } from '../types'; +import { getConfiguration } from '../init'; interface VolumeLoaderOptions { imageIds: Array; @@ -273,6 +274,8 @@ export async function createAndCacheDerivedVolume( let numBytes, TypedArray; + const { useNorm16Texture } = getConfiguration().rendering; + // If target buffer is provided if (targetBuffer) { if (targetBuffer.type === 'Float32Array') { @@ -281,10 +284,10 @@ export async function createAndCacheDerivedVolume( } else if (targetBuffer.type === 'Uint8Array') { numBytes = scalarLength; TypedArray = Uint8Array; - } else if (targetBuffer.type === 'Uint16Array') { + } else if (useNorm16Texture && targetBuffer.type === 'Uint16Array') { numBytes = scalarLength * 2; TypedArray = Uint16Array; - } else if (targetBuffer.type === 'Int16Array') { + } else if (useNorm16Texture && targetBuffer.type === 'Int16Array') { numBytes = scalarLength * 2; TypedArray = Uint16Array; } else { diff --git a/packages/core/src/utilities/loadImageToCanvas.ts b/packages/core/src/utilities/loadImageToCanvas.ts index dcdbc0696..65d657f57 100644 --- a/packages/core/src/utilities/loadImageToCanvas.ts +++ b/packages/core/src/utilities/loadImageToCanvas.ts @@ -5,6 +5,7 @@ import * as metaData from '../metaData'; import { RequestType } from '../enums'; import imageLoadPoolManager from '../requestPool/imageLoadPoolManager'; import renderToCanvas from './renderToCanvas'; +import { getConfiguration } from '../init'; /** * Loads and renders an imageId to a Canvas. It will use the CPU rendering pipeline @@ -55,9 +56,14 @@ export default function loadImageToCanvas( ); } + const { useNorm16Texture } = getConfiguration().rendering; + // IMPORTANT: Request type should be passed if not the 'interaction' // highest priority will be used for the request type in the imageRetrievalPool const options = { + targetBuffer: { + type: useNorm16Texture ? undefined : 'Float32Array', + }, preScale: { enabled: true, }, diff --git a/packages/docs/package.json b/packages/docs/package.json index 9c4cc04a9..d671a4e80 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.8.0", + "cornerstone-wado-image-loader": "^4.10.2", "dcmjs": "^0.24.4", "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 a3d0a3af6..3ff9be123 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.36.1", - "cornerstone-wado-image-loader": "^4.10.0" + "cornerstone-wado-image-loader": "^4.10.2" }, "peerDependencies": { "@cornerstonejs/calculate-suv": "1.0.2" }, "devDependencies": { "@cornerstonejs/calculate-suv": "1.0.2", - "cornerstone-wado-image-loader": "^4.10.0" + "cornerstone-wado-image-loader": "^4.10.2" }, "contributors": [ { diff --git a/packages/streaming-image-volume-loader/src/cornerstoneStreamingImageVolumeLoader.ts b/packages/streaming-image-volume-loader/src/cornerstoneStreamingImageVolumeLoader.ts index 20456ae3f..ceb4ecf51 100644 --- a/packages/streaming-image-volume-loader/src/cornerstoneStreamingImageVolumeLoader.ts +++ b/packages/streaming-image-volume-loader/src/cornerstoneStreamingImageVolumeLoader.ts @@ -177,8 +177,20 @@ function cornerstoneStreamingImageVolumeLoader( break; case 16: + // Temporary fix for 16 bit images to use Float32 + // until the new dicom image loader handler the conversion + // correctly + if (!use16BitDataType) { + sizeInBytes = length * 4; + scalarData = useSharedArrayBuffer + ? createFloat32SharedArray(length) + : new Float32Array(length); + + break; + } + sizeInBytes = length * 2; - if (use16BitDataType && (signed || hasNegativeRescale)) { + if (signed || hasNegativeRescale) { handleCache(sizeInBytes); scalarData = useSharedArrayBuffer ? createInt16SharedArray(length) @@ -186,13 +198,15 @@ function cornerstoneStreamingImageVolumeLoader( break; } - if (use16BitDataType && !signed && !hasNegativeRescale) { + if (!signed && !hasNegativeRescale) { handleCache(sizeInBytes); scalarData = useSharedArrayBuffer ? createUint16SharedArray(length) : new Uint16Array(length); break; } + + // Default to Float32 again sizeInBytes = length * 4; handleCache(sizeInBytes); scalarData = useSharedArrayBuffer diff --git a/packages/tools/src/utilities/stackPrefetch/stackPrefetch.ts b/packages/tools/src/utilities/stackPrefetch/stackPrefetch.ts index 1ee97a164..6a9a6b90c 100644 --- a/packages/tools/src/utilities/stackPrefetch/stackPrefetch.ts +++ b/packages/tools/src/utilities/stackPrefetch/stackPrefetch.ts @@ -6,7 +6,7 @@ import { eventTarget, imageLoadPoolManager, cache, - Types, + getConfiguration as getCoreConfiguration, } from '@cornerstonejs/core'; import { addToolState, getToolState } from './state'; @@ -243,10 +243,15 @@ function prefetch(element) { const requestFn = (imageId, options) => imageLoader.loadAndCacheImage(imageId, options); + const { useNorm16Texture } = getCoreConfiguration().rendering; + imageIdsToPrefetch.forEach((imageId) => { // IMPORTANT: Request type should be passed if not the 'interaction' // highest priority will be used for the request type in the imageRetrievalPool const options = { + targetBuffer: { + type: useNorm16Texture ? undefined : 'Float32Array', + }, preScale: { enabled: true, }, diff --git a/yarn.lock b/yarn.lock index e60767052..57d98b3ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8908,7 +8908,23 @@ 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.10.0, cornerstone-wado-image-loader@^4.8.0: +cornerstone-wado-image-loader@^4.10.2: + version "4.10.2" + resolved "https://registry.npmjs.org/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-4.10.2.tgz#139956654324fd2b01fe5b4900d0f4ed8c52633d" + integrity sha512-qj9dThELqYCm3jAZfg9qnUl8d76gngOl55kYJabY5lh/dFeVIxno/hYxy3ydE7RtG2c/TUGXb+EMUl0CJSqKBQ== + dependencies: + "@babel/eslint-parser" "^7.19.1" + "@cornerstonejs/codec-charls" "^1.2.3" + "@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2" + "@cornerstonejs/codec-openjpeg" "^1.2.2" + "@cornerstonejs/codec-openjph" "^2.4.2" + coverage-istanbul-loader "^3.0.5" + date-format "^4.0.14" + dicom-parser "^1.8.9" + pako "^2.0.4" + uuid "^9.0.0" + +cornerstone-wado-image-loader@^4.8.0: version "4.10.0" resolved "https://registry.npmjs.org/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-4.10.0.tgz#25c367cfc54a2c92ebbb5c64dba4fe38439112a1" integrity sha512-XZcgB8DpUxnsTA3vU/zbPtB2uFLL4ght70BPrQHykkX/Jlg/r6Ob7ztX2dEEAAb4ELE/d4icfGuSy10Acg/kyw==