Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable metadata loading from images #3935

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,25 @@ const OHIFCornerstoneViewport = React.memo(props => {
};
}, [displaySets, elementRef, viewportId]);

useEffect(() => {
const SubscriptionDisplaySetsChanged = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_CHANGED,
async (changedDisplaySets) => {
var displaySets = [];
displaySets.push(changedDisplaySets);
const newViewportData = await cornerstoneCacheService.createViewportData(
displaySets,
viewportOptions,
dataSource,
initialImageIndex
);

const keepCamera = true;
cornerstoneViewportService.updateViewport(viewportId, newViewportData, keepCamera);
}
);
}, [displaySets]);

return (
<React.Fragment>
<div className="viewport-wrapper">
Expand Down
102 changes: 100 additions & 2 deletions extensions/default/src/DicomJSONDataSource/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import OHIF from '@ohif/core';
import getImageId from '../DicomWebDataSource/utils/getImageId';
import getDirectURL from '../utils/getDirectURL';

import dicomImageLoader from '@cornerstonejs/dicom-image-loader';
import dcmjs from 'dcmjs';
import { ServicesManager, Types } from '@ohif/core';
import isDisplaySetReconstructable from '@ohif/core/src/utils/isDisplaySetReconstructable';
import getDisplaySetMessages from '../getDisplaySetMessages';


const metadataProvider = OHIF.classes.MetadataProvider;

const mappings = {
Expand All @@ -25,6 +32,9 @@ let _store = {
// }
};

let _servicesManager;
let _nbInstancesUpdated = 0;

function wrapSequences(obj) {
return Object.keys(obj).reduce(
(acc, key) => {
Expand Down Expand Up @@ -58,8 +68,90 @@ const findStudies = (key, value) => {
return studies;
};

function createDicomJSONApi(dicomJsonConfig) {
const isMultiFrame = instance => {
return instance.NumberOfFrames > 1;
};


function updateMetadata(instance) {

if (instance.imageMetadataLoaded === false) {

const dataSetCacheManager = dicomImageLoader.wadouri.dataSetCacheManager
const parsedImageId = dicomImageLoader.wadouri.parseImageId(instance.imageId);
let url = parsedImageId.url;

if (parsedImageId.frame) {
url = `${url}&frame=${parsedImageId.frame}`;
}

const rawDataSet = dataSetCacheManager.get(url);

if (!rawDataSet) {
return;
}

const dicomData = dcmjs.data.DicomMessage.readFile(rawDataSet.byteArray.buffer);
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(dicomData.dict);

instance = {
...instance,
...dataset,
imageMetadataLoaded: true
}

const uids = this.getUIDsFromImageID(instance.imageId);

if (!uids) {
return;
}

const {
StudyInstanceUID,
SeriesInstanceUID,
SOPInstanceUID
} = uids;

DicomMetadataStore.updateMetadataForInstance(StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID, instance)
_nbInstancesUpdated++;

const { displaySetService } = _servicesManager.services;

const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(SOPInstanceUID);
if (displaySet?.unsupported) {
throw new Error('Unsupported displaySet');
}

// when last instance has been updated, we need to update displayset in order to check if it is reconstructable (among other things)
if (_nbInstancesUpdated === DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID).instances.length) {

const { value: isReconstructable, averageSpacingBetweenFrames } =
isDisplaySetReconstructable(DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID).instances);

// set appropriate attributes to image set...
const messages = getDisplaySetMessages(DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID).instances, isReconstructable);

displaySet.setAttributes({
SeriesDate: instance.SeriesDate,
SeriesTime: instance.SeriesTime,
FrameRate: instance.FrameTime,
SeriesDescription: instance.SeriesDescription || '',
Modality: instance.Modality,
isMultiFrame: isMultiFrame(instance),
countIcon: isReconstructable ? 'icon-mpr' : undefined,
isReconstructable,
messages,
averageSpacingBetweenFrames: averageSpacingBetweenFrames || null,
});

displaySetService.triggerDisplaySetChanged(displaySet);
}
}
}

function createDicomJSONApi(dicomJsonConfig, servicesManager) {
const { wadoRoot } = dicomJsonConfig;
_servicesManager = servicesManager;

const implementation = {
initialize: async ({ query, url }) => {
Expand All @@ -85,6 +177,10 @@ function createDicomJSONApi(dicomJsonConfig) {
data.studies.forEach(study => {
StudyInstanceUID = study.StudyInstanceUID;

if (study.fullmetadataset == "no") {
metadataProvider.setUpdataMetadataCallback(updateMetadata);
}

study.series.forEach(series => {
SeriesInstanceUID = series.SeriesInstanceUID;

Expand Down Expand Up @@ -112,7 +208,7 @@ function createDicomJSONApi(dicomJsonConfig) {
},
query: {
studies: {
mapParams: () => {},
mapParams: () => { },
search: async param => {
const [key, value] = Object.entries(param)[0];
const mappedParam = mappings[key];
Expand Down Expand Up @@ -219,6 +315,8 @@ function createDicomJSONApi(dicomJsonConfig) {
imageId: instance.url,
...series,
...study,
// We need to force this flag to false in case the client wants to load the metadata from the image directly
imageMetadataLoaded: false
};
delete obj.instances;
delete obj.series;
Expand Down
14 changes: 14 additions & 0 deletions platform/core/src/classes/MetadataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPal
import toNumber from '../utils/toNumber';
import combineFrameInstance from '../utils/combineFrameInstance';


class MetadataProvider {
constructor() {
// Define the main "metadataLookup" private property as an immutable property.
Expand All @@ -32,6 +33,9 @@ class MetadataProvider {
writable: false,
value: new Map(),
});

this.updateMedataCallback = null;

}

addImageIdToUIDs(imageId, uids) {
Expand Down Expand Up @@ -113,6 +117,10 @@ class MetadataProvider {
return instance[naturalizedTagOrWADOImageLoaderTag];
}

if (this.updateMedataCallback !== null) {
this.updateMedataCallback(instance);
}

// Maybe its a legacy dicomImageLoader tag then:
return this._getCornerstoneDICOMImageLoaderTag(naturalizedTagOrWADOImageLoaderTag, instance);
}
Expand All @@ -125,6 +133,12 @@ class MetadataProvider {
WADO_IMAGE_LOADER[wadoImageLoaderTag] = handler;
}


public setUpdataMetadataCallback(callback) {
this.updateMedataCallback = callback;
}


_getCornerstoneDICOMImageLoaderTag(wadoImageLoaderTag, instance) {
let metadata = WADO_IMAGE_LOADER[wadoImageLoaderTag]?.(instance);
if (metadata) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,29 @@ function _getInstanceByImageId(imageId) {
}
}

function _updateMetadataForInstance(StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID, metadata) {
const study = _getStudy(StudyInstanceUID);

if (!study) {
return;
}

const series = study.series.find(
aSeries => aSeries.SeriesInstanceUID === SeriesInstanceUID
);

const { instances } = series;
// update all instances metadata for this series with the new metadata
instances.forEach(instance => {
if (instance.SOPInstanceUID === SOPInstanceUID) {
Object.keys(metadata).forEach(key => {
instance[key] = metadata[key];
});
}
});
}


/**
* Update the metadata of a specific series
* @param {*} StudyInstanceUID
Expand Down Expand Up @@ -255,6 +278,7 @@ const BaseImplementation = {
getInstance: _getInstance,
getInstanceByImageId: _getInstanceByImageId,
updateMetadataForSeries: _updateMetadataForSeries,
updateMetadataForInstance: _updateMetadataForInstance,
};
const DicomMetadataStore = Object.assign(
// get study
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,4 +424,10 @@ export default class DisplaySetService extends PubSubService {

return result;
}


public triggerDisplaySetChanged(displayset): void {

this._broadcastEvent(EVENTS.DISPLAY_SETS_CHANGED, displayset);
}
}