IDC2345: implement thumbnails footnotes UI for segmentation series inconsistencies warning (#2364)
* IDC2345: implement thumbnails footnotes UI for segmentation series inconsistencies warning
This commit is contained in:
parent
a08caa9bdb
commit
07b272e024
@ -190,6 +190,11 @@ const SegmentationPanel = ({
|
|||||||
refreshSegmentations
|
refreshSegmentations
|
||||||
);
|
);
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
'extensiondicomsegmentationsegloadingfailed',
|
||||||
|
cleanSegmentationComboBox
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These are specific to each element;
|
* These are specific to each element;
|
||||||
* Need to iterate cornerstone-tools tracked enabled elements?
|
* Need to iterate cornerstone-tools tracked enabled elements?
|
||||||
@ -207,6 +212,10 @@ const SegmentationPanel = ({
|
|||||||
'extensiondicomsegmentationsegloaded',
|
'extensiondicomsegmentationsegloaded',
|
||||||
refreshSegmentations
|
refreshSegmentations
|
||||||
);
|
);
|
||||||
|
document.removeEventListener(
|
||||||
|
'extensiondicomsegmentationsegloadingfailed',
|
||||||
|
cleanSegmentationComboBox
|
||||||
|
);
|
||||||
cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
|
cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
|
||||||
enabledElement.removeEventListener(
|
enabledElement.removeEventListener(
|
||||||
'cornerstonetoolslabelmapmodified',
|
'cornerstonetoolslabelmapmodified',
|
||||||
@ -216,6 +225,17 @@ const SegmentationPanel = ({
|
|||||||
};
|
};
|
||||||
}, [activeIndex, viewports]);
|
}, [activeIndex, viewports]);
|
||||||
|
|
||||||
|
const cleanSegmentationComboBox = () => {
|
||||||
|
setState(state => ({
|
||||||
|
...state,
|
||||||
|
segmentsHidden: [],
|
||||||
|
segmentNumbers: [],
|
||||||
|
labelMapList: [],
|
||||||
|
segmentList: [],
|
||||||
|
isDisabled: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
const refreshSegmentations = () => {
|
const refreshSegmentations = () => {
|
||||||
const activeViewport = getActiveViewport();
|
const activeViewport = getActiveViewport();
|
||||||
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
|
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
|
||||||
@ -277,7 +297,10 @@ const SegmentationPanel = ({
|
|||||||
activeViewport.SeriesInstanceUID
|
activeViewport.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
return referencedSegDisplaysets.map((displaySet, index) => {
|
const filteredReferencedSegDisplaysets = referencedSegDisplaysets.filter(
|
||||||
|
(segDisplay => segDisplay.loadError !== true && segDisplay.isLoaded));
|
||||||
|
|
||||||
|
return filteredReferencedSegDisplaysets.map((displaySet, index) => {
|
||||||
const {
|
const {
|
||||||
labelmapIndex,
|
labelmapIndex,
|
||||||
originLabelMapIndex,
|
originLabelMapIndex,
|
||||||
|
|||||||
@ -63,11 +63,12 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
metadata,
|
metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.getSourceDisplaySet = function(studies) {
|
segDisplaySet.getSourceDisplaySet = function(studies, activateLabelMap = true, onDisplaySetLoadFailureHandler) {
|
||||||
return getSourceDisplaySet(studies, segDisplaySet);
|
return getSourceDisplaySet(studies, segDisplaySet, activateLabelMap, onDisplaySetLoadFailureHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.load = async function(referencedDisplaySet, studies) {
|
segDisplaySet.load = async function(referencedDisplaySet, studies) {
|
||||||
|
segDisplaySet.isLoading = true;
|
||||||
segDisplaySet.isLoaded = true;
|
segDisplaySet.isLoaded = true;
|
||||||
const { StudyInstanceUID } = referencedDisplaySet;
|
const { StudyInstanceUID } = referencedDisplaySet;
|
||||||
const segArrayBuffer = await DicomLoaderService.findDicomDataPromise(
|
const segArrayBuffer = await DicomLoaderService.findDicomDataPromise(
|
||||||
@ -82,59 +83,53 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
referencedDisplaySet.SeriesInstanceUID
|
referencedDisplaySet.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
let results;
|
|
||||||
try {
|
|
||||||
results = _parseSeg(segArrayBuffer, imageIds);
|
|
||||||
} catch (error) {
|
|
||||||
segDisplaySet.isLoaded = false;
|
|
||||||
segDisplaySet.loadError = true;
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
labelmapBufferArray,
|
|
||||||
segMetadata,
|
|
||||||
segmentsOnFrame,
|
|
||||||
segmentsOnFrameArray,
|
|
||||||
} = results;
|
|
||||||
|
|
||||||
let labelmapIndex;
|
const results = await _parseSeg(segArrayBuffer, imageIds);
|
||||||
if (labelmapBufferArray.length > 1) {
|
if (results === undefined) {
|
||||||
let labelmapIndexes = [];
|
return;
|
||||||
for (let i = 0; i < labelmapBufferArray.length; ++i) {
|
}
|
||||||
labelmapIndexes.push(
|
const {
|
||||||
await loadSegmentation(
|
labelmapBufferArray,
|
||||||
imageIds,
|
segMetadata,
|
||||||
segDisplaySet,
|
segmentsOnFrame,
|
||||||
labelmapBufferArray[i],
|
segmentsOnFrameArray,
|
||||||
segMetadata,
|
} = results;
|
||||||
segmentsOnFrame,
|
let labelmapIndex;
|
||||||
segmentsOnFrameArray[i]
|
if (labelmapBufferArray.length > 1) {
|
||||||
)
|
let labelmapIndexes = [];
|
||||||
);
|
for (let i = 0; i < labelmapBufferArray.length; ++i) {
|
||||||
}
|
labelmapIndexes.push(
|
||||||
/**
|
await loadSegmentation(
|
||||||
* Since overlapping segmentations have virtual labelmaps,
|
imageIds,
|
||||||
* originLabelMapIndex is used in the panel to select the correct dropdown value.
|
segDisplaySet,
|
||||||
*/
|
labelmapBufferArray[i],
|
||||||
segDisplaySet.hasOverlapping = true;
|
segMetadata,
|
||||||
segDisplaySet.originLabelMapIndex = labelmapIndexes[0];
|
segmentsOnFrame,
|
||||||
labelmapIndex = labelmapIndexes[0];
|
segmentsOnFrameArray[i]
|
||||||
console.warn('Overlapping segmentations!');
|
)
|
||||||
} else {
|
|
||||||
labelmapIndex = await loadSegmentation(
|
|
||||||
imageIds,
|
|
||||||
segDisplaySet,
|
|
||||||
labelmapBufferArray[0],
|
|
||||||
segMetadata,
|
|
||||||
segmentsOnFrame,
|
|
||||||
[]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
resolve(labelmapIndex);
|
/**
|
||||||
});
|
* Since overlapping segments have virtual labelmaps,
|
||||||
|
* originLabelMapIndex is used in the panel to select the correct dropdown value.
|
||||||
|
*/
|
||||||
|
segDisplaySet.hasOverlapping = true;
|
||||||
|
segDisplaySet.originLabelMapIndex = labelmapIndexes[0];
|
||||||
|
labelmapIndex = labelmapIndexes[0];
|
||||||
|
console.warn('Overlapping segments!');
|
||||||
|
} else {
|
||||||
|
labelmapIndex = await loadSegmentation(
|
||||||
|
imageIds,
|
||||||
|
segDisplaySet,
|
||||||
|
labelmapBufferArray[0],
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
segDisplaySet.isLoading = false;
|
||||||
return segDisplaySet;
|
return segDisplaySet;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,10 +3,12 @@ import { classes } from '@ohif/core';
|
|||||||
|
|
||||||
const { ImageSet } = classes;
|
const { ImageSet } = classes;
|
||||||
|
|
||||||
export default function getSourceDisplaySet(studies, segDisplaySet) {
|
export default function getSourceDisplaySet(studies, segDisplaySet, activateLabelMap = true, onDisplaySetLoadFailureHandler) {
|
||||||
const referencedDisplaySet = _getReferencedDisplaySet(segDisplaySet, studies);
|
const referencedDisplaySet = _getReferencedDisplaySet(segDisplaySet, studies);
|
||||||
|
|
||||||
setActiveLabelmap(referencedDisplaySet, studies, segDisplaySet);
|
if (activateLabelMap) {
|
||||||
|
setActiveLabelmap(referencedDisplaySet, studies, segDisplaySet, undefined, onDisplaySetLoadFailureHandler);
|
||||||
|
}
|
||||||
|
|
||||||
return referencedDisplaySet;
|
return referencedDisplaySet;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export default async function setActiveLabelmap(
|
|||||||
displaySet,
|
displaySet,
|
||||||
callback = () => {},
|
callback = () => {},
|
||||||
onDisplaySetLoadFailure = err => {
|
onDisplaySetLoadFailure = err => {
|
||||||
throw new Error(err.message);
|
console.error(err.message);
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const studyMetadata = studyMetadataManager.get(
|
const studyMetadata = studyMetadataManager.get(
|
||||||
@ -47,17 +47,24 @@ export default async function setActiveLabelmap(
|
|||||||
return labelmapIndex;
|
return labelmapIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!displaySet.isLoaded) {
|
if (displaySet.isLoading) {
|
||||||
const loadPromise = displaySet.load(referencedDisplaySet, studies);
|
return activeLabelmapIndex;
|
||||||
|
}
|
||||||
|
|
||||||
loadPromise.catch(error => {
|
if (!displaySet.isLoaded) {
|
||||||
|
try {
|
||||||
|
await displaySet.load(referencedDisplaySet, studies);
|
||||||
|
} catch (error) {
|
||||||
|
displaySet.isLoaded = false;
|
||||||
|
displaySet.isLoading = false;
|
||||||
|
displaySet.loadError = true;
|
||||||
onDisplaySetLoadFailure(error);
|
onDisplaySetLoadFailure(error);
|
||||||
|
|
||||||
// Return old index.
|
const event = new CustomEvent('extensiondicomsegmentationsegloadingfailed');
|
||||||
return activeLabelmapIndex;
|
document.dispatchEvent(event);
|
||||||
});
|
|
||||||
|
|
||||||
await loadPromise;
|
return activeLabelmapIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
labelmapIndex =
|
labelmapIndex =
|
||||||
|
|||||||
@ -45,9 +45,11 @@ import studyMetadataManager from './studyMetadataManager';
|
|||||||
* @param {string} referencedDisplaySet.studyInstanceUid
|
* @param {string} referencedDisplaySet.studyInstanceUid
|
||||||
* @param {Array} referencedDisplaySet.sopClassUids
|
* @param {Array} referencedDisplaySet.sopClassUids
|
||||||
* @param {Study[]} studies Collection of studies
|
* @param {Study[]} studies Collection of studies
|
||||||
|
* @param {object} logger
|
||||||
|
* @param {object} snackbar
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
|
async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, logger, snackbar) {
|
||||||
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
|
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||||
@ -78,11 +80,10 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// For each type, see if any are loaded, if not load the most recent.
|
// For each type, see if any are loaded, if not load the most recent.
|
||||||
Object.keys(displaySetsPerModality).forEach(key => {
|
await Promise.all(Object.keys(displaySetsPerModality).map(async (key) => {
|
||||||
const displaySets = displaySetsPerModality[key];
|
const displaySets = displaySetsPerModality[key];
|
||||||
|
|
||||||
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
|
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
|
||||||
|
|
||||||
if (isLoaded) {
|
if (isLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -107,20 +108,27 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
|
|||||||
|
|
||||||
recentDisplaySet.isLoading = true;
|
recentDisplaySet.isLoading = true;
|
||||||
|
|
||||||
promises.push(recentDisplaySet.load(referencedDisplaySet, studies));
|
try {
|
||||||
});
|
await recentDisplaySet.load(referencedDisplaySet, studies);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error({ error, message: error.message });
|
||||||
|
snackbar.show({
|
||||||
|
title: 'Error loading derived display set:',
|
||||||
|
message: error.message,
|
||||||
|
type: 'error',
|
||||||
|
error,
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
Promise.all(promises).then(() => {
|
/*
|
||||||
/*
|
* TODO: Improve the way we notify parts of the app
|
||||||
* TODO: Improve the way we notify parts of the app
|
* that depends on derived display sets to be loaded.
|
||||||
* that depends on derived display sets to be loaded.
|
* (Implement pubsub for better tracking of derived display sets)
|
||||||
* (Implement pubsub for better tracking of derived display sets)
|
*/
|
||||||
*/
|
const event = new CustomEvent('deriveddisplaysetsloadedandcached');
|
||||||
const event = new CustomEvent('deriveddisplaysetsloadedandcached');
|
document.dispatchEvent(event);
|
||||||
document.dispatchEvent(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
return promises;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default loadAndCacheDerivedDisplaySets;
|
export default loadAndCacheDerivedDisplaySets;
|
||||||
|
|||||||
@ -41,19 +41,7 @@ const ViewportGrid = function(props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isStudyLoaded) {
|
if (isStudyLoaded) {
|
||||||
viewportData.forEach(displaySet => {
|
viewportData.forEach(displaySet => {
|
||||||
const promises = loadAndCacheDerivedDisplaySets(displaySet, studies);
|
loadAndCacheDerivedDisplaySets(displaySet, studies, logger, snackbar);
|
||||||
promises.forEach(promise => {
|
|
||||||
promise.catch(error => {
|
|
||||||
logger.error({ error, message: error.message });
|
|
||||||
snackbar.show({
|
|
||||||
title: 'Error loading derived display set:',
|
|
||||||
message: error.message,
|
|
||||||
type: 'error',
|
|
||||||
error,
|
|
||||||
autoClose: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [studies, viewportData, isStudyLoaded, snackbar]);
|
}, [studies, viewportData, isStudyLoaded, snackbar]);
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { connect } from 'react-redux';
|
|||||||
import { StudyBrowser } from '@ohif/ui';
|
import { StudyBrowser } from '@ohif/ui';
|
||||||
import cloneDeep from 'lodash.clonedeep';
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
import findDisplaySetByUID from './findDisplaySetByUID';
|
import findDisplaySetByUID from './findDisplaySetByUID';
|
||||||
|
import { servicesManager } from './../App.js';
|
||||||
|
|
||||||
const { studyMetadataManager } = OHIF.utils;
|
const { studyMetadataManager } = OHIF.utils;
|
||||||
|
|
||||||
@ -48,8 +49,22 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
|
|
||||||
if (displaySet.isDerived) {
|
if (displaySet.isDerived) {
|
||||||
const { Modality } = displaySet;
|
const { Modality } = displaySet;
|
||||||
|
if (Modality === 'SEG' && servicesManager) {
|
||||||
|
const {LoggerService, UINotificationService} = servicesManager.services;
|
||||||
|
const onDisplaySetLoadFailureHandler = error => {
|
||||||
|
LoggerService.error({ error, message: error.message });
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'DICOM Segmentation Loader',
|
||||||
|
message: error.message,
|
||||||
|
type: 'error',
|
||||||
|
autoClose: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
displaySet = displaySet.getSourceDisplaySet(ownProps.studyMetadata);
|
displaySet = displaySet.getSourceDisplaySet(ownProps.studyMetadata, true, onDisplaySetLoadFailureHandler);
|
||||||
|
} else {
|
||||||
|
displaySet = displaySet.getSourceDisplaySet(ownProps.studyMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
if (!displaySet) {
|
if (!displaySet) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useLogger } from '@ohif/ui';
|
||||||
|
|
||||||
import OHIF, { MODULE_TYPES, DICOMSR } from '@ohif/core';
|
import OHIF, { MODULE_TYPES, DICOMSR } from '@ohif/core';
|
||||||
import { withDialog } from '@ohif/ui';
|
import { withDialog } from '@ohif/ui';
|
||||||
@ -13,6 +14,7 @@ import SidePanel from './../components/SidePanel.js';
|
|||||||
import ErrorBoundaryDialog from './../components/ErrorBoundaryDialog';
|
import ErrorBoundaryDialog from './../components/ErrorBoundaryDialog';
|
||||||
import { extensionManager } from './../App.js';
|
import { extensionManager } from './../App.js';
|
||||||
import { ReconstructionIssues } from './../../../core/src/enums.js';
|
import { ReconstructionIssues } from './../../../core/src/enums.js';
|
||||||
|
import dcmjs from 'dcmjs';
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
import WhiteLabelingContext from '../context/WhiteLabelingContext.js';
|
import WhiteLabelingContext from '../context/WhiteLabelingContext.js';
|
||||||
@ -21,6 +23,7 @@ import AppContext from '../context/AppContext';
|
|||||||
|
|
||||||
import './Viewer.css';
|
import './Viewer.css';
|
||||||
import { finished } from 'stream';
|
import { finished } from 'stream';
|
||||||
|
import { cornerstoneWADOImageLoader } from 'cornerstone-wado-image-loader';
|
||||||
|
|
||||||
class Viewer extends Component {
|
class Viewer extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -369,59 +372,144 @@ class Viewer extends Component {
|
|||||||
export default withDialog(Viewer);
|
export default withDialog(Viewer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async function to check if there are any inconsistences in the series
|
* Async function to check if there are any inconsistences in the series.
|
||||||
* (i.e. reconstructable to a 3D volume). If not reconstructable MPR is disabled.
|
|
||||||
* The actual computations are done in isDisplaySetReconstructable.
|
|
||||||
*
|
*
|
||||||
|
* 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 reconstructable 3D volume:
|
||||||
* 1) Is series multiframe?
|
* 1) Is series multiframe?
|
||||||
* 2) Do the frames have different dimensions/numer of components/orientations?
|
* 2) Do the frames have different dimensions/number of components/orientations?
|
||||||
* 3) Has the series any missing frames or irregular spacing?
|
* 3) Has the series any missing frames or irregular spacing?
|
||||||
* 4) Is the series 4D?
|
* 4) Is the series 4D?
|
||||||
*
|
*
|
||||||
|
* If not reconstructable, MPR is disabled.
|
||||||
|
* The actual computations are done in isDisplaySetReconstructable.
|
||||||
|
*
|
||||||
* @param {*object} displaySet
|
* @param {*object} displaySet
|
||||||
* @returns {[string]} an array of strings containing the warnings
|
* @returns {[string]} an array of strings containing the warnings
|
||||||
*/
|
*/
|
||||||
const _checkForSeriesInconsistencesWarnings = async function (displaySet) {
|
const _checkForSeriesInconsistencesWarnings = async function (displaySet, studies) {
|
||||||
// NOTE: at the moment this function is async even if it does not perfom any heavy calculation.
|
|
||||||
// We may add or move here some of the computations
|
|
||||||
// done when creating the displaySet (see makeDisplaySet and isDisplaySetReconstructable).
|
|
||||||
// the thumbnail footnotes warning react element is already set up to handle a promise.
|
|
||||||
const warningsList = [];
|
const warningsList = [];
|
||||||
if (displaySet.warningIssues && displaySet.warningIssues.length !== 0) {
|
|
||||||
displaySet.warningIssues.forEach(warning => {
|
if (displaySet.Modality !== 'SEG') {
|
||||||
switch (warning) {
|
if (displaySet.warningIssues && displaySet.warningIssues.length !== 0) {
|
||||||
case ReconstructionIssues.DATASET_4D:
|
displaySet.warningIssues.forEach(warning => {
|
||||||
warningsList.push("The dataset is 4D.");
|
switch (warning) {
|
||||||
break;
|
case ReconstructionIssues.DATASET_4D:
|
||||||
case ReconstructionIssues.VARYING_IMAGESDIMENSIONS:
|
warningsList.push('The dataset is 4D.');
|
||||||
warningsList.push("The dataset frames have different dimensions (rows, columns).");
|
break;
|
||||||
break;
|
case ReconstructionIssues.VARYING_IMAGESDIMENSIONS:
|
||||||
case ReconstructionIssues.VARYING_IMAGESCOMPONENTS:
|
warningsList.push('The dataset frames have different dimensions (rows, columns).');
|
||||||
warningsList.push("The dataset frames have different components (Sample per pixel).");
|
break;
|
||||||
break;
|
case ReconstructionIssues.VARYING_IMAGESCOMPONENTS:
|
||||||
case ReconstructionIssues.VARYING_IMAGESORIENTATION:
|
warningsList.push('The dataset frames have different components (Sample per pixel).');
|
||||||
warningsList.push("The dataset frames have different orientation.");
|
break;
|
||||||
break;
|
case ReconstructionIssues.VARYING_IMAGESORIENTATION:
|
||||||
case ReconstructionIssues.IRREGULAR_SPACING:
|
warningsList.push('The dataset frames have different orientation.');
|
||||||
warningsList.push("The dataset frames have different pixel spacing.");
|
break;
|
||||||
break;
|
case ReconstructionIssues.IRREGULAR_SPACING:
|
||||||
case ReconstructionIssues.MULTIFFRAMES:
|
warningsList.push('The dataset frames have different pixel spacing.');
|
||||||
warningsList.push("The dataset is a multiframes.");
|
break;
|
||||||
break;
|
case ReconstructionIssues.MULTIFFRAMES:
|
||||||
default:
|
warningsList.push('The dataset is a multiframes.');
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
});
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
warningsList.push('The datasets is not a reconstructable 3D volume. MPR mode is not available.');
|
warningsList.push('The datasets is not a reconstructable 3D volume. MPR mode is not available.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displaySet.missingFrames &&
|
||||||
|
(!displaySet.warningIssues ||
|
||||||
|
(displaySet.warningIssues && !displaySet.warningIssues.find(warn => warn === ReconstructionIssues.DATASET_4D)))) {
|
||||||
|
warningsList.push('The datasets is missing frames: ' + displaySet.missingFrames + '.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const segMetadata = displaySet.metadata;
|
||||||
|
if (!segMetadata) {
|
||||||
|
return warningsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceDisplaySet = displaySet.getSourceDisplaySet(studies, false);
|
||||||
|
const imageIds = sourceDisplaySet.images.map(image => image.getImageId());
|
||||||
|
if (!imageIds || imageIds.length === 0) {
|
||||||
|
return warningsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (warningsList.length === 0) {
|
||||||
|
const warningMessage = 'The segmentation ' +
|
||||||
|
'has frames out of plane respect to the source images.';
|
||||||
|
warningsList.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.';
|
||||||
|
warningsList.push(warningMessage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warningsList.length !== 0) {
|
||||||
|
const warningMessage = 'The segmentation format is not supported yet. ' +
|
||||||
|
'The segmentation data (segments) could not be loaded.';
|
||||||
|
warningsList.push(warningMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displaySet.missingFrames &&
|
return warningsList;
|
||||||
(!displaySet.warningIssues ||
|
|
||||||
(displaySet.warningIssues && !displaySet.warningIssues.find(warn => warn === ReconstructionIssues.DATASET_4D)))) {
|
|
||||||
warningsList.push('The datasets is missing frames: ' + displaySet.missingFrames + '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return warningsList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -463,7 +551,7 @@ const _mapStudiesToThumbnails = function(studies) {
|
|||||||
altImageText = displaySet.Modality ? displaySet.Modality : 'UN';
|
altImageText = displaySet.Modality ? displaySet.Modality : 'UN';
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasWarnings = _checkForSeriesInconsistencesWarnings(displaySet)
|
const hasWarnings = _checkForSeriesInconsistencesWarnings(displaySet, studies);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
imageId,
|
imageId,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import './ViewerMain.css';
|
import './ViewerMain.css';
|
||||||
|
import { servicesManager } from './../App.js';
|
||||||
import { Component } from 'react';
|
import { Component } from 'react';
|
||||||
import { ConnectedViewportGrid } from './../components/ViewportGrid/index.js';
|
import { ConnectedViewportGrid } from './../components/ViewportGrid/index.js';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@ -141,7 +141,22 @@ class ViewerMain extends Component {
|
|||||||
|
|
||||||
if (displaySet.isDerived) {
|
if (displaySet.isDerived) {
|
||||||
const { Modality } = displaySet;
|
const { Modality } = displaySet;
|
||||||
displaySet = displaySet.getSourceDisplaySet(this.props.studies);
|
if (Modality === 'SEG' && servicesManager) {
|
||||||
|
const {LoggerService, UINotificationService} = servicesManager.services;
|
||||||
|
const onDisplaySetLoadFailureHandler = error => {
|
||||||
|
LoggerService.error({ error, message: error.message });
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'DICOM Segmentation Loader',
|
||||||
|
message: error.message,
|
||||||
|
type: 'error',
|
||||||
|
autoClose: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
displaySet = displaySet.getSourceDisplaySet(this.props.studies, true, onDisplaySetLoadFailureHandler);
|
||||||
|
} else {
|
||||||
|
displaySet = displaySet.getSourceDisplaySet(this.props.studies);
|
||||||
|
}
|
||||||
|
|
||||||
if (!displaySet) {
|
if (!displaySet) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
@ -301,7 +301,6 @@ function ViewerRetrieveStudyData({
|
|||||||
});
|
});
|
||||||
|
|
||||||
setStudies(studies);
|
setStudies(studies);
|
||||||
setIsStudyLoaded(true);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -6619,10 +6619,10 @@ dateformat@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||||
|
|
||||||
dcmjs@0.18.3:
|
dcmjs@0.18.4:
|
||||||
version "0.18.3"
|
version "0.18.4"
|
||||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.18.3.tgz#17d5a23d96b63264b8a1a859e4cb000b3e20495b"
|
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.18.4.tgz#70643e665dda373ed83bc7228981350f07dc3849"
|
||||||
integrity sha512-nmG6wEBcIMnadXAZOTcXwacBv+lrh8bqPwIKZwdy7AkdihLEwI6/OnyP+PYjzcdltuPu9wOQcKZionvXuCteag==
|
integrity sha512-iprngI79c6iAJRq16/K8JNVvUvrSzN7u6yGES8pl2/KvtbSIRP2LEXwbZ01bKtXz9syEtiQNvdtTR+gTZtd4Sw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/polyfill" "^7.8.3"
|
"@babel/polyfill" "^7.8.3"
|
||||||
"@babel/runtime" "^7.8.4"
|
"@babel/runtime" "^7.8.4"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user