Instance metadata/metadata providers overhaul (#1481)

* Instance metadata plus metadata provider overhaul.

Fix consumption of wado-uri urls

fallbacks + datatype agnosticism.

WIP DICOMify things.

fix various issues with naturalized variable naming migration.

Remove metadata provider.

Fix consumption of multiframe images and addition of CWIL metadata.

Fix strange build issues.

Fix CWIL style windowWidth to array from naturalized DICOM.

Fix PT, CT, CR and DX issues for cornerstone + DX issues for vtkjs.

Move color palette fetching down to the natuaralized JSON level.

Remove unused StudyMetadataSummary

Remove redundant dicom metadata dictionary.

Working local + json routes.

Fix SR read.

Finished first round of testing + cleaned up debugging etc.

* data => metadata for instance naturalizedJSON

* Update dcmjs version

* Correct github isssues.

* Fix erroneously replaced files.

* Danny's recommended changes.

* Instance metadata plus metadata provider overhaul.

Fix consumption of wado-uri urls

fallbacks + datatype agnosticism.

WIP DICOMify things.

fix various issues with naturalized variable naming migration.

Remove metadata provider.

Fix consumption of multiframe images and addition of CWIL metadata.

Fix strange build issues.

Fix CWIL style windowWidth to array from naturalized DICOM.

Fix PT, CT, CR and DX issues for cornerstone + DX issues for vtkjs.

Move color palette fetching down to the natuaralized JSON level.

Remove unused StudyMetadataSummary

Remove redundant dicom metadata dictionary.

Working local + json routes.

Fix SR read.

Finished first round of testing + cleaned up debugging etc.

* data => metadata for instance naturalizedJSON

* Update dcmjs version

* Correct github isssues.

* Fix erroneously replaced files.

* Danny's recommended changes.

* Update JSON CI

* Update casing of import.

* Fix jump for SR.

* Fix unit tests for measurements service

* Fix json CI test.

* fix: update yarn lock

* Fix local non-encapsulated pdf view

* CI updated to new sucess message.

Co-authored-by: Danny <danny.ri.brown@gmail.com>
This commit is contained in:
James PettsandDanny authored and GitHub committed 2020-03-09 19:03:23 +00:00
1 parent 8833731ad3
commit 1d85f7a74e
142 files changed
+5322 -8570

No files matched your search

@@ -8,15 +8,6 @@ import handleSegmentationStorage from './handleSegmentationStorage.js';
const { StackManager } = OHIF.utils;
// Metadata configuration
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
cornerstone.metaData.addProvider(
metadataProvider.provider.bind(metadataProvider)
);
StackManager.setMetadataProvider(metadataProvider);
const SOP_CLASSES = {
SEGMENTATION_STORAGE: '1.2.840.10008.5.1.4.1.1.66.4',
};
@@ -58,34 +49,34 @@ class OHIFCornerstoneViewport extends Component {
* Obtain the CornerstoneTools Stack for the specified display set.
*
* @param {Object[]} studies
* @param {String} studyInstanceUid
* @param {String} displaySetInstanceUid
* @param {String} [sopInstanceUid]
* @param {String} StudyInstanceUID
* @param {String} displaySetInstanceUID
* @param {String} [SOPInstanceUID]
* @param {Number} [frameIndex=1]
* @return {Object} CornerstoneTools Stack
*/
static getCornerstoneStack(
studies,
studyInstanceUid,
displaySetInstanceUid,
sopInstanceUid,
StudyInstanceUID,
displaySetInstanceUID,
SOPInstanceUID,
frameIndex = 0
) {
if (!studies || !studies.length) {
throw new Error('Studies not provided.');
}
if (!studyInstanceUid) {
if (!StudyInstanceUID) {
throw new Error('StudyInstanceUID not provided.');
}
if (!displaySetInstanceUid) {
if (!displaySetInstanceUID) {
throw new Error('StudyInstanceUID not provided.');
}
// Create shortcut to displaySet
const study = studies.find(
study => study.studyInstanceUid === studyInstanceUid
study => study.StudyInstanceUID === StudyInstanceUID
);
if (!study) {
@@ -93,7 +84,7 @@ class OHIFCornerstoneViewport extends Component {
}
const displaySet = study.displaySets.find(set => {
return set.displaySetInstanceUid === displaySetInstanceUid;
return set.displaySetInstanceUID === displaySetInstanceUID;
});
if (!displaySet) {
@@ -107,17 +98,14 @@ class OHIFCornerstoneViewport extends Component {
const stack = Object.assign({}, storedStack);
stack.currentImageIdIndex = frameIndex;
if (sopInstanceUid) {
if (SOPInstanceUID) {
const index = stack.imageIds.findIndex(imageId => {
const sopCommonModule = cornerstone.metaData.get(
'sopCommonModule',
const imageIdSOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
imageId
);
if (!sopCommonModule) {
return;
}
return sopCommonModule.sopInstanceUID === sopInstanceUid;
return imageIdSOPInstanceUID === SOPInstanceUID;
});
if (index > -1) {
@@ -134,39 +122,39 @@ class OHIFCornerstoneViewport extends Component {
getViewportData = async (
studies,
studyInstanceUid,
displaySetInstanceUid,
sopClassUid,
sopInstanceUid,
StudyInstanceUID,
displaySetInstanceUID,
SOPClassUID,
SOPInstanceUID,
frameIndex
) => {
let viewportData;
switch (sopClassUid) {
switch (SOPClassUID) {
case SOP_CLASSES.SEGMENTATION_STORAGE:
const specialCaseHandler =
specialCaseHandlers[SOP_CLASSES.SEGMENTATION_STORAGE];
viewportData = await specialCaseHandler(
studies,
studyInstanceUid,
displaySetInstanceUid,
sopInstanceUid,
StudyInstanceUID,
displaySetInstanceUID,
SOPInstanceUID,
frameIndex
);
break;
default:
const stack = OHIFCornerstoneViewport.getCornerstoneStack(
studies,
studyInstanceUid,
displaySetInstanceUid,
sopInstanceUid,
StudyInstanceUID,
displaySetInstanceUID,
SOPInstanceUID,
frameIndex
);
viewportData = {
studyInstanceUid,
displaySetInstanceUid,
StudyInstanceUID,
displaySetInstanceUID,
stack,
};
@@ -179,31 +167,31 @@ class OHIFCornerstoneViewport extends Component {
setStateFromProps() {
const { studies, displaySet } = this.props.viewportData;
const {
studyInstanceUid,
displaySetInstanceUid,
sopClassUids,
sopInstanceUid,
StudyInstanceUID,
displaySetInstanceUID,
sopClassUIDs,
SOPInstanceUID,
frameIndex,
} = displaySet;
if (!studyInstanceUid || !displaySetInstanceUid) {
if (!StudyInstanceUID || !displaySetInstanceUID) {
return;
}
if (sopClassUids && sopClassUids.length > 1) {
if (sopClassUIDs && sopClassUIDs.length > 1) {
console.warn(
'More than one SOPClassUid in the same series is not yet supported.'
'More than one SOPClassUID in the same series is not yet supported.'
);
}
const sopClassUid = sopClassUids && sopClassUids[0];
const SOPClassUID = sopClassUIDs && sopClassUIDs[0];
this.getViewportData(
studies,
studyInstanceUid,
displaySetInstanceUid,
sopClassUid,
sopInstanceUid,
StudyInstanceUID,
displaySetInstanceUID,
SOPClassUID,
SOPInstanceUID,
frameIndex
).then(viewportData => {
this.setState({
@@ -221,9 +209,9 @@ class OHIFCornerstoneViewport extends Component {
const prevDisplaySet = prevProps.viewportData.displaySet;
if (
displaySet.displaySetInstanceUid !==
prevDisplaySet.displaySetInstanceUid ||
displaySet.sopInstanceUid !== prevDisplaySet.sopInstanceUid ||
displaySet.displaySetInstanceUID !==
prevDisplaySet.displaySetInstanceUID ||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
displaySet.frameIndex !== prevDisplaySet.frameIndex
) {
this.setStateFromProps();
@@ -247,6 +247,7 @@ const commandsModule = ({ servicesManager }) => {
if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement);
viewport.voi = {
windowWidth: Number(window),
windowCenter: Number(level),
@@ -6,25 +6,25 @@ import cornerstoneTools from 'cornerstone-tools';
const { StackManager, DicomLoaderService } = OHIF.utils;
function getDisplaySet(studies, studyInstanceUid, displaySetInstanceUid) {
function getDisplaySet(studies, StudyInstanceUID, displaySetInstanceUID) {
const study = studies.find(
study => study.studyInstanceUid === studyInstanceUid
study => study.StudyInstanceUID === StudyInstanceUID
);
const displaySet = study.displaySets.find(set => {
return set.displaySetInstanceUid === displaySetInstanceUid;
return set.displaySetInstanceUID === displaySetInstanceUID;
});
return displaySet;
}
function getDisplaySetsBySeries(studies, studyInstanceUid, seriesInstanceUid) {
function getDisplaySetsBySeries(studies, StudyInstanceUID, SeriesInstanceUID) {
const study = studies.find(
study => study.studyInstanceUid === studyInstanceUid
study => study.StudyInstanceUID === StudyInstanceUID
);
return study.displaySets.filter(set => {
return set.seriesInstanceUid === seriesInstanceUid;
return set.SeriesInstanceUID === SeriesInstanceUID;
});
}
@@ -39,7 +39,7 @@ function parseSeg(arrayBuffer, imageIds) {
function addSegMetadataToCornerstoneToolState(
segMetadata,
toolState,
displaySetInstanceUid
displaySetInstanceUID
) {
cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState(
toolState
@@ -48,22 +48,22 @@ function addSegMetadataToCornerstoneToolState(
const brushModule = cornerstoneTools.store.modules.brush;
for (let i = 0; i < segMetadata.length; i++) {
brushModule.setters.metadata(displaySetInstanceUid, i, segMetadata[i]);
brushModule.setters.metadata(displaySetInstanceUID, i, segMetadata[i]);
}
}
async function handleSegmentationStorage(
studies,
studyInstanceUid,
displaySetInstanceUid
StudyInstanceUID,
displaySetInstanceUID
) {
const study = studies.find(
study => study.studyInstanceUid === studyInstanceUid
study => study.StudyInstanceUID === StudyInstanceUID
);
const displaySet = getDisplaySet(
studies,
studyInstanceUid,
displaySetInstanceUid
StudyInstanceUID,
displaySetInstanceUID
);
const arrayBuffer = await DicomLoaderService.findDicomDataPromise(
@@ -77,16 +77,16 @@ async function handleSegmentationStorage(
dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(dicomData.meta);
const seriesInstanceUid = dataset.ReferencedSeriesSequence.SeriesInstanceUID;
const SeriesInstanceUID = dataset.ReferencedSeriesSequence.SeriesInstanceUID;
const displaySets = getDisplaySetsBySeries(
studies,
studyInstanceUid,
seriesInstanceUid
StudyInstanceUID,
SeriesInstanceUID
);
if (displaySets.length > 1) {
console.warn(
'More than one display set with the same seriesInstanceUid. This is not supported yet...'
'More than one display set with the same SeriesInstanceUID. This is not supported yet...'
);
}
@@ -119,8 +119,8 @@ async function handleSegmentationStorage(
stack.currentImageIdIndex = 0;
return {
studyInstanceUid,
displaySetInstanceUid,
StudyInstanceUID,
displaySetInstanceUID,
stack,
};
}
+3 -26
View File
@@ -3,29 +3,9 @@ import { SimpleDialog } from '@ohif/ui';
import cornerstone from 'cornerstone-core';
import csTools from 'cornerstone-tools';
import merge from 'lodash.merge';
import queryString from 'query-string';
import initCornerstoneTools from './initCornerstoneTools.js';
import measurementServiceMappingsFactory from './utils/measurementServiceMappings/measurementServiceMappingsFactory';
function fallbackMetaDataProvider(type, imageId) {
if (!imageId.includes('wado?requestType=WADO')) {
return;
}
// If you call for an WADO-URI imageId and get no
// metadata, try reformatting to WADO-RS imageId
const qs = queryString.parse(imageId);
const wadoRoot = window.store.getState().servers.servers[0].wadoRoot;
const wadoRsImageId = `wadors:${wadoRoot}/studies/${qs.studyUID}/series/${
qs.seriesUID
}/instances/${qs.objectUID}/frames/${qs.frame || 1}`;
return cornerstone.metaData.get(type, wadoRsImageId);
}
// Add this fallback provider with a low priority so it is handled last
cornerstone.metaData.addProvider(fallbackMetaDataProvider, -1);
/**
*
* @param {Object} servicesManager
@@ -58,16 +38,13 @@ export default function init({ servicesManager, configuration }) {
};
const { csToolsConfig } = configuration;
const { StackManager } = OHIF.utils;
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
const metadataProvider = OHIF.cornerstone.metadataProvider;
// ~~ Set our MetadataProvider
cornerstone.metaData.addProvider(
metadataProvider.provider.bind(metadataProvider)
metadataProvider.get.bind(metadataProvider),
9999
);
StackManager.setMetadataProvider(metadataProvider);
// ~~
const defaultCsToolsConfig = csToolsConfig || {
globalToolSyncEnabled: true,
@@ -1,6 +1,11 @@
import cornerstone from 'cornerstone-core';
const SUPPORTED_TOOLS = ['Length', 'EllipticalRoi', 'RectangleRoi', 'ArrowAnnotate'];
const SUPPORTED_TOOLS = [
'Length',
'EllipticalRoi',
'RectangleRoi',
'ArrowAnnotate',
];
const measurementServiceMappingsFactory = measurementService => {
/**
@@ -17,17 +22,17 @@ const measurementServiceMappingsFactory = measurementService => {
description,
points,
unit,
sopInstanceUID,
frameOfReferenceUID,
SOPInstanceUID,
FrameOfReferenceUID,
referenceSeriesUID,
} = measurement;
return {
toolName: definition,
measurementData: {
sopInstanceUid: sopInstanceUID,
frameOfReferenceUid: frameOfReferenceUID,
seriesInstanceUid: referenceSeriesUID,
sopInstanceUid: SOPInstanceUID,
frameOfReferenceUID: FrameOfReferenceUID,
SeriesInstanceUID: referenceSeriesUID,
unit,
text: label,
description,
@@ -57,9 +62,9 @@ const measurementServiceMappingsFactory = measurementService => {
}
const {
sopInstanceUid,
frameOfReferenceUid,
seriesInstanceUid,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
} = _getAttributes(element);
const points = [];
@@ -67,13 +72,16 @@ const measurementServiceMappingsFactory = measurementService => {
return {
id: measurementData._measurementServiceId,
sopInstanceUID: sopInstanceUid,
frameOfReferenceUID: frameOfReferenceUid,
referenceSeriesUID: seriesInstanceUid,
SOPInstanceUID: SOPInstanceUID,
FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID,
label: measurementData.text,
description: measurementData.description,
unit: measurementData.unit,
area: measurementData.cachedStats && measurementData.cachedStats.area, /* TODO: Add concept names instead (descriptor) */
area:
measurementData.cachedStats &&
measurementData.cachedStats
.area /* TODO: Add concept names instead (descriptor) */,
type: _getValueTypeFromToolType(tool),
points: _getPointsFromHandles(measurementData.handles),
};
@@ -82,13 +90,13 @@ const measurementServiceMappingsFactory = measurementService => {
const _getAttributes = element => {
const enabledElement = cornerstone.getEnabledElement(element);
const imageId = enabledElement.image.imageId;
const sopInstance = cornerstone.metaData.get('instance', imageId);
const sopInstanceUid = sopInstance.sopInstanceUid;
const frameOfReferenceUid = sopInstance.frameOfReferenceUID;
const series = cornerstone.metaData.get('series', imageId);
const seriesInstanceUid = series.seriesInstanceUid;
const instance = cornerstone.metaData.get('instance', imageId);
return { sopInstanceUid, frameOfReferenceUid, seriesInstanceUid };
return {
SOPInstanceUID: instance.SOPInstanceUID,
FrameOfReferenceUID: instance.FrameOfReferenceUID,
SeriesInstanceUID: instance.SeriesInstanceUID,
};
};
const _getValueTypeFromToolType = toolType => {
@@ -8,9 +8,9 @@ jest.mock('cornerstone-core', () => ({
metaData: {
...jest.requireActual('cornerstone-core').metaData,
get: () => ({
sopInstanceUid: "123",
frameOfReferenceUID: "123",
seriesInstanceUid: "123",
SOPInstanceUID: '123',
FrameOfReferenceUID: '123',
SeriesInstanceUID: '123',
}),
},
}));
@@ -42,8 +42,8 @@ describe('measurementServiceMappings.js', () => {
measurementData: {
_measurementServiceId: 1,
sopInstanceUid: '123',
frameOfReferenceUid: '123',
seriesInstanceUid: '123',
frameOfReferenceUID: '123',
SeriesInstanceUID: '123',
handles,
text: 'Test',
description: 'Test',
@@ -52,8 +52,8 @@ describe('measurementServiceMappings.js', () => {
};
measurement = {
id: 1,
sopInstanceUID: '123',
frameOfReferenceUID: '123',
SOPInstanceUID: '123',
FrameOfReferenceUID: '123',
referenceSeriesUID: '123',
label: 'Test',
description: 'Test',
@@ -66,7 +66,10 @@ describe('measurementServiceMappings.js', () => {
describe('toAnnotation()', () => {
it('map measurement service format to annotation', async () => {
const mappedMeasurement = await mappings.toAnnotation(measurement, definition);
const mappedMeasurement = await mappings.toAnnotation(
measurement,
definition
);
expect(mappedMeasurement).toEqual(csToolsAnnotation);
});
});