Re IDC #2761: fix loading of segmentations update dcmjs to 0.19.9
This commit is contained in:
parent
b94d4782d1
commit
32828d9a6e
@ -36,7 +36,7 @@
|
||||
"cornerstone-math": "^0.1.9",
|
||||
"cornerstone-tools": "^6.0.6",
|
||||
"cornerstone-wado-image-loader": "^4.1.0",
|
||||
"dcmjs": "0.19.8",
|
||||
"dcmjs": "0.19.9",
|
||||
"dicom-parser": "^1.8.11",
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"dcmjs": "0.19.8",
|
||||
"dcmjs": "0.19.9",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
"@ohif/core": "^0.50.0",
|
||||
"cornerstone-core": "^2.6.1",
|
||||
"cornerstone-tools": "^6.0.6",
|
||||
"dcmjs": "0.19.8",
|
||||
"dcmjs": "0.19.9",
|
||||
"gl-matrix": "^3.3.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
"@ohif/core": "^0.50.0",
|
||||
"cornerstone-core": "^2.6.1",
|
||||
"cornerstone-tools": "^6.0.6",
|
||||
"dcmjs": "0.19.8",
|
||||
"dcmjs": "0.19.9",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
|
||||
@ -226,7 +226,6 @@ const SegmentationPanel = ({
|
||||
};
|
||||
}, [
|
||||
activeIndex,
|
||||
updateSegmentationComboBox,
|
||||
viewports,
|
||||
]);
|
||||
|
||||
@ -234,23 +233,9 @@ const SegmentationPanel = ({
|
||||
const index = e.detail.activatedLabelmapIndex;
|
||||
if (index !== -1) {
|
||||
setState(state => ({ ...state, selectedSegmentation: index }));
|
||||
} else {
|
||||
cleanSegmentationComboBox();
|
||||
}
|
||||
};
|
||||
|
||||
const cleanSegmentationComboBox = () => {
|
||||
setState(state => ({
|
||||
...state,
|
||||
segmentsHidden: [],
|
||||
segmentNumbers: [],
|
||||
labelMapList: [],
|
||||
segmentList: [],
|
||||
isDisabled: true,
|
||||
selectedSegmentation: -1,
|
||||
}));
|
||||
};
|
||||
|
||||
const refreshSegmentations = () => {
|
||||
const activeViewport = getActiveViewport();
|
||||
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
|
||||
@ -709,7 +694,7 @@ const SegmentationPanel = ({
|
||||
count={state.segmentList.length}
|
||||
isVisible={
|
||||
state.segmentsHidden.filter(isHidden => isHidden === true).length <
|
||||
state.segmentNumbers.length
|
||||
state.segmentNumbers.length && state.segmentNumbers.length > 0
|
||||
}
|
||||
onVisibilityChange={onVisibilityChangeHandler}
|
||||
>
|
||||
|
||||
@ -47,14 +47,23 @@ export default async function setActiveLabelmap(
|
||||
return labelmapIndex;
|
||||
}
|
||||
|
||||
if (!displaySet.isLoaded) {
|
||||
if (!displaySet.isLoaded && !displaySet.loadError) {
|
||||
try {
|
||||
await displaySet.load(referencedDisplaySet, studies);
|
||||
} catch (error) {
|
||||
displaySet.isLoaded = false;
|
||||
displaySet.loadError = true;
|
||||
displaySet.segLoadErrorMessagge = error.message;
|
||||
onDisplaySetLoadFailure(error);
|
||||
|
||||
/*
|
||||
* TODO: Improve the way we notify parts of the app
|
||||
* that depends on derived display sets to be loaded.
|
||||
* (Implement pubsub for better tracking of derived display sets)
|
||||
*/
|
||||
const event = new CustomEvent('segmentationLoadingError');
|
||||
document.dispatchEvent(event);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^2.6.0",
|
||||
"dcmjs": "0.19.8",
|
||||
"dcmjs": "0.19.9",
|
||||
"react": "^16.8.6"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
"cornerstone-core": "^2.6.1",
|
||||
"cornerstone-tools": "^6.0.6",
|
||||
"cornerstone-wado-image-loader": "^4.1.0",
|
||||
"dcmjs": "0.19.8",
|
||||
"dcmjs": "0.19.9",
|
||||
"dicom-parser": "^1.8.11",
|
||||
"i18next": "^17.0.3",
|
||||
"i18next-browser-languagedetector": "^3.0.1",
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"ajv": "^6.10.0",
|
||||
"dcmjs": "0.19.8",
|
||||
"dcmjs": "0.19.9",
|
||||
"dicomweb-client": "^0.8.3",
|
||||
"immer": "9.0.12",
|
||||
"isomorphic-base64": "^1.0.2",
|
||||
|
||||
@ -49,7 +49,12 @@ import studyMetadataManager from './studyMetadataManager';
|
||||
* @param {object} snackbar
|
||||
* @returns void
|
||||
*/
|
||||
async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, logger, snackbar) {
|
||||
async function loadAndCacheDerivedDisplaySets(
|
||||
referencedDisplaySet,
|
||||
studies,
|
||||
logger,
|
||||
snackbar
|
||||
) {
|
||||
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
|
||||
const promises = [];
|
||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||
@ -80,60 +85,111 @@ async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, log
|
||||
});
|
||||
|
||||
// For each type, see if any are loaded, if not load the most recent.
|
||||
await Promise.all(Object.keys(displaySetsPerModality).map(async (key) => {
|
||||
const displaySets = displaySetsPerModality[key];
|
||||
await Promise.all(
|
||||
Object.keys(displaySetsPerModality).map(async key => {
|
||||
const displaySets = displaySetsPerModality[key];
|
||||
|
||||
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
|
||||
if (isLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (displaySets.some(displaySet => displaySet.loadError)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find most recent and load it.
|
||||
let recentDateTime = 0;
|
||||
let recentDisplaySet = displaySets[0];
|
||||
|
||||
displaySets.forEach(displaySet => {
|
||||
const dateTime = Number(
|
||||
`${displaySet.SeriesDate}${displaySet.SeriesTime}`
|
||||
);
|
||||
if (dateTime > recentDateTime) {
|
||||
recentDateTime = dateTime;
|
||||
recentDisplaySet = displaySet;
|
||||
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
|
||||
if (isLoaded) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
if (recentDisplaySet.hasOwnProperty('getSourceDisplaySet') &&
|
||||
typeof recentDisplaySet.getSourceDisplaySet === 'function') {
|
||||
await recentDisplaySet.getSourceDisplaySet(studies);
|
||||
} else {
|
||||
await recentDisplaySet.load(referencedDisplaySet, studies);
|
||||
if (displaySets.some(displaySet => displaySet.loadError)) {
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
recentDisplaySet.isLoaded = false;
|
||||
recentDisplaySet.loadError = true;
|
||||
logger.error({ error, message: error.message });
|
||||
snackbar.show({
|
||||
title: 'Error loading derived display set:',
|
||||
message: error.message,
|
||||
type: 'error',
|
||||
error,
|
||||
autoClose: false,
|
||||
|
||||
// find most recent and load it.
|
||||
let recentDateTime = 0;
|
||||
let recentDisplaySet = displaySets[0];
|
||||
|
||||
displaySets.forEach(displaySet => {
|
||||
const dateTime = Number(
|
||||
`${displaySet.SeriesDate}${displaySet.SeriesTime}`
|
||||
);
|
||||
if (dateTime > recentDateTime) {
|
||||
recentDateTime = dateTime;
|
||||
recentDisplaySet = displaySet;
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
try {
|
||||
if (
|
||||
recentDisplaySet.hasOwnProperty('getSourceDisplaySet') &&
|
||||
typeof recentDisplaySet.getSourceDisplaySet === 'function'
|
||||
) {
|
||||
if (recentDisplaySet.Modality === 'SEG' && logger) {
|
||||
const onDisplaySetLoadFailureHandler = error => {
|
||||
logger.error({ error, message: error.message });
|
||||
snackbar.show({
|
||||
title: 'DICOM Segmentation Loader',
|
||||
message: error.message,
|
||||
type: 'error',
|
||||
autoClose: true,
|
||||
});
|
||||
};
|
||||
|
||||
let activatedLabelmapIndex = -1;
|
||||
while (activatedLabelmapIndex == -1) {
|
||||
const {
|
||||
referencedDisplaySet,
|
||||
activatedLabelmapPromise,
|
||||
} = await recentDisplaySet.getSourceDisplaySet(
|
||||
studies,
|
||||
true,
|
||||
onDisplaySetLoadFailureHandler
|
||||
);
|
||||
|
||||
activatedLabelmapIndex = await activatedLabelmapPromise;
|
||||
const selectionFired = new CustomEvent(
|
||||
'extensiondicomsegmentationsegselected',
|
||||
{
|
||||
detail: { activatedLabelmapIndex: activatedLabelmapIndex },
|
||||
}
|
||||
);
|
||||
document.dispatchEvent(selectionFired);
|
||||
|
||||
const lastDateTime = Number(
|
||||
`${recentDisplaySet.SeriesDate}${recentDisplaySet.SeriesTime}`
|
||||
);
|
||||
recentDateTime = 0;
|
||||
displaySets.forEach(displaySet => {
|
||||
const dateTime = Number(
|
||||
`${displaySet.SeriesDate}${displaySet.SeriesTime}`
|
||||
);
|
||||
if (dateTime > recentDateTime && dateTime < lastDateTime) {
|
||||
recentDateTime = dateTime;
|
||||
recentDisplaySet = displaySet;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await recentDisplaySet.getSourceDisplaySet(studies);
|
||||
}
|
||||
} else {
|
||||
await recentDisplaySet.load(referencedDisplaySet, studies);
|
||||
}
|
||||
} catch (error) {
|
||||
recentDisplaySet.isLoaded = false;
|
||||
recentDisplaySet.loadError = true;
|
||||
logger.error({ error, message: error.message });
|
||||
snackbar.show({
|
||||
title: 'Error loading derived display set:',
|
||||
message: error.message,
|
||||
type: 'error',
|
||||
error,
|
||||
autoClose: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
/*
|
||||
* TODO: Improve the way we notify parts of the app
|
||||
* that depends on derived display sets to be loaded.
|
||||
* (Implement pubsub for better tracking of derived display sets)
|
||||
*/
|
||||
* TODO: Improve the way we notify parts of the app
|
||||
* that depends on derived display sets to be loaded.
|
||||
* (Implement pubsub for better tracking of derived display sets)
|
||||
*/
|
||||
const event = new CustomEvent('deriveddisplaysetsloadedandcached');
|
||||
document.dispatchEvent(event);
|
||||
};
|
||||
}
|
||||
|
||||
export default loadAndCacheDerivedDisplaySets;
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
"cornerstone-math": "^0.1.9",
|
||||
"cornerstone-tools": "^6.0.6",
|
||||
"cornerstone-wado-image-loader": "^4.1.0",
|
||||
"dcmjs": "0.19.8",
|
||||
"dcmjs": "0.19.9",
|
||||
"dicom-parser": "^1.8.11",
|
||||
"dicomweb-client": "^0.8.3",
|
||||
"hammerjs": "^2.0.8",
|
||||
|
||||
@ -105,6 +105,11 @@ class Viewer extends Component {
|
||||
if (this.props.dialog) {
|
||||
this.props.dialog.dismissAll();
|
||||
}
|
||||
|
||||
document.removeEventListener(
|
||||
'segmentationLoadingError',
|
||||
this._updateThumbnails
|
||||
);
|
||||
}
|
||||
|
||||
retrieveTimepoints = filter => {
|
||||
@ -212,6 +217,12 @@ class Viewer extends Component {
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener(
|
||||
'segmentationLoadingError',
|
||||
this._updateThumbnails.bind(this),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@ -255,6 +266,20 @@ class Viewer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
_updateThumbnails() {
|
||||
const { studies, activeViewportIndex, viewports } = this.props;
|
||||
|
||||
const activeViewport = viewports[activeViewportIndex];
|
||||
const activeDisplaySetInstanceUID = activeViewport
|
||||
? activeViewport.displaySetInstanceUID
|
||||
: undefined;
|
||||
|
||||
this.setState({
|
||||
thumbnails: _mapStudiesToThumbnails(studies, activeDisplaySetInstanceUID),
|
||||
activeDisplaySetInstanceUID,
|
||||
});
|
||||
}
|
||||
|
||||
_getActiveViewport() {
|
||||
return this.props.viewports[this.props.activeViewportIndex];
|
||||
}
|
||||
@ -461,9 +486,7 @@ const _checkForDerivedDisplaySets = async function(displaySet, study) {
|
||||
/**
|
||||
* Async function to check if there are any inconsistences in the series.
|
||||
*
|
||||
* For segmentation checks that the geometry is consistent with the source images:
|
||||
* 1) no frames out of plane;
|
||||
* 2) have the same width and height.
|
||||
* For segmentation returns any error during loading.
|
||||
*
|
||||
* For reconstructable 3D volume:
|
||||
* 1) Is series multiframe?
|
||||
@ -477,10 +500,7 @@ const _checkForDerivedDisplaySets = async function(displaySet, study) {
|
||||
* @param {*object} displaySet
|
||||
* @returns {[string]} an array of strings containing the warnings
|
||||
*/
|
||||
const _checkForSeriesInconsistencesWarnings = async function(
|
||||
displaySet,
|
||||
studies
|
||||
) {
|
||||
const _checkForSeriesInconsistencesWarnings = async function(displaySet) {
|
||||
if (displaySet.inconsistencyWarnings) {
|
||||
// warnings already checked and cached in displaySet
|
||||
return displaySet.inconsistencyWarnings;
|
||||
@ -546,98 +566,14 @@ const _checkForSeriesInconsistencesWarnings = async function(
|
||||
if (displaySet.isModalitySupported === false) {
|
||||
inconsistencyWarnings.push('The datasets modality is not supported.');
|
||||
}
|
||||
displaySet.inconsistencyWarnings = inconsistencyWarnings;
|
||||
} else {
|
||||
const segMetadata = displaySet.metadata;
|
||||
if (!segMetadata) {
|
||||
return inconsistencyWarnings;
|
||||
}
|
||||
|
||||
const { referencedDisplaySet } = displaySet.getSourceDisplaySet(
|
||||
studies,
|
||||
false
|
||||
);
|
||||
if (!referencedDisplaySet) {
|
||||
return inconsistencyWarnings;
|
||||
}
|
||||
|
||||
const imageIds = referencedDisplaySet.images.map(image =>
|
||||
image.getImageId()
|
||||
);
|
||||
if (!imageIds || imageIds.length === 0) {
|
||||
return inconsistencyWarnings;
|
||||
}
|
||||
|
||||
for (
|
||||
let i = 0,
|
||||
groupsLen = segMetadata.PerFrameFunctionalGroupsSequence.length;
|
||||
i < groupsLen;
|
||||
++i
|
||||
) {
|
||||
const PerFrameFunctionalGroups =
|
||||
segMetadata.PerFrameFunctionalGroupsSequence[i];
|
||||
if (!PerFrameFunctionalGroups) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let SourceImageSequence = undefined;
|
||||
if (segMetadata.SourceImageSequence) {
|
||||
SourceImageSequence = segMetadata.SourceImageSequence[i];
|
||||
} else if (PerFrameFunctionalGroups.DerivationImageSequence) {
|
||||
SourceImageSequence =
|
||||
PerFrameFunctionalGroups.DerivationImageSequence.SourceImageSequence;
|
||||
}
|
||||
if (!SourceImageSequence) {
|
||||
if (inconsistencyWarnings.length === 0) {
|
||||
const warningMessage =
|
||||
'The segmentation ' +
|
||||
'has frames out of plane respect to the source images.';
|
||||
inconsistencyWarnings.push(warningMessage);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const { ReferencedSOPInstanceUID } = SourceImageSequence;
|
||||
|
||||
const imageId = imageIds.find(imageId => {
|
||||
const sopCommonModule = cornerstone.metaData.get(
|
||||
'sopCommonModule',
|
||||
imageId
|
||||
);
|
||||
if (!sopCommonModule) {
|
||||
return;
|
||||
}
|
||||
|
||||
return sopCommonModule.sopInstanceUID === ReferencedSOPInstanceUID;
|
||||
});
|
||||
|
||||
if (!imageId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sourceImageMetadata = cornerstone.metaData.get('instance', imageId);
|
||||
if (
|
||||
segMetadata.Rows !== sourceImageMetadata.Rows ||
|
||||
segMetadata.Columns !== sourceImageMetadata.Columns
|
||||
) {
|
||||
const warningMessage =
|
||||
'The segmentation ' +
|
||||
'has frames with different geometry ' +
|
||||
'dimensions (Rows and Columns) respect to the source images.';
|
||||
inconsistencyWarnings.push(warningMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inconsistencyWarnings.length !== 0) {
|
||||
const warningMessage =
|
||||
'The segmentation format is not supported yet. ' +
|
||||
'The segmentation data (segments) could not be loaded.';
|
||||
inconsistencyWarnings.push(warningMessage);
|
||||
if (displaySet.loadError) {
|
||||
inconsistencyWarnings.push(displaySet.segLoadErrorMessagge);
|
||||
displaySet.inconsistencyWarnings = inconsistencyWarnings;
|
||||
}
|
||||
}
|
||||
|
||||
// cache the warnings
|
||||
displaySet.inconsistencyWarnings = inconsistencyWarnings;
|
||||
return inconsistencyWarnings;
|
||||
};
|
||||
|
||||
@ -736,10 +672,7 @@ const _mapStudiesToThumbnails = function(studies, activeDisplaySetInstanceUID) {
|
||||
altImageText = displaySet.Modality ? displaySet.Modality : 'UN';
|
||||
}
|
||||
|
||||
const hasWarnings = _checkForSeriesInconsistencesWarnings(
|
||||
displaySet,
|
||||
studies
|
||||
);
|
||||
const hasWarnings = _checkForSeriesInconsistencesWarnings(displaySet);
|
||||
|
||||
const hasDerivedDisplaySets = _checkForDerivedDisplaySets(
|
||||
displaySet,
|
||||
|
||||
@ -154,12 +154,25 @@ class ViewerMain extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
const { referencedDisplaySet } = displaySet.getSourceDisplaySet(
|
||||
const {
|
||||
referencedDisplaySet,
|
||||
activatedLabelmapPromise,
|
||||
} = displaySet.getSourceDisplaySet(
|
||||
this.props.studies,
|
||||
true,
|
||||
onDisplaySetLoadFailureHandler
|
||||
);
|
||||
displaySet = referencedDisplaySet;
|
||||
|
||||
activatedLabelmapPromise.then(activatedLabelmapIndex => {
|
||||
const selectionFired = new CustomEvent(
|
||||
'extensiondicomsegmentationsegselected',
|
||||
{
|
||||
detail: { activatedLabelmapIndex: activatedLabelmapIndex },
|
||||
}
|
||||
);
|
||||
document.dispatchEvent(selectionFired);
|
||||
});
|
||||
} else {
|
||||
displaySet = displaySet.getSourceDisplaySet(this.props.studies);
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ function DicomStorePickerModal({
|
||||
user,
|
||||
url,
|
||||
},
|
||||
onClose
|
||||
onClose,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -5449,10 +5449,10 @@ dateformat@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||
|
||||
dcmjs@0.19.8:
|
||||
version "0.19.8"
|
||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.19.8.tgz#9be7471f8f2e20d4e09489b5f0c51dfa56621ec1"
|
||||
integrity sha512-po/GWh7pXF+diO/umpqOfpF0Kk7MzAmhFlscOR+KchCIZbN0pmt2npFT3p8jMii/FBre++k+8PIvFa/LKh0P2A==
|
||||
dcmjs@0.19.9:
|
||||
version "0.19.9"
|
||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.19.9.tgz#be500101b1f75a28ea9eb7ef845a8ed197355e76"
|
||||
integrity sha512-V+0tND+SPX5e7WFeLzeoooaTsZbiKmEP2i3AyERsojaXXfaDV5E+pta5IqId4Ok1yfRT5qfTFwLGstGT6nLplA==
|
||||
dependencies:
|
||||
"@babel/polyfill" "^7.8.3"
|
||||
"@babel/runtime" "^7.8.4"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user