diff --git a/extensions/default/src/getSopClassHandlerModule.js b/extensions/default/src/getSopClassHandlerModule.js index e2dba9a64..359cd4cec 100644 --- a/extensions/default/src/getSopClassHandlerModule.js +++ b/extensions/default/src/getSopClassHandlerModule.js @@ -1,4 +1,5 @@ import { isImage } from '@ohif/core/src/utils/isImage'; +import sopClassDictionary from '@ohif/core/src/utils/sopClassDictionary'; import ImageSet from '@ohif/core/src/classes/ImageSet'; import isDisplaySetReconstructable from '@ohif/core/src/utils/isDisplaySetReconstructable'; import { id } from './id'; @@ -143,138 +144,6 @@ function getDisplaySetsFromSeries(instances) { return displaySets; } -// TODO: Remove since we have roughly the same thing in dcmjs -const sopClassDictionary = { - ComputedRadiographyImageStorage: '1.2.840.10008.5.1.4.1.1.1', - DigitalXRayImageStorageForPresentation: '1.2.840.10008.5.1.4.1.1.1.1', - DigitalXRayImageStorageForProcessing: '1.2.840.10008.5.1.4.1.1.1.1.1', - DigitalMammographyXRayImageStorageForPresentation: - '1.2.840.10008.5.1.4.1.1.1.2', - DigitalMammographyXRayImageStorageForProcessing: - '1.2.840.10008.5.1.4.1.1.1.2.1', - DigitalIntraOralXRayImageStorageForPresentation: - '1.2.840.10008.5.1.4.1.1.1.3', - DigitalIntraOralXRayImageStorageForProcessing: - '1.2.840.10008.5.1.4.1.1.1.3.1', - CTImageStorage: '1.2.840.10008.5.1.4.1.1.2', - EnhancedCTImageStorage: '1.2.840.10008.5.1.4.1.1.2.1', - LegacyConvertedEnhancedCTImageStorage: '1.2.840.10008.5.1.4.1.1.2.2', - UltrasoundMultiframeImageStorage: '1.2.840.10008.5.1.4.1.1.3.1', - MRImageStorage: '1.2.840.10008.5.1.4.1.1.4', - EnhancedMRImageStorage: '1.2.840.10008.5.1.4.1.1.4.1', - MRSpectroscopyStorage: '1.2.840.10008.5.1.4.1.1.4.2', - EnhancedMRColorImageStorage: '1.2.840.10008.5.1.4.1.1.4.3', - LegacyConvertedEnhancedMRImageStorage: '1.2.840.10008.5.1.4.1.1.4.4', - UltrasoundImageStorageRET: '1.2.840.10008.5.1.4.1.1.6', - UltrasoundImageStorage: '1.2.840.10008.5.1.4.1.1.6.1', - EnhancedUSVolumeStorage: '1.2.840.10008.5.1.4.1.1.6.2', - SecondaryCaptureImageStorage: '1.2.840.10008.5.1.4.1.1.7', - MultiframeSingleBitSecondaryCaptureImageStorage: - '1.2.840.10008.5.1.4.1.1.7.1', - MultiframeGrayscaleByteSecondaryCaptureImageStorage: - '1.2.840.10008.5.1.4.1.1.7.2', - MultiframeGrayscaleWordSecondaryCaptureImageStorage: - '1.2.840.10008.5.1.4.1.1.7.3', - MultiframeTrueColorSecondaryCaptureImageStorage: - '1.2.840.10008.5.1.4.1.1.7.4', - Sop12LeadECGWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.1.1', - GeneralECGWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.1.2', - AmbulatoryECGWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.1.3', - HemodynamicWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.2.1', - CardiacElectrophysiologyWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.3.1', - BasicVoiceAudioWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.4.1', - GeneralAudioWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.4.2', - ArterialPulseWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.5.1', - RespiratoryWaveformStorage: '1.2.840.10008.5.1.4.1.1.9.6.1', - GrayscaleSoftcopyPresentationStateStorage: '1.2.840.10008.5.1.4.1.1.11.1', - ColorSoftcopyPresentationStateStorage: '1.2.840.10008.5.1.4.1.1.11.2', - PseudoColorSoftcopyPresentationStateStorage: '1.2.840.10008.5.1.4.1.1.11.3', - BlendingSoftcopyPresentationStateStorage: '1.2.840.10008.5.1.4.1.1.11.4', - XAXRFGrayscaleSoftcopyPresentationStateStorage: - '1.2.840.10008.5.1.4.1.1.11.5', - XRayAngiographicImageStorage: '1.2.840.10008.5.1.4.1.1.12.1', - EnhancedXAImageStorage: '1.2.840.10008.5.1.4.1.1.12.1.1', - XRayRadiofluoroscopicImageStorage: '1.2.840.10008.5.1.4.1.1.12.2', - EnhancedXRFImageStorage: '1.2.840.10008.5.1.4.1.1.12.2.1', - XRay3DAngiographicImageStorage: '1.2.840.10008.5.1.4.1.1.13.1.1', - XRay3DCraniofacialImageStorage: '1.2.840.10008.5.1.4.1.1.13.1.2', - BreastTomosynthesisImageStorage: '1.2.840.10008.5.1.4.1.1.13.1.3', - BreastProjectionXRayImageStorageForPresentation: - '1.2.840.10008.5.1.4.1.1.13.1.4', - BreastProjectionXRayImageStorageForProcessing: - '1.2.840.10008.5.1.4.1.1.13.1.5', - IntravascularOpticalCoherenceTomographyImageStorageForPresentation: - '1.2.840.10008.5.1.4.1.1.14.1', - IntravascularOpticalCoherenceTomographyImageStorageForProcessing: - '1.2.840.10008.5.1.4.1.1.14.2', - NuclearMedicineImageStorage: '1.2.840.10008.5.1.4.1.1.20', - RawDataStorage: '1.2.840.10008.5.1.4.1.1.66', - SpatialRegistrationStorage: '1.2.840.10008.5.1.4.1.1.66.1', - SpatialFiducialsStorage: '1.2.840.10008.5.1.4.1.1.66.2', - DeformableSpatialRegistrationStorage: '1.2.840.10008.5.1.4.1.1.66.3', - SegmentationStorage: '1.2.840.10008.5.1.4.1.1.66.4', - SurfaceSegmentationStorage: '1.2.840.10008.5.1.4.1.1.66.5', - RealWorldValueMappingStorage: '1.2.840.10008.5.1.4.1.1.67', - SurfaceScanMeshStorage: '1.2.840.10008.5.1.4.1.1.68.1', - SurfaceScanPointCloudStorage: '1.2.840.10008.5.1.4.1.1.68.2', - VLEndoscopicImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.1', - VideoEndoscopicImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.1.1', - VLMicroscopicImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.2', - VideoMicroscopicImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.2.1', - VLSlideCoordinatesMicroscopicImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.3', - VLPhotographicImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.4', - VideoPhotographicImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.4.1', - OphthalmicPhotography8BitImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.5.1', - OphthalmicPhotography16BitImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.5.2', - StereometricRelationshipStorage: '1.2.840.10008.5.1.4.1.1.77.1.5.3', - OphthalmicTomographyImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.5.4', - VLWholeSlideMicroscopyImageStorage: '1.2.840.10008.5.1.4.1.1.77.1.6', - LensometryMeasurementsStorage: '1.2.840.10008.5.1.4.1.1.78.1', - AutorefractionMeasurementsStorage: '1.2.840.10008.5.1.4.1.1.78.2', - KeratometryMeasurementsStorage: '1.2.840.10008.5.1.4.1.1.78.3', - SubjectiveRefractionMeasurementsStorage: '1.2.840.10008.5.1.4.1.1.78.4', - VisualAcuityMeasurementsStorage: '1.2.840.10008.5.1.4.1.1.78.5', - SpectaclePrescriptionReportStorage: '1.2.840.10008.5.1.4.1.1.78.6', - OphthalmicAxialMeasurementsStorage: '1.2.840.10008.5.1.4.1.1.78.7', - IntraocularLensCalculationsStorage: '1.2.840.10008.5.1.4.1.1.78.8', - MacularGridThicknessandVolumeReport: '1.2.840.10008.5.1.4.1.1.79.1', - OphthalmicVisualFieldStaticPerimetryMeasurementsStorage: - '1.2.840.10008.5.1.4.1.1.80.1', - OphthalmicThicknessMapStorage: '1.2.840.10008.5.1.4.1.1.81.1', - CornealTopographyMapStorage: '1.2.840.10008.5.1.4.1.1.82.1', - BasicTextSR: '1.2.840.10008.5.1.4.1.1.88.11', - EnhancedSR: '1.2.840.10008.5.1.4.1.1.88.22', - ComprehensiveSR: '1.2.840.10008.5.1.4.1.1.88.33', - Comprehensive3DSR: '1.2.840.10008.5.1.4.1.1.88.34', - ProcedureLog: '1.2.840.10008.5.1.4.1.1.88.40', - MammographyCADSR: '1.2.840.10008.5.1.4.1.1.88.50', - KeyObjectSelection: '1.2.840.10008.5.1.4.1.1.88.59', - ChestCADSR: '1.2.840.10008.5.1.4.1.1.88.65', - XRayRadiationDoseSR: '1.2.840.10008.5.1.4.1.1.88.67', - RadiopharmaceuticalRadiationDoseSR: '1.2.840.10008.5.1.4.1.1.88.68', - ColonCADSR: '1.2.840.10008.5.1.4.1.1.88.69', - ImplantationPlanSRDocumentStorage: '1.2.840.10008.5.1.4.1.1.88.70', - EncapsulatedPDFStorage: '1.2.840.10008.5.1.4.1.1.104.1', - EncapsulatedCDAStorage: '1.2.840.10008.5.1.4.1.1.104.2', - PositronEmissionTomographyImageStorage: '1.2.840.10008.5.1.4.1.1.128', - EnhancedPETImageStorage: '1.2.840.10008.5.1.4.1.1.130', - LegacyConvertedEnhancedPETImageStorage: '1.2.840.10008.5.1.4.1.1.128.1', - BasicStructuredDisplayStorage: '1.2.840.10008.5.1.4.1.1.131', - RTImageStorage: '1.2.840.10008.5.1.4.1.1.481.1', - RTDoseStorage: '1.2.840.10008.5.1.4.1.1.481.2', - RTStructureSetStorage: '1.2.840.10008.5.1.4.1.1.481.3', - RTBeamsTreatmentRecordStorage: '1.2.840.10008.5.1.4.1.1.481.4', - RTPlanStorage: '1.2.840.10008.5.1.4.1.1.481.5', - RTBrachyTreatmentRecordStorage: '1.2.840.10008.5.1.4.1.1.481.6', - RTTreatmentSummaryRecordStorage: '1.2.840.10008.5.1.4.1.1.481.7', - RTIonPlanStorage: '1.2.840.10008.5.1.4.1.1.481.8', - RTIonBeamsTreatmentRecordStorage: '1.2.840.10008.5.1.4.1.1.481.9', - RTBeamsDeliveryInstructionStorage: '1.2.840.10008.5.1.4.34.7', - GenericImplantTemplateStorage: '1.2.840.10008.5.1.4.43.1', - ImplantAssemblyTemplateStorage: '1.2.840.10008.5.1.4.44.1', - ImplantTemplateGroupStorage: '1.2.840.10008.5.1.4.45.1', -}; - const sopClassUids = [ sopClassDictionary.ComputedRadiographyImageStorage, sopClassDictionary.DigitalXRayImageStorageForPresentation, @@ -325,6 +194,7 @@ const sopClassUids = [ sopClassDictionary.EnhancedPETImageStorage, sopClassDictionary.LegacyConvertedEnhancedPETImageStorage, sopClassDictionary.RTImageStorage, + sopClassDictionary.EnhancedUSVolumeStorage, ]; function getSopClassHandlerModule() { diff --git a/platform/core/src/classes/MetadataProvider.js b/platform/core/src/classes/MetadataProvider.js index ed6f8d307..d665d31b9 100644 --- a/platform/core/src/classes/MetadataProvider.js +++ b/platform/core/src/classes/MetadataProvider.js @@ -5,6 +5,7 @@ import getPixelSpacingInformation from '../utils/metadataProvider/getPixelSpacin import DicomMetadataStore from '../services/DicomMetadataStore'; import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData'; import toNumber from '../utils/toNumber'; +import combineFrameInstance from '../utils/combineFrameInstance'; class MetadataProvider { constructor() { @@ -57,13 +58,21 @@ class MetadataProvider { return; } - const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = uids; + const { + StudyInstanceUID, + SeriesInstanceUID, + SOPInstanceUID, + frameNumber, + } = uids; - return DicomMetadataStore.getInstance( + const instance = DicomMetadataStore.getInstance( StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID ); + return ( + (frameNumber && combineFrameInstance(frameNumber, instance)) || instance + ); } get(query, imageId, options = { fallback: false }) { @@ -408,7 +417,10 @@ class MetadataProvider { // SOPInstanceUID, // }) // somewhere else - if (imageId.startsWith('wadors:')) { + if ( + imageId.startsWith('wadors:') || + imageId.startsWith('streaming-wadors:') + ) { const strippedImageId = imageId.split('/studies/')[1]; const splitImageId = strippedImageId.split('/'); @@ -416,6 +428,7 @@ class MetadataProvider { StudyInstanceUID: splitImageId[0], // Note: splitImageId[1] === 'series' SeriesInstanceUID: splitImageId[2], // Note: splitImageId[3] === 'instances' SOPInstanceUID: splitImageId[4], + frameNumber: splitImageId[6], }; } else if (imageId.includes('?requestType=WADO')) { const qs = queryString.parse(imageId); @@ -424,6 +437,7 @@ class MetadataProvider { StudyInstanceUID: qs.studyUID, SeriesInstanceUID: qs.seriesUID, SOPInstanceUID: qs.objectUID, + frameNumber: qs.frameNumber, }; } @@ -438,7 +452,13 @@ class MetadataProvider { imageURI = imageIdToURI(imageId); } - return this.imageURIToUIDs.get(imageURI); + const uids = this.imageURIToUIDs.get(imageURI); + const frameNumber = imageId.split(/\/frames\//)[1]; + + if (uids && frameNumber !== undefined) { + return { ...uids, frameNumber }; + } + return uids; } } diff --git a/platform/core/src/utils/combineFrameInstance.ts b/platform/core/src/utils/combineFrameInstance.ts new file mode 100644 index 000000000..377503b53 --- /dev/null +++ b/platform/core/src/utils/combineFrameInstance.ts @@ -0,0 +1,33 @@ +/** + * Combine the Per instance frame data, the shared frame data + * and the root data objects. + * The data is combined by taking nested sequence objects within + * the functional group sequences. Data that is directly contained + * within the functional group sequences, such as private creators + * will be ignored. + * This can be safely called with an undefined frame in order to handle + * single frame data. (eg frame is undefined is the same as frame===1). + */ +const combineFrameInstance = (frame, instance) => { + const { + PerFrameFunctionalGroupsSequence, + SharedFunctionalGroupsSequence, + } = instance; + if (!PerFrameFunctionalGroupsSequence) return instance; + const shared = Object.values(SharedFunctionalGroupsSequence[0]) + .map(it => it[0]) + .filter(it => it !== undefined && typeof it === 'object'); + const perFrame = Object.values( + PerFrameFunctionalGroupsSequence[(frame || 1) - 1] + ) + .map(it => it[0]) + .filter(it => it !== undefined && typeof it === 'object'); + return Object.assign( + {}, + instance, + ...Object.values(shared), + ...Object.values(perFrame) + ); +}; + +export default combineFrameInstance; diff --git a/platform/core/src/utils/isImage.js b/platform/core/src/utils/isImage.js index 5720f046c..525e6ccd9 100644 --- a/platform/core/src/utils/isImage.js +++ b/platform/core/src/utils/isImage.js @@ -12,6 +12,7 @@ const imagesTypes = [ sopClassDictionary.EnhancedCTImageStorage, sopClassDictionary.LegacyConvertedEnhancedCTImageStorage, sopClassDictionary.UltrasoundMultiframeImageStorage, + sopClassDictionary.EnhancedUSVolumeStorage, sopClassDictionary.MRImageStorage, sopClassDictionary.EnhancedMRImageStorage, sopClassDictionary.EnhancedMRColorImageStorage, diff --git a/platform/core/src/utils/sopClassDictionary.js b/platform/core/src/utils/sopClassDictionary.js index f4a7cb6c7..5e9557695 100644 --- a/platform/core/src/utils/sopClassDictionary.js +++ b/platform/core/src/utils/sopClassDictionary.js @@ -128,3 +128,5 @@ export const sopClassDictionary = { ImplantAssemblyTemplateStorage: '1.2.840.10008.5.1.4.44.1', ImplantTemplateGroupStorage: '1.2.840.10008.5.1.4.45.1', }; + +export default sopClassDictionary;