diff --git a/packages/core/src/utilities/windowLevel.ts b/packages/core/src/utilities/windowLevel.ts index 62a1737e1f..2d54fad386 100644 --- a/packages/core/src/utilities/windowLevel.ts +++ b/packages/core/src/utilities/windowLevel.ts @@ -58,6 +58,12 @@ function toLowHighRange( lower: number; upper: number; } { + // LINEAR formula produces lower === upper when WW <= 1 because (WW-1) + // becomes 0. Fall back to LINEAR_EXACT to avoid division by zero. See #2706. + if (windowWidth <= 1 && voiLUTFunction === VOILUTFunctionType.LINEAR) { + voiLUTFunction = VOILUTFunctionType.LINEAR_EXACT; + } + // Note: The SIGMOID function is currently treated the same as LINEAR // because we don't have a good way to define "bounds" for it. // Remove or statement when fixed diff --git a/packages/dicomImageLoader/src/decodeImageFrameWorker.js b/packages/dicomImageLoader/src/decodeImageFrameWorker.js index ea41912fff..cb525fbb89 100644 --- a/packages/dicomImageLoader/src/decodeImageFrameWorker.js +++ b/packages/dicomImageLoader/src/decodeImageFrameWorker.js @@ -244,6 +244,20 @@ function _handlePreScaleSetup( const scalingParameters = options.preScale.scalingParameters; _validateScalingParameters(scalingParameters); + // Force Float32Array when scaling parameters are non-integer to avoid + // getPixelDataTypeFromMinMax selecting Uint8Array (Number.isInteger(1.0) + // is true in JS, so scaled min/max can appear integer even when values + // between them are fractional). See #2706. + const hasFloatRescale = Object.values(scalingParameters).some( + (v) => typeof v === 'number' && !Number.isInteger(v) + ); + + if (hasFloatRescale) { + const typedArray = new Float32Array(imageFrame.pixelData.length); + typedArray.set(imageFrame.pixelData, 0); + return typedArray; + } + const scaledValues = _calculateScaledMinMax( minBeforeScale, maxBeforeScale, diff --git a/packages/dicomImageLoader/src/imageLoader/setPixelDataType.ts b/packages/dicomImageLoader/src/imageLoader/setPixelDataType.ts index 397c102b4d..b3d55a64d6 100644 --- a/packages/dicomImageLoader/src/imageLoader/setPixelDataType.ts +++ b/packages/dicomImageLoader/src/imageLoader/setPixelDataType.ts @@ -8,6 +8,12 @@ import getPixelDataTypeFromMinMax from '../shared/getPixelDataTypeFromMinMax'; * min and max values */ function setPixelDataType(imageFrame) { + // Skip re-typing if already Float32Array to prevent downgrading to + // Uint8Array via getPixelDataTypeFromMinMax. See #2706. + if (imageFrame.pixelData instanceof Float32Array) { + return; + } + const minValue = imageFrame.smallestPixelValue; const maxValue = imageFrame.largestPixelValue;