2020-03-13 20:45:26 +01:00
|
|
|
import OHIF from '@ohif/core';
|
2020-04-28 12:49:57 +02:00
|
|
|
import dcmjs from 'dcmjs';
|
2020-03-13 20:45:26 +01:00
|
|
|
import cornerstone from 'cornerstone-core';
|
|
|
|
|
import cornerstoneTools from 'cornerstone-tools';
|
|
|
|
|
|
|
|
|
|
const { DicomLoaderService } = OHIF.utils;
|
|
|
|
|
|
|
|
|
|
export default async function loadSegmentation(
|
|
|
|
|
segDisplaySet,
|
|
|
|
|
referencedDisplaySet,
|
|
|
|
|
studies
|
|
|
|
|
) {
|
|
|
|
|
const { StudyInstanceUID } = referencedDisplaySet;
|
|
|
|
|
|
|
|
|
|
// Set here is loading is asynchronous.
|
|
|
|
|
// If this function throws its set back to false.
|
|
|
|
|
segDisplaySet.isLoaded = true;
|
|
|
|
|
|
|
|
|
|
const segArrayBuffer = await DicomLoaderService.findDicomDataPromise(
|
|
|
|
|
segDisplaySet,
|
|
|
|
|
studies
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const dicomData = dcmjs.data.DicomMessage.readFile(segArrayBuffer);
|
|
|
|
|
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
|
|
|
|
|
dicomData.dict
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(dicomData.meta);
|
|
|
|
|
|
|
|
|
|
const imageIds = _getImageIdsForDisplaySet(
|
|
|
|
|
studies,
|
|
|
|
|
StudyInstanceUID,
|
|
|
|
|
referencedDisplaySet.SeriesInstanceUID
|
|
|
|
|
);
|
|
|
|
|
|
2020-05-04 16:10:39 +02:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
let results;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
results = _parseSeg(segArrayBuffer, imageIds);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
segDisplaySet.isLoaded = false;
|
|
|
|
|
reject(error);
|
|
|
|
|
}
|
2020-03-13 20:45:26 +01:00
|
|
|
|
2020-05-04 16:10:39 +02:00
|
|
|
const { labelmapBuffer, segMetadata, segmentsOnFrame } = results;
|
|
|
|
|
const { setters } = cornerstoneTools.getModule('segmentation');
|
2020-03-13 20:45:26 +01:00
|
|
|
|
2020-05-04 16:10:39 +02:00
|
|
|
// TODO: Could define a color LUT based on colors in the SEG.
|
|
|
|
|
const labelmapIndex = _getNextLabelmapIndex(imageIds[0]);
|
2020-05-04 16:43:09 +02:00
|
|
|
const colorLUTIndex = _makeColorLUTAndGetIndex(segMetadata);
|
2020-03-13 20:45:26 +01:00
|
|
|
|
2020-05-04 16:10:39 +02:00
|
|
|
setters.labelmap3DByFirstImageId(
|
|
|
|
|
imageIds[0],
|
|
|
|
|
labelmapBuffer,
|
|
|
|
|
labelmapIndex,
|
|
|
|
|
segMetadata,
|
|
|
|
|
imageIds.length,
|
2020-05-04 16:43:09 +02:00
|
|
|
segmentsOnFrame,
|
|
|
|
|
colorLUTIndex
|
2020-05-04 16:10:39 +02:00
|
|
|
);
|
2020-03-13 20:45:26 +01:00
|
|
|
|
2020-05-04 16:10:39 +02:00
|
|
|
segDisplaySet.labelmapIndex = labelmapIndex;
|
2020-03-13 20:45:26 +01:00
|
|
|
|
2020-05-04 16:10:39 +02:00
|
|
|
resolve(labelmapIndex);
|
|
|
|
|
});
|
2020-03-13 20:45:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _getNextLabelmapIndex(firstImageId) {
|
|
|
|
|
const { state } = cornerstoneTools.getModule('segmentation');
|
|
|
|
|
const brushStackState = state.series[firstImageId];
|
|
|
|
|
|
|
|
|
|
let labelmapIndex = 0;
|
|
|
|
|
|
|
|
|
|
if (brushStackState) {
|
|
|
|
|
const { labelmaps3D } = brushStackState;
|
|
|
|
|
labelmapIndex = labelmaps3D.length;
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < labelmaps3D.length; i++) {
|
|
|
|
|
if (!labelmaps3D[i]) {
|
|
|
|
|
labelmapIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return labelmapIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-04 16:43:09 +02:00
|
|
|
function _makeColorLUTAndGetIndex(segMetadata) {
|
|
|
|
|
const { setters, state } = cornerstoneTools.getModule('segmentation');
|
|
|
|
|
const { colorLutTables } = state;
|
|
|
|
|
const colorLUTIndex = _getNextColorLUTIndex();
|
|
|
|
|
|
|
|
|
|
const { data } = segMetadata;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!data.some(
|
|
|
|
|
segment =>
|
|
|
|
|
segment &&
|
|
|
|
|
(segment.ROIDisplayColor || segment.RecommendedDisplayCIELabValue)
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
// Use default cornerstoneTools colorLUT.
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const colorLUT = [];
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < data.length; i++) {
|
|
|
|
|
const segment = data[i];
|
|
|
|
|
if (!segment) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { ROIDisplayColor, RecommendedDisplayCIELabValue } = segment;
|
|
|
|
|
|
|
|
|
|
if (RecommendedDisplayCIELabValue) {
|
|
|
|
|
const rgb = dcmjs.data.Colors.dicomlab2RGB(
|
|
|
|
|
RecommendedDisplayCIELabValue
|
|
|
|
|
).map(x => Math.round(x * 255));
|
|
|
|
|
|
|
|
|
|
colorLUT[i] = [...rgb, 255];
|
|
|
|
|
} else if (ROIDisplayColor) {
|
|
|
|
|
colorLUT[i] = [...ROIDisplayColor, 255];
|
|
|
|
|
} else {
|
|
|
|
|
colorLUT[i] = [...colorLutTables[0][i]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
colorLUT.shift();
|
|
|
|
|
setters.colorLUT(colorLUTIndex, colorLUT);
|
|
|
|
|
|
|
|
|
|
return colorLUTIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _getNextColorLUTIndex() {
|
|
|
|
|
const { state } = cornerstoneTools.getModule('segmentation');
|
|
|
|
|
const { colorLutTables } = state;
|
|
|
|
|
|
|
|
|
|
let colorLUTIndex = colorLutTables.length;
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < colorLutTables.length; i++) {
|
|
|
|
|
if (!colorLutTables[i]) {
|
|
|
|
|
colorLUTIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return colorLUTIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-13 20:45:26 +01:00
|
|
|
function _parseSeg(arrayBuffer, imageIds) {
|
|
|
|
|
return dcmjs.adapters.Cornerstone.Segmentation.generateToolState(
|
|
|
|
|
imageIds,
|
|
|
|
|
arrayBuffer,
|
|
|
|
|
cornerstone.metaData
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _getImageIdsForDisplaySet(
|
|
|
|
|
studies,
|
|
|
|
|
StudyInstanceUID,
|
|
|
|
|
SeriesInstanceUID
|
|
|
|
|
) {
|
|
|
|
|
const study = studies.find(
|
|
|
|
|
study => study.StudyInstanceUID === StudyInstanceUID
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const displaySets = study.displaySets.filter(displaySet => {
|
|
|
|
|
return displaySet.SeriesInstanceUID === SeriesInstanceUID;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (displaySets.length > 1) {
|
|
|
|
|
console.warn(
|
|
|
|
|
'More than one display set with the same SeriesInstanceUID. This is not supported yet...'
|
|
|
|
|
);
|
|
|
|
|
// TODO -> We could make check the instance list and see if any match?
|
|
|
|
|
// Do we split the segmentation into two cornerstoneTools segmentations if there are images in both series?
|
|
|
|
|
// ^ Will that even happen?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const referencedDisplaySet = displaySets[0];
|
|
|
|
|
|
|
|
|
|
return referencedDisplaySet.images.map(image => image.getImageId());
|
|
|
|
|
}
|