Skip to content

Commit 951dc8e

Browse files
authored
refactor: 💡 Combine rescale computation with slice insertion (#47)
* refactor: 💡 Combine rescale computation with slice insertion Combine rescale computation with slice insertion into volume. Closes: #41 * Removed unused typed array.
1 parent b642773 commit 951dc8e

File tree

2 files changed

+112
-91
lines changed

2 files changed

+112
-91
lines changed

‎src/lib/data/insertSlice.js‎

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import computeImageDataIncrements from './computeImageDataIncrements.js';
22
import computeIndex from './computeIndex.js';
3+
import cornerstone from 'cornerstone-core';
34

45
// insert the slice at the z index location.
5-
export default function insertSlice(imageData, pixels, index) {
6+
export default function insertSlice(imageData, index, image) {
7+
const pixels = image.getPixelData();
8+
const scalingParameters = _calculateScalingParametersForModality(image);
9+
610
const datasetDefinition = imageData.get('extent', 'spacing', 'origin');
711
const scalars = imageData.getPointData().getScalars();
812
const increments = computeImageDataIncrements(imageData, 1); // TODO number of components.
@@ -20,8 +24,113 @@ export default function insertSlice(imageData, pixels, index) {
2024
increments,
2125
indexXYZ
2226
);
23-
scalarData[destIdx] = pixels[pixelIndex++];
27+
const pixel = pixels[pixelIndex];
28+
29+
scalarData[destIdx] = _getModalityPixelsOrSUV(pixel, scalingParameters);
30+
31+
pixelIndex++;
2432
}
2533
}
2634
imageData.modified();
2735
}
36+
37+
function _calculateScalingParametersForModality(image) {
38+
const patientStudyModule = cornerstone.metaData.get(
39+
'patientStudyModule',
40+
image.imageId
41+
);
42+
const seriesModule = cornerstone.metaData.get(
43+
'generalSeriesModule',
44+
image.imageId
45+
);
46+
47+
if (!patientStudyModule) {
48+
throw new Error('patientStudyModule metadata is required');
49+
}
50+
51+
if (!seriesModule) {
52+
throw new Error('seriesModule metadata is required');
53+
}
54+
55+
const modality = seriesModule.modality;
56+
57+
const scalingParameters = {
58+
slope: image.slope,
59+
intercept: image.intercept,
60+
modality,
61+
};
62+
63+
if (modality === 'PT') {
64+
const patientWeight = patientStudyModule.patientWeight; // In kg
65+
66+
if (!patientWeight) {
67+
throw new Error(
68+
'patientWeight must be present in patientStudyModule for modality PT'
69+
);
70+
}
71+
72+
const petSequenceModule = cornerstone.metaData.get(
73+
'petIsotopeModule',
74+
image.imageId
75+
);
76+
77+
if (!petSequenceModule) {
78+
throw new Error('petSequenceModule metadata is required');
79+
}
80+
81+
// TODO:
82+
// - Update this to match the SUV logic provided here:
83+
// https://github.com/salimkanoun/fijiPlugins/blob/master/Pet_Ct_Viewer/src/SUVDialog.java
84+
// - Test with PET datasets from various providers to ensure SUV is correct
85+
const radiopharmaceuticalInfo = petSequenceModule.radiopharmaceuticalInfo;
86+
const startTime = radiopharmaceuticalInfo.radiopharmaceuticalStartTime;
87+
const totalDose = radiopharmaceuticalInfo.radionuclideTotalDose;
88+
const halfLife = radiopharmaceuticalInfo.radionuclideHalfLife;
89+
const seriesAcquisitionTime = seriesModule.seriesTime;
90+
91+
if (!startTime || !totalDose || !halfLife || !seriesAcquisitionTime) {
92+
throw new Error(
93+
'The required radiopharmaceutical information was not present.'
94+
);
95+
}
96+
97+
const acquisitionTimeInSeconds =
98+
fracToDec(seriesAcquisitionTime.fractionalSeconds || 0) +
99+
seriesAcquisitionTime.seconds +
100+
seriesAcquisitionTime.minutes * 60 +
101+
seriesAcquisitionTime.hours * 60 * 60;
102+
const injectionStartTimeInSeconds =
103+
fracToDec(startTime.fractionalSeconds) +
104+
startTime.seconds +
105+
startTime.minutes * 60 +
106+
startTime.hours * 60 * 60;
107+
const durationInSeconds =
108+
acquisitionTimeInSeconds - injectionStartTimeInSeconds;
109+
const correctedDose =
110+
totalDose * Math.exp((-durationInSeconds * Math.log(2)) / halfLife);
111+
112+
scalingParameters.patientWeight = patientWeight;
113+
scalingParameters.correctedDose = correctedDose;
114+
}
115+
116+
return scalingParameters;
117+
}
118+
119+
function _getModalityPixelsOrSUV(pixel, scalingParameters) {
120+
if (scalingParameters.modality === 'PT') {
121+
const {
122+
slope,
123+
intercept,
124+
patientWeight,
125+
correctedDose,
126+
} = scalingParameters;
127+
128+
const modalityPixelValue = pixel * slope + intercept;
129+
const suv = (1000 * modalityPixelValue * patientWeight) / correctedDose;
130+
return suv;
131+
}
132+
133+
const { slope, intercept } = scalingParameters;
134+
135+
return pixel * slope + intercept;
136+
}

‎src/lib/loadImageData.js‎

Lines changed: 1 addition & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -8,96 +8,8 @@ function loadImageDataProgressively(imageIds, imageData, metaDataMap, zAxis) {
88
const insertPixelData = image => {
99
const { imagePositionPatient } = metaDataMap.get(image.imageId);
1010
const sliceIndex = getSliceIndex(zAxis, imagePositionPatient);
11-
const pixels = image.getPixelData();
12-
const { slope, intercept } = image;
13-
const numPixels = pixels.length;
1411

15-
// TODO: Sometimes after scaling from stored pixel value to modality
16-
// pixel value, the result is negative. In this case, we need to use a
17-
// signed array. I've hardcoded Int16 for now but I guess we can try to
18-
// figure out if Int8 is also an option.
19-
const modalityPixelsOrSUV = new Int16Array(numPixels);
20-
21-
const patientStudyModule = cornerstone.metaData.get(
22-
'patientStudyModule',
23-
image.imageId
24-
);
25-
const seriesModule = cornerstone.metaData.get(
26-
'generalSeriesModule',
27-
image.imageId
28-
);
29-
30-
if (!patientStudyModule) {
31-
throw new Error('patientStudyModule metadata is required');
32-
}
33-
34-
if (!seriesModule) {
35-
throw new Error('seriesModule metadata is required');
36-
}
37-
38-
const modality = seriesModule.modality;
39-
40-
if (modality === 'PT') {
41-
const patientWeight = patientStudyModule.patientWeight; // In kg
42-
43-
if (!patientWeight) {
44-
throw new Error(
45-
'patientWeight must be present in patientStudyModule for modality PT'
46-
);
47-
}
48-
49-
const petSequenceModule = cornerstone.metaData.get(
50-
'petIsotopeModule',
51-
image.imageId
52-
);
53-
54-
if (!petSequenceModule) {
55-
throw new Error('petSequenceModule metadata is required');
56-
}
57-
58-
// TODO:
59-
// - Update this to match the SUV logic provided here:
60-
// https://github.com/salimkanoun/fijiPlugins/blob/master/Pet_Ct_Viewer/src/SUVDialog.java
61-
// - Test with PET datasets from various providers to ensure SUV is correct
62-
const radiopharmaceuticalInfo = petSequenceModule.radiopharmaceuticalInfo;
63-
const startTime = radiopharmaceuticalInfo.radiopharmaceuticalStartTime;
64-
const totalDose = radiopharmaceuticalInfo.radionuclideTotalDose;
65-
const halfLife = radiopharmaceuticalInfo.radionuclideHalfLife;
66-
const seriesAcquisitionTime = seriesModule.seriesTime;
67-
68-
if (!startTime || !totalDose || !halfLife || !seriesAcquisitionTime) {
69-
throw new Error(
70-
'The required radiopharmaceutical information was not present.'
71-
);
72-
}
73-
74-
const acquisitionTimeInSeconds =
75-
fracToDec(seriesAcquisitionTime.fractionalSeconds || 0) +
76-
seriesAcquisitionTime.seconds +
77-
seriesAcquisitionTime.minutes * 60 +
78-
seriesAcquisitionTime.hours * 60 * 60;
79-
const injectionStartTimeInSeconds =
80-
fracToDec(startTime.fractionalSeconds) +
81-
startTime.seconds +
82-
startTime.minutes * 60 +
83-
startTime.hours * 60 * 60;
84-
const durationInSeconds =
85-
acquisitionTimeInSeconds - injectionStartTimeInSeconds;
86-
const correctedDose =
87-
totalDose * Math.exp((-durationInSeconds * Math.log(2)) / halfLife);
88-
89-
for (let i = 0; i < numPixels; i++) {
90-
const modalityPixelValue = pixels[i] * slope + intercept;
91-
const suv = (1000 * modalityPixelValue * patientWeight) / correctedDose;
92-
modalityPixelsOrSUV[i] = suv;
93-
}
94-
} else {
95-
for (let i = 0; i < numPixels; i++) {
96-
modalityPixelsOrSUV[i] = pixels[i] * slope + intercept;
97-
}
98-
}
99-
100-
insertSlice(imageData, modalityPixelsOrSUV, sliceIndex);
12+
insertSlice(imageData, sliceIndex, image);
10113
};
10214

10315
loadImagePromises.forEach(promise => {

0 commit comments

Comments
 (0)