@@ -32,15 +32,16 @@ const typedArrayConstructors = {
3232 Uint16Array,
3333 Int16Array,
3434 Float32Array,
35+ Uint32Array,
3536} ;
3637
3738/**
3839 *
3940 * @param {import("@cornerstonejs/core").Types.IImageFrame } imageFrame
40- * @param {* } options
41+ * @param {import("./types").DICOMLoaderImageOptions } options
4142 * @param {number } start
4243 * @param {import('./types').LoaderDecodeOptions } decodeConfig
43- * @returns
44+ * @returns { import("@cornerstonejs/core").Types.IImageFrame }
4445 */
4546function postProcessDecodedPixels ( imageFrame , options , start , decodeConfig ) {
4647 const shouldShift =
@@ -94,13 +95,15 @@ function postProcessDecodedPixels(imageFrame, options, start, decodeConfig) {
9495 // are actually within the range of the type. If not, we need to convert the
9596 // pixel data to the correct type.
9697 if ( type && options . preScale . enabled && ! disableScale ) {
97- const { rescaleSlope, rescaleIntercept } =
98- options . preScale . scalingParameters ;
99- const minAfterScale = rescaleSlope * minBeforeScale + rescaleIntercept ;
100- const maxAfterScale = rescaleSlope * maxBeforeScale + rescaleIntercept ;
98+ const scalingParameters = options . preScale . scalingParameters ;
99+ const scaledValues = _calculateScaledMinMax (
100+ minBeforeScale ,
101+ maxBeforeScale ,
102+ scalingParameters
103+ ) ;
101104 invalidType = ! validatePixelDataType (
102- minAfterScale ,
103- maxAfterScale ,
105+ scaledValues . min ,
106+ scaledValues . max ,
104107 typedArrayConstructors [ type ]
105108 ) ;
106109 }
@@ -134,26 +137,22 @@ function postProcessDecodedPixels(imageFrame, options, start, decodeConfig) {
134137 const scalingParameters = options . preScale . scalingParameters ;
135138 _validateScalingParameters ( scalingParameters ) ;
136139
137- const { rescaleSlope, rescaleIntercept } = scalingParameters ;
138- const isSlopeAndInterceptNumbers =
139- typeof rescaleSlope === 'number' && typeof rescaleIntercept === 'number' ;
140+ const isRequiredScaling = _isRequiredScaling ( scalingParameters ) ;
140141
141- if ( isSlopeAndInterceptNumbers ) {
142+ if ( isRequiredScaling ) {
142143 applyModalityLUT ( pixelDataArray , scalingParameters ) ;
143144 imageFrame . preScale = {
144145 ...options . preScale ,
145146 scaled : true ,
146147 } ;
147148
148- // calculate the min and max after scaling
149- const { rescaleIntercept, rescaleSlope, suvbw } = scalingParameters ;
150- minAfterScale = rescaleSlope * minBeforeScale + rescaleIntercept ;
151- maxAfterScale = rescaleSlope * maxBeforeScale + rescaleIntercept ;
152-
153- if ( suvbw ) {
154- minAfterScale = minAfterScale * suvbw ;
155- maxAfterScale = maxAfterScale * suvbw ;
156- }
149+ const scaledValues = _calculateScaledMinMax (
150+ minBeforeScale ,
151+ maxBeforeScale ,
152+ scalingParameters
153+ ) ;
154+ minAfterScale = scaledValues . min ;
155+ maxAfterScale = scaledValues . max ;
157156 }
158157 } else if ( disableScale ) {
159158 imageFrame . preScale = {
@@ -175,13 +174,32 @@ function postProcessDecodedPixels(imageFrame, options, start, decodeConfig) {
175174 return imageFrame ;
176175}
177176
177+ /**
178+ *
179+ * @param {import("@cornerstonejs/core").Types.ScalingParameters } scalingParameters
180+ * @returns {boolean }
181+ */
182+ function _isRequiredScaling ( scalingParameters ) {
183+ // @ts -expect-error ScalingParameters type does not include `doseGridScaling`
184+ const { rescaleSlope, rescaleIntercept, modality, doseGridScaling, suvbw } =
185+ scalingParameters ;
186+
187+ const hasRescaleValues =
188+ typeof rescaleSlope === 'number' && typeof rescaleIntercept === 'number' ;
189+ const isRTDOSEWithScaling =
190+ modality === 'RTDOSE' && typeof doseGridScaling === 'number' ;
191+ const isPTWithSUV = modality === 'PT' && typeof suvbw === 'number' ;
192+
193+ return hasRescaleValues || isRTDOSEWithScaling || isPTWithSUV ;
194+ }
195+
178196/**
179197 *
180198 * @param {* } options
181199 * @param {import("@cornerstonejs/core").Types.IImageFrame } imageFrame
182200 * @param {* } typedArrayConstructors
183201 * @param {import("@cornerstonejs/core").Types.PixelDataTypedArray } pixelDataArray
184- * @returns
202+ * @returns { import("@cornerstonejs/core").Types.PixelDataTypedArray }
185203 */
186204function _handleTargetBuffer (
187205 options ,
@@ -237,7 +255,7 @@ function _handleTargetBuffer(
237255
238256/**
239257 *
240- * @param {* } options
258+ * @param {import("./types").DICOMLoaderImageOptions } options
241259 * @param {number } minBeforeScale
242260 * @param {number } maxBeforeScale
243261 * @param {import("@cornerstonejs/core").Types.IImageFrame } imageFrame
@@ -252,27 +270,25 @@ function _handlePreScaleSetup(
252270 const scalingParameters = options . preScale . scalingParameters ;
253271 _validateScalingParameters ( scalingParameters ) ;
254272
255- const { rescaleSlope, rescaleIntercept } = scalingParameters ;
256- const areSlopeAndInterceptNumbers =
257- typeof rescaleSlope === 'number' && typeof rescaleIntercept === 'number' ;
258-
259- let scaledMin = minBeforeScale ;
260- let scaledMax = maxBeforeScale ;
261-
262- if ( areSlopeAndInterceptNumbers ) {
263- scaledMin = rescaleSlope * minBeforeScale + rescaleIntercept ;
264- scaledMax = rescaleSlope * maxBeforeScale + rescaleIntercept ;
265- }
273+ const scaledValues = _calculateScaledMinMax (
274+ minBeforeScale ,
275+ maxBeforeScale ,
276+ scalingParameters
277+ ) ;
266278
267- return _getDefaultPixelDataArray ( scaledMin , scaledMax , imageFrame ) ;
279+ return _getDefaultPixelDataArray (
280+ scaledValues . min ,
281+ scaledValues . max ,
282+ imageFrame
283+ ) ;
268284}
269285
270286/**
271287 *
272288 * @param {number } min
273289 * @param {number } max
274290 * @param {import("@cornerstonejs/core").Types.IImageFrame } imageFrame
275- * @returns
291+ * @returns { import("@cornerstonejs/core").Types.PixelDataTypedArray }
276292 */
277293function _getDefaultPixelDataArray ( min , max , imageFrame ) {
278294 const TypedArrayConstructor = getPixelDataTypeFromMinMax ( min , max ) ;
@@ -283,6 +299,48 @@ function _getDefaultPixelDataArray(min, max, imageFrame) {
283299 return typedArray ;
284300}
285301
302+ /**
303+ *
304+ * @param {number } minValue
305+ * @param {number } maxValue
306+ * @param {import("@cornerstonejs/core").Types.ScalingParameters } scalingParameters
307+ * @returns {{ min: number, max: number } }
308+ */
309+ function _calculateScaledMinMax ( minValue , maxValue , scalingParameters ) {
310+ // @ts -expect-error ScalingParameters type does not include `doseGridScaling`
311+ const { rescaleSlope, rescaleIntercept, modality, doseGridScaling, suvbw } =
312+ scalingParameters ;
313+
314+ if ( modality === 'PT' && typeof suvbw === 'number' && ! isNaN ( suvbw ) ) {
315+ return {
316+ min : suvbw * ( minValue * rescaleSlope + rescaleIntercept ) ,
317+ max : suvbw * ( maxValue * rescaleSlope + rescaleIntercept ) ,
318+ } ;
319+ } else if (
320+ modality === 'RTDOSE' &&
321+ typeof doseGridScaling === 'number' &&
322+ ! isNaN ( doseGridScaling )
323+ ) {
324+ return {
325+ min : minValue * doseGridScaling ,
326+ max : maxValue * doseGridScaling ,
327+ } ;
328+ } else if (
329+ typeof rescaleSlope === 'number' &&
330+ typeof rescaleIntercept === 'number'
331+ ) {
332+ return {
333+ min : rescaleSlope * minValue + rescaleIntercept ,
334+ max : rescaleSlope * maxValue + rescaleIntercept ,
335+ } ;
336+ } else {
337+ return {
338+ min : minValue ,
339+ max : maxValue ,
340+ } ;
341+ }
342+ }
343+
286344function _validateScalingParameters ( scalingParameters ) {
287345 if ( ! scalingParameters ) {
288346 throw new Error (
0 commit comments