Skip to content

Commit 3f0eaec

Browse files
authored
fix: πŸ› Switch to orienting the volume actor differently (OHIF#70)
* fix: πŸ› Switch to orienting the volume actor differently Switch to orienting the volume actor differently depending on its acquisition direction, rather than filling up the volume differently. * Address Steve's comments on the PR.
1 parent cec8579 commit 3f0eaec

File tree

5 files changed

+9
-232
lines changed

5 files changed

+9
-232
lines changed

β€Žsrc/VTKViewport/View2D.js

+2
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ export default class View2D extends Component {
315315
istyle.setViewport(currentViewport);
316316
}
317317

318+
istyle.getVolumeMapper();
319+
318320
if (istyle.getVolumeMapper() !== volumes[0]) {
319321
if (slabThickness && istyle.setSlabThickness) {
320322
istyle.setSlabThickness(slabThickness);

β€Žsrc/VTKViewport/vtkInteractorStyleMPRSlice.js

+3-16
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,7 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) {
116116
const _viewUp = [...viewUp];
117117

118118
if (model.volumeMapper) {
119-
let mapper = model.volumeMapper;
120-
// get the mapper if the model is actually the actor, not the mapper
121-
if (!model.volumeMapper.getInputData && model.volumeMapper.getMapper) {
122-
mapper = model.volumeMapper.getMapper();
123-
}
124-
let volumeCoordinateSpace = vec9toMat3(
125-
mapper.getInputData().getDirection()
126-
);
119+
let volumeCoordinateSpace = vec9toMat3([1, 0, 0, 0, 1, 0, 0, 0, 1]);
127120
// Transpose the volume's coordinate space to create a transformation matrix
128121
vtkMath.transpose3x3(volumeCoordinateSpace, volumeCoordinateSpace);
129122

@@ -142,14 +135,8 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) {
142135

143136
if (model.volumeMapper) {
144137
vtkMath.normalize(_normal);
145-
let mapper = model.volumeMapper;
146-
// get the mapper if the model is actually the actor, not the mapper
147-
if (!model.volumeMapper.getInputData && model.volumeMapper.getMapper) {
148-
mapper = model.volumeMapper.getMapper();
149-
}
150-
let volumeCoordinateSpace = vec9toMat3(
151-
mapper.getInputData().getDirection()
152-
);
138+
139+
let volumeCoordinateSpace = vec9toMat3([1, 0, 0, 0, 1, 0, 0, 0, 1]);
153140
// Transpose the volume's coordinate space to create a transformation matrix
154141
vtkMath.transpose3x3(volumeCoordinateSpace, volumeCoordinateSpace);
155142
// Convert the provided normal into the volume's space

β€Žsrc/lib/data/insertSlice.js

+1-160
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
*
33
* @param {Object} imageData - The vtkImageData
44
* @param {*} sliceIndex - The index of the slice you are inserting.
5-
* @param {*} acquistionDirection - The acquistion direction of the slice.
65
* @param {*} image The cornerstone image to pull pixel data data from.
76
* @param {*} modality The modality of the image.
87
* @param {*} modalitySpecificScalingParameters Specific scaling paramaters for this modality. E.g. Patient weight.
98
*/
109
export default function insertSlice(
1110
imageData,
1211
sliceIndex,
13-
acquistionDirection,
1412
image,
1513
modality,
1614
modalitySpecificScalingParameters
@@ -24,163 +22,6 @@ export default function insertSlice(
2422
modalitySpecificScalingParameters
2523
);
2624

27-
const vtkImageDimensions = imageData.getDimensions();
28-
29-
let minAndMax;
30-
31-
switch (acquistionDirection) {
32-
case 'coronal':
33-
minAndMax = insertCoronalSlice(
34-
image,
35-
sliceIndex,
36-
vtkImageDimensions,
37-
scalarData,
38-
scalingFunction
39-
);
40-
break;
41-
case 'sagittal':
42-
minAndMax = insertSagittalSlice(
43-
image,
44-
sliceIndex,
45-
vtkImageDimensions,
46-
scalarData,
47-
scalingFunction
48-
);
49-
break;
50-
case 'axial':
51-
minAndMax = insertAxialSlice(
52-
image,
53-
sliceIndex,
54-
scalarData,
55-
scalingFunction
56-
);
57-
break;
58-
}
59-
60-
return minAndMax;
61-
}
62-
63-
/**
64-
*
65-
* @param {object} image The cornerstone image to pull pixel data data from.
66-
* @param {number} xIndex The x index of axially oriented vtk volume to put the sagital slice.
67-
* @param {number[]} vtkImageDimensions The dimensions of the axially oriented vtk volume.
68-
* @param {number[]} scalarData The data array for the axially oriented vtk volume.
69-
* @param {function} scalingFunction The modality specific scaling function.
70-
*
71-
* @returns {object} The min and max pixel values in the inserted slice.
72-
*/
73-
function insertSagittalSlice(
74-
image,
75-
xIndex,
76-
vtkImageDimensions,
77-
scalarData,
78-
scalingFunction
79-
) {
80-
const pixels = image.getPixelData();
81-
const { rows, columns } = image;
82-
83-
let pixelIndex = 0;
84-
let max = scalingFunction(pixels[pixelIndex]);
85-
let min = max;
86-
87-
const vtkImageDimensionsX = vtkImageDimensions[0];
88-
const vtkImageDimensionsY = vtkImageDimensions[1];
89-
const vtkImageDimensionsZ = vtkImageDimensions[2];
90-
91-
const axialSliceLength = vtkImageDimensionsX * vtkImageDimensionsY;
92-
93-
for (let row = 0; row < rows; row++) {
94-
for (let col = 0; col < columns; col++) {
95-
const yPos = vtkImageDimensionsY - col;
96-
const zPos = vtkImageDimensionsZ - row;
97-
98-
const destIdx =
99-
zPos * axialSliceLength + yPos * vtkImageDimensionsX + xIndex;
100-
101-
const pixel = pixels[pixelIndex];
102-
const pixelValue = scalingFunction(pixel);
103-
104-
if (pixelValue > max) {
105-
max = pixelValue;
106-
} else if (pixelValue < min) {
107-
min = pixelValue;
108-
}
109-
110-
scalarData[destIdx] = pixelValue;
111-
pixelIndex++;
112-
}
113-
}
114-
115-
return { min, max };
116-
}
117-
118-
/**
119-
*
120-
* @param {object} image The cornerstone image to pull pixel data data from.
121-
* @param {number} yIndex The y index of axially oriented vtk volume to put the coronal slice.
122-
* @param {number[]} vtkImageDimensions The dimensions of the axially oriented vtk volume.
123-
* @param {number[]} scalarData The data array for the axially oriented vtk volume.
124-
* @param {function} scalingFunction The modality specific scaling function.
125-
*
126-
* @returns {object} The min and max pixel values in the inserted slice.
127-
*/
128-
function insertCoronalSlice(
129-
image,
130-
yIndex,
131-
vtkImageDimensions,
132-
scalarData,
133-
scalingFunction
134-
) {
135-
const pixels = image.getPixelData();
136-
const { rows, columns } = image;
137-
138-
let pixelIndex = 0;
139-
let max = scalingFunction(pixels[pixelIndex]);
140-
let min = max;
141-
142-
const vtkImageDimensionsX = vtkImageDimensions[0];
143-
const vtkImageDimensionsY = vtkImageDimensions[1];
144-
const vtkImageDimensionsZ = vtkImageDimensions[2];
145-
146-
const axialSliceLength = vtkImageDimensionsX * vtkImageDimensionsY;
147-
148-
for (let row = 0; row < rows; row++) {
149-
for (let col = 0; col < columns; col++) {
150-
const xPos = col;
151-
const yPos = yIndex;
152-
const zPos = vtkImageDimensionsZ - row;
153-
154-
const destIdx =
155-
zPos * axialSliceLength + yPos * vtkImageDimensionsX + xPos;
156-
157-
const pixel = pixels[pixelIndex];
158-
const pixelValue = scalingFunction(pixel);
159-
160-
if (pixelValue > max) {
161-
max = pixelValue;
162-
} else if (pixelValue < min) {
163-
min = pixelValue;
164-
}
165-
166-
scalarData[destIdx] = pixelValue;
167-
pixelIndex++;
168-
}
169-
}
170-
171-
return { min, max };
172-
}
173-
174-
/**
175-
*
176-
* @param {object} image The cornerstone image to pull pixel data data from.
177-
* @param {number} zIndex The z index of axially oriented vtk volume to put the axial slice.
178-
* @param {number[]} scalarData The data array for the axially oriented vtk volume.
179-
* @param {function} scalingFunction The modality specific scaling function.
180-
*
181-
* @returns {object} The min and max pixel values in the inserted slice.
182-
*/
183-
function insertAxialSlice(image, zIndex, scalarData, scalingFunction) {
18425
const pixels = image.getPixelData();
18526
const sliceLength = pixels.length;
18627

@@ -189,7 +30,7 @@ function insertAxialSlice(image, zIndex, scalarData, scalingFunction) {
18930
let min = max;
19031

19132
for (let pixelIndex = 0; pixelIndex < pixels.length; pixelIndex++) {
192-
const destIdx = pixelIndex + zIndex * sliceLength;
33+
const destIdx = pixelIndex + sliceIndex * sliceLength;
19334
const pixel = pixels[pixelIndex];
19435
const pixelValue = scalingFunction(pixel);
19536

β€Žsrc/lib/getImageData.js

+3-46
Original file line numberDiff line numberDiff line change
@@ -87,27 +87,10 @@ export default function getImageData(imageIds, displaySetInstanceUid) {
8787
scanAxisNormal.z,
8888
];
8989

90-
const acquistionDirection = _getAcquisitionDirection(scanAxisNormal);
90+
imageData.setDimensions(xVoxels, yVoxels, zVoxels);
91+
imageData.setSpacing(xSpacing, ySpacing, zSpacing);
9192

92-
console.log(rowCosineVec);
93-
console.log(colCosineVec);
94-
console.log(scanAxisNormal);
95-
console.log(acquistionDirection);
96-
97-
switch (acquistionDirection) {
98-
case 'sagittal':
99-
imageData.setDimensions(zVoxels, xVoxels, yVoxels);
100-
imageData.setSpacing(zSpacing, xSpacing, ySpacing);
101-
break;
102-
case 'coronal':
103-
imageData.setDimensions(xVoxels, zVoxels, yVoxels);
104-
imageData.setSpacing(xSpacing, zSpacing, ySpacing);
105-
break;
106-
case 'axial':
107-
imageData.setDimensions(xVoxels, yVoxels, zVoxels);
108-
imageData.setSpacing(xSpacing, ySpacing, zSpacing);
109-
break;
110-
}
93+
imageData.setDirection(direction);
11194

11295
imageData.setOrigin(...origin);
11396
imageData.getPointData().setScalars(scalarArray);
@@ -123,36 +106,10 @@ export default function getImageData(imageIds, displaySetInstanceUid) {
123106
vtkImageData: imageData,
124107
metaDataMap,
125108
sortedDatasets,
126-
acquistionDirection,
127109
loaded: false,
128110
};
129111

130112
imageDataCache.set(displaySetInstanceUid, imageDataObject);
131113

132114
return imageDataObject;
133115
}
134-
135-
const sagittal = new Vector3(1, 0, 0);
136-
const coronal = new Vector3(0, 1, 0);
137-
const axial = new Vector3(0, 0, 1);
138-
139-
function _getAcquisitionDirection(zDirection) {
140-
const zAbs = new Vector3(
141-
Math.abs(zDirection.x),
142-
Math.abs(zDirection.y),
143-
Math.abs(zDirection.z)
144-
);
145-
146-
// Get the direction of the acquisition.
147-
const dotProducts = [
148-
zAbs.dot(sagittal), // z . Sagittal
149-
zAbs.dot(coronal), // z . Coronal
150-
zAbs.dot(axial), // z . Axial
151-
];
152-
153-
const directionIndex = dotProducts.indexOf(Math.max(...dotProducts));
154-
155-
const acquisitionDirections = ['sagittal', 'coronal', 'axial'];
156-
157-
return acquisitionDirections[directionIndex];
158-
}

β€Žsrc/lib/loadImageData.js

-10
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export default function loadImageDataProgressively(imageDataObject) {
1515
vtkImageData,
1616
metaDataMap,
1717
sortedDatasets,
18-
acquistionDirection,
1918
} = imageDataObject;
2019
const loadImagePromises = imageIds.map(cornerstone.loadAndCacheImage);
2120
const imageId0 = imageIds[0];
@@ -69,7 +68,6 @@ export default function loadImageDataProgressively(imageDataObject) {
6968
const { max, min } = insertSlice(
7069
vtkImageData,
7170
sliceIndex,
72-
acquistionDirection,
7371
image,
7472
modality,
7573
modalitySpecificScalingParameters
@@ -114,12 +112,4 @@ export default function loadImageDataProgressively(imageDataObject) {
114112
});
115113

116114
imageDataObject.insertPixelDataPromises = insertPixelDataPromises;
117-
118-
// TODO: Investigate progressive loading. Right now the UI gets super slow because
119-
// we are rendering and decoding simultaneously. We might want to use fewer web workers
120-
// for the decoding tasks.
121-
//
122-
// Update: Had some success with this locally. But it completely freezes up when stuck
123-
// In an app like OHIF. There seems to be many small calls made to various vtk functions.
124-
// Putting it aside for now, but a progressive loader still shows promise.
125115
}

0 commit comments

Comments
Β (0)