Skip to content

Commit a7d2a89

Browse files
committed
refactor(dicom-image-loader): add additional types
1 parent 790b535 commit a7d2a89

1 file changed

Lines changed: 94 additions & 36 deletions

File tree

packages/dicomImageLoader/src/decodeImageFrameWorker.js

Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
4546
function 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
*/
186204
function _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
*/
277293
function _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+
286344
function _validateScalingParameters(scalingParameters) {
287345
if (!scalingParameters) {
288346
throw new Error(

0 commit comments

Comments
 (0)