import { api } from 'dicomweb-client'; import dcmjs from 'dcmjs'; import { calculateSUVScalingFactors } from '@cornerstonejs/calculate-suv'; import { getPTImageIdInstanceMetadata } from './getPTImageIdInstanceMetadata'; import { utilities } from '@cornerstonejs/core'; import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'; import ptScalingMetaDataProvider from './ptScalingMetaDataProvider'; import getPixelSpacingInformation from './getPixelSpacingInformation'; import { convertMultiframeImageIds } from './convertMultiframeImageIds'; import removeInvalidTags from './removeInvalidTags'; const { DicomMetaDictionary } = dcmjs.data; const { calibratedPixelSpacingMetadataProvider } = utilities; /** /** * Uses dicomweb-client to fetch metadata of a study, cache it in cornerstone, * and return a list of imageIds for the frames. * * Uses the app config to choose which study to fetch, and which * dicom-web server to fetch it from. * * @returns {string[]} An array of imageIds for instances in the study. */ export default async function createImageIdsAndCacheMetaData({ StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID = null, wadoRsRoot, client = null, }) { const SOP_INSTANCE_UID = '00080018'; const SERIES_INSTANCE_UID = '0020000E'; const MODALITY = '00080060'; const studySearchOptions = { studyInstanceUID: StudyInstanceUID, seriesInstanceUID: SeriesInstanceUID, }; client = client || new api.DICOMwebClient({ url: wadoRsRoot }); const instances = await client.retrieveSeriesMetadata(studySearchOptions); const modality = instances[0][MODALITY].Value[0]; let imageIds = instances.map((instanceMetaData) => { const SeriesInstanceUID = instanceMetaData[SERIES_INSTANCE_UID].Value[0]; const SOPInstanceUIDToUse = SOPInstanceUID || instanceMetaData[SOP_INSTANCE_UID].Value[0]; const prefix = 'wadors:'; const imageId = prefix + wadoRsRoot + '/studies/' + StudyInstanceUID + '/series/' + SeriesInstanceUID + '/instances/' + SOPInstanceUIDToUse + '/frames/1'; cornerstoneDICOMImageLoader.wadors.metaDataManager.add( imageId, instanceMetaData ); return imageId; }); // if the image ids represent multiframe information, creates a new list with one image id per frame // if not multiframe data available, just returns the same list given imageIds = convertMultiframeImageIds(imageIds); imageIds.forEach((imageId) => { let instanceMetaData = cornerstoneDICOMImageLoader.wadors.metaDataManager.get(imageId); // It was using JSON.parse(JSON.stringify(...)) before but it is 8x slower instanceMetaData = removeInvalidTags(instanceMetaData); if (instanceMetaData) { // Add calibrated pixel spacing const metadata = DicomMetaDictionary.naturalizeDataset(instanceMetaData); const pixelSpacing = getPixelSpacingInformation(metadata); if (pixelSpacing) { calibratedPixelSpacingMetadataProvider.add(imageId, { rowPixelSpacing: parseFloat(pixelSpacing[0]), columnPixelSpacing: parseFloat(pixelSpacing[1]), }); } } }); // we don't want to add non-pet // Note: for 99% of scanners SUV calculation is consistent bw slices if (modality === 'PT') { const InstanceMetadataArray = []; imageIds.forEach((imageId) => { const instanceMetadata = getPTImageIdInstanceMetadata(imageId); // TODO: Temporary fix because static-wado is producing a string, not an array of values // (or maybe dcmjs isn't parsing it correctly?) // It's showing up like 'DECY\\ATTN\\SCAT\\DTIM\\RAN\\RADL\\DCAL\\SLSENS\\NORM' // but calculate-suv expects ['DECY', 'ATTN', ...] if (typeof instanceMetadata.CorrectedImage === 'string') { instanceMetadata.CorrectedImage = instanceMetadata.CorrectedImage.split('\\'); } if (instanceMetadata) { InstanceMetadataArray.push(instanceMetadata); } }); if (InstanceMetadataArray.length) { try { const suvScalingFactors = calculateSUVScalingFactors( InstanceMetadataArray ); InstanceMetadataArray.forEach((instanceMetadata, index) => { ptScalingMetaDataProvider.addInstance( imageIds[index], suvScalingFactors[index] ); }); } catch (error) { console.log(error); } } } return imageIds; }