diff --git a/packages/core/src/RenderingEngine/StackViewport.ts b/packages/core/src/RenderingEngine/StackViewport.ts index 52b90b1b9a..6571b644aa 100644 --- a/packages/core/src/RenderingEngine/StackViewport.ts +++ b/packages/core/src/RenderingEngine/StackViewport.ts @@ -157,6 +157,7 @@ class StackViewport extends Viewport implements IStackViewport, IImagesLoader { private voiUpdatedWithSetProperties = false; private VOILUTFunction: VOILUTFunctionType; // + private outOfRange = false; private invert = false; // The initial invert of the image loaded as opposed to the invert status of the viewport itself (see above). private initialInvert = false; @@ -1386,7 +1387,8 @@ class StackViewport extends Viewport implements IStackViewport, IImagesLoader { let voiRangeToUse = voiRange; - if (typeof voiRangeToUse === 'undefined') { + if (typeof voiRangeToUse === 'undefined' || this.outOfRange) { + this.outOfRange = false; const imageData = imageActor.getMapper().getInputData(); const range = imageData.getPointData().getScalars().getRange(); const maxVoiRange = { lower: range[0], upper: range[1] }; @@ -1782,7 +1784,10 @@ class StackViewport extends Viewport implements IStackViewport, IImagesLoader { // Update the pixel data in the vtkImageData object with the pixelData // from the loaded Cornerstone image - updateVTKImageDataWithCornerstoneImage(this._imageData, image); + this.outOfRange = updateVTKImageDataWithCornerstoneImage( + this._imageData, + image + ); } /** @@ -3047,7 +3052,6 @@ class StackViewport extends Viewport implements IStackViewport, IImagesLoader { }; triggerEvent(this.element, Events.COLORMAP_MODIFIED, eventDetail); - } private unsetColormapGPU() { diff --git a/packages/core/src/RenderingEngine/helpers/normalizePixels.ts b/packages/core/src/RenderingEngine/helpers/normalizePixels.ts new file mode 100644 index 0000000000..3f362e8544 --- /dev/null +++ b/packages/core/src/RenderingEngine/helpers/normalizePixels.ts @@ -0,0 +1,51 @@ +import { IImage } from '../../types'; +import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray'; +import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData'; +import generateLut from './cpuFallback/rendering/generateLut'; + +export function normalizePixels(vtkImage: vtkImageData, image: IImage): any { + const { windowCenter, windowWidth } = image; + const width = Array.isArray(windowWidth) ? windowWidth[0] : windowWidth; + const center = Array.isArray(windowCenter) ? windowCenter[0] : windowCenter; + + const lut = generateLut(image, width, center, false, undefined, undefined); + const pixelData = image.getPixelData(); + + const numPixels = pixelData.length; + const minPixelValue = image.minPixelValue; + let storedPixelDataIndex = 0; + + const numComp = vtkImage.getPointData().getNumberOfComponents(); + const textureData = new Float32Array(numPixels); + for (let i = 0; i < numPixels; i++) { + textureData[i * numComp] = 255; // Red + textureData[i * numComp + 1] = 255; // Green + textureData[i * numComp + 2] = 255; // Blue + textureData[i * numComp + 3] = 255; // Alpha + } + + if (pixelData instanceof Float32Array) { + while (storedPixelDataIndex < numPixels) { + if (minPixelValue < 0) { + textureData[storedPixelDataIndex * numComp + 3] = + lut[pixelData[storedPixelDataIndex++] + -minPixelValue]; // Alpha + } else { + textureData[storedPixelDataIndex * numComp + 3] = + lut[pixelData[storedPixelDataIndex++]]; // Alpha + } + } + } else { + while (storedPixelDataIndex < numPixels) { + textureData[storedPixelDataIndex * numComp + 3] = + lut[pixelData[storedPixelDataIndex++]]; // Alpha + } + } + + const dataArray = vtkDataArray.newInstance({ + name: 'Pixels', + numberOfComponents: numComp, + values: textureData, + }); + + return dataArray; +} diff --git a/packages/core/src/utilities/updateVTKImageDataWithCornerstoneImage.ts b/packages/core/src/utilities/updateVTKImageDataWithCornerstoneImage.ts index 193e7a6867..f5ae718660 100644 --- a/packages/core/src/utilities/updateVTKImageDataWithCornerstoneImage.ts +++ b/packages/core/src/utilities/updateVTKImageDataWithCornerstoneImage.ts @@ -1,10 +1,12 @@ import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData'; import { IImage, PixelDataTypedArray } from '../types'; +import { normalizePixels } from '../RenderingEngine/helpers/normalizePixels'; function updateVTKImageDataWithCornerstoneImage( sourceImageData: vtkImageData, image: IImage ) { + let outOfRange = false; const pixelData = image.getPixelData(); if (!sourceImageData.getPointData) { // This happens for a CanvasActor, that doesn't have the getPointData @@ -31,12 +33,22 @@ function updateVTKImageDataWithCornerstoneImage( image.getPixelData = () => newPixelData; scalarData.set(newPixelData); } else { - scalarData.set(pixelData); + const maxPixelValue = image.maxPixelValue; + const minPixelValue = image.minPixelValue; + + if (maxPixelValue < 0 && minPixelValue < 0) { + outOfRange = true; + const newPixelData = normalizePixels(sourceImageData, image); + sourceImageData.getPointData().setScalars(newPixelData); + } else { + scalarData.set(pixelData); + } } // Trigger modified on the VTK Object so the texture is updated // TODO: evaluate directly changing things with texSubImage3D later sourceImageData.modified(); + return outOfRange; } export { updateVTKImageDataWithCornerstoneImage };