UNPKG

@cornerstonejs/core

Version:
155 lines (154 loc) 5.68 kB
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction.js'; import { loadAndCacheImage } from '../../loaders/imageLoader.js'; import * as metaData from '../../metaData.js'; import * as windowLevel from '../../utilities/windowLevel.js'; import { MetadataModules, RequestType } from '../../enums/index.js'; import cache from '../../cache/cache.js'; const PRIORITY = 0; const REQUEST_TYPE = RequestType.Prefetch; async function setDefaultVolumeVOI(volumeActor, imageVolume) { const voi = await getDefaultVolumeVOIRange(imageVolume); if (!voi || (voi.lower === 0 && voi.upper === 0) || voi.lower === undefined || voi.upper === undefined) { return; } ensureRGBTransferFunction(volumeActor).setMappingRange(voi.lower, voi.upper); } function ensureRGBTransferFunction(volumeActor) { const property = volumeActor.getProperty(); let transferFunction = property.getRGBTransferFunction(0); if (transferFunction) { return transferFunction; } transferFunction = vtkColorTransferFunction.newInstance(); transferFunction.addRGBPoint(0, 0, 0, 0); transferFunction.addRGBPoint(1, 1, 1, 1); property.setRGBTransferFunction(0, transferFunction); if ('setUseLookupTableScalarRange' in property) { property.setUseLookupTableScalarRange?.(true); } return transferFunction; } export async function getDefaultVolumeVOIRange(imageVolume) { let voi = getVOIFromMetadata(imageVolume); if (!voi && imageVolume.imageIds.length && shouldUseImageIdsForVOI(imageVolume)) { voi = await getVOIFromMiddleSliceMinMax(imageVolume); voi = handlePreScaledVolume(imageVolume, voi); } return voi; } function shouldUseImageIdsForVOI(imageVolume) { const { imageIds, referencedImageIds } = imageVolume; if (referencedImageIds?.length) { return true; } const imageId = imageIds[Math.floor(imageIds.length / 2)]; if (!imageId || imageId.startsWith('derived:')) { return false; } return imageId.includes(':'); } function handlePreScaledVolume(imageVolume, voi) { const imageIds = imageVolume.imageIds; const imageIdIndex = Math.floor(imageIds.length / 2); const imageId = imageIds[imageIdIndex]; const generalSeriesModule = metaData.get(MetadataModules.GENERAL_SERIES, imageId) || {}; if (_isCurrentImagePTPrescaled(generalSeriesModule.modality, imageVolume)) { return { lower: 0, upper: 5, }; } return voi; } function getVOIFromMetadata(imageVolume) { const { imageIds, metadata } = imageVolume; let voi; if (imageIds?.length) { const imageIdIndex = Math.floor(imageIds.length / 2); const imageId = imageIds[imageIdIndex]; const voiLutModule = metaData.get(MetadataModules.VOI_LUT, imageId); if (voiLutModule && voiLutModule.windowWidth && voiLutModule.windowCenter) { if (voiLutModule?.voiLUTFunction) { voi = {}; voi.voiLUTFunction = voiLutModule?.voiLUTFunction; } const { windowWidth, windowCenter } = voiLutModule; const width = Array.isArray(windowWidth) ? windowWidth[0] : windowWidth; const center = Array.isArray(windowCenter) ? windowCenter[0] : windowCenter; if (width !== 0) { voi = { windowWidth: width, windowCenter: center }; } } } else { voi = metadata.voiLut[0]; } if (voi && (voi.windowWidth !== 0 || voi.windowCenter !== 0)) { const { lower, upper } = windowLevel.toLowHighRange(Number(voi.windowWidth), Number(voi.windowCenter), voi.voiLUTFunction); if (isNaN(lower) || isNaN(upper)) { return; } return { lower, upper }; } return undefined; } async function getVOIFromMiddleSliceMinMax(imageVolume) { const { imageIds } = imageVolume; const imageIdIndex = Math.floor(imageIds.length / 2); const imageId = imageVolume.imageIds[imageIdIndex]; const generalSeriesModule = metaData.get(MetadataModules.GENERAL_SERIES, imageId) || {}; const { modality } = generalSeriesModule; const modalityLutModule = metaData.get(MetadataModules.MODALITY_LUT, imageId) || {}; const scalingParameters = { rescaleSlope: modalityLutModule.rescaleSlope, rescaleIntercept: modalityLutModule.rescaleIntercept, modality, }; let scalingParametersToUse; if (modality === 'PT') { const suvFactor = metaData.get(MetadataModules.SCALING, imageId); if (suvFactor) { scalingParametersToUse = { ...scalingParameters, suvbw: suvFactor.suvbw, }; } } const options = { priority: PRIORITY, requestType: REQUEST_TYPE, preScale: { scalingParameters: scalingParametersToUse, }, }; let image = cache.getImage(imageId); if (!imageVolume.referencedImageIds?.length) { image = await loadAndCacheImage(imageId, { ...options, ignoreCache: true }); } let { min, max } = image.voxelManager.getMinMax(); if (min?.length > 1) { min = Math.min(...min); max = Math.max(...max); } return { lower: min, upper: max, }; } function _isCurrentImagePTPrescaled(modality, imageVolume) { if (modality !== 'PT' || !imageVolume.isPreScaled) { return false; } if (!imageVolume.scaling?.PT.suvbw) { return false; } return true; } export default setDefaultVolumeVOI;