fix: Improve rt handling for bulkdata (#5415)
This commit is contained in:
parent
6db50d26b2
commit
d88a341884
@ -2,9 +2,34 @@ import dcmjs from 'dcmjs';
|
|||||||
const { DicomMessage, DicomMetaDictionary } = dcmjs.data;
|
const { DicomMessage, DicomMetaDictionary } = dcmjs.data;
|
||||||
const dicomlab2RGB = dcmjs.data.Colors.dicomlab2RGB;
|
const dicomlab2RGB = dcmjs.data.Colors.dicomlab2RGB;
|
||||||
|
|
||||||
async function checkAndLoadContourData(instance, datasource) {
|
/**
|
||||||
|
* Checks and loads contour data for RT Structure Set, handling both inline and bulk data URIs.
|
||||||
|
* Processes ROIContourSequence to extract contour data and resolve any bulk data references.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @function checkAndLoadContourData
|
||||||
|
* @param {Object} params - Parameters object
|
||||||
|
* @param {Object} params.instance - Initial RT Structure instance
|
||||||
|
* @param {Object} params.dataSource - Data source for retrieving bulk data
|
||||||
|
* @param {Object} params.extensionManager - OHIF extension manager
|
||||||
|
* @param {Object} params.rtStructDisplaySet - RT Structure display set
|
||||||
|
* @param {Object} params.headers - HTTP headers for requests
|
||||||
|
* @returns {Promise<Object>} Promise that resolves to the processed RT Structure instance with loaded contour data
|
||||||
|
* @throws {Promise<string>} Rejects with error message if instance is invalid or data retrieval fails
|
||||||
|
*/
|
||||||
|
async function checkAndLoadContourData({
|
||||||
|
instance: initialInstance,
|
||||||
|
dataSource,
|
||||||
|
extensionManager,
|
||||||
|
rtStructDisplaySet,
|
||||||
|
headers,
|
||||||
|
}) {
|
||||||
|
let instance = initialInstance;
|
||||||
if (!instance || !instance.ROIContourSequence) {
|
if (!instance || !instance.ROIContourSequence) {
|
||||||
return Promise.reject('Invalid instance object or ROIContourSequence');
|
instance = await getRTStructInstance({ extensionManager, rtStructDisplaySet, headers });
|
||||||
|
if (!instance || !instance.ROIContourSequence) {
|
||||||
|
return Promise.reject('Invalid instance object or ROIContourSequence');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const promisesMap = new Map();
|
const promisesMap = new Map();
|
||||||
@ -30,11 +55,11 @@ async function checkAndLoadContourData(instance, datasource) {
|
|||||||
} else if (contourData && contourData.BulkDataURI) {
|
} else if (contourData && contourData.BulkDataURI) {
|
||||||
const bulkDataURI = contourData.BulkDataURI;
|
const bulkDataURI = contourData.BulkDataURI;
|
||||||
|
|
||||||
if (!datasource || !datasource.retrieve || !datasource.retrieve.bulkDataURI) {
|
if (!dataSource || !dataSource.retrieve || !dataSource.retrieve.bulkDataURI) {
|
||||||
return Promise.reject('Invalid datasource object or retrieve function');
|
return Promise.reject('Invalid dataSource object or retrieve function');
|
||||||
}
|
}
|
||||||
|
|
||||||
const bulkDataPromise = datasource.retrieve.bulkDataURI({
|
const bulkDataPromise = dataSource.retrieve.bulkDataURI({
|
||||||
BulkDataURI: bulkDataURI,
|
BulkDataURI: bulkDataURI,
|
||||||
StudyInstanceUID: instance.StudyInstanceUID,
|
StudyInstanceUID: instance.StudyInstanceUID,
|
||||||
SeriesInstanceUID: instance.SeriesInstanceUID,
|
SeriesInstanceUID: instance.SeriesInstanceUID,
|
||||||
@ -87,35 +112,74 @@ async function checkAndLoadContourData(instance, datasource) {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function loadRTStruct(extensionManager, rtStructDisplaySet, headers) {
|
/**
|
||||||
|
* Retrieves and parses RT Structure Set instance from DICOM data.
|
||||||
|
* Uses the cornerstone utility module to load DICOM data and converts it to a naturalized dataset.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @function getRTStructInstance
|
||||||
|
* @param {Object} params - Parameters object
|
||||||
|
* @param {Object} params.extensionManager - OHIF extension manager
|
||||||
|
* @param {Object} params.rtStructDisplaySet - RT Structure display set
|
||||||
|
* @param {Object} params.headers - HTTP headers for requests
|
||||||
|
* @returns {Promise<Object>} Promise that resolves to the parsed RT Structure dataset
|
||||||
|
*/
|
||||||
|
const getRTStructInstance = async ({ extensionManager, rtStructDisplaySet, headers }) => {
|
||||||
const utilityModule = extensionManager.getModuleEntry(
|
const utilityModule = extensionManager.getModuleEntry(
|
||||||
'@ohif/extension-cornerstone.utilityModule.common'
|
'@ohif/extension-cornerstone.utilityModule.common'
|
||||||
);
|
);
|
||||||
|
const { dicomLoaderService } = utilityModule.exports;
|
||||||
|
const segArrayBuffer = await dicomLoaderService.findDicomDataPromise(
|
||||||
|
rtStructDisplaySet,
|
||||||
|
null,
|
||||||
|
headers
|
||||||
|
);
|
||||||
|
const dicomData = DicomMessage.readFile(segArrayBuffer);
|
||||||
|
const rtStructDataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
||||||
|
rtStructDataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
||||||
|
return rtStructDataset;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main function to load and process RT Structure Set data.
|
||||||
|
* Creates a structure set object with ROI contours, metadata, and visualization properties.
|
||||||
|
* Handles both bulk data URI and inline contour data scenarios.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @function loadRTStruct
|
||||||
|
* @param {Object} extensionManager - OHIF extension manager
|
||||||
|
* @param {Object} rtStructDisplaySet - RT Structure display set to process
|
||||||
|
* @param {Object} headers - HTTP headers for data requests
|
||||||
|
* @returns {Promise<Object>} Promise that resolves to a structure set object containing:
|
||||||
|
* - StructureSetLabel: Label of the structure set
|
||||||
|
* - SeriesInstanceUID: Series instance UID
|
||||||
|
* - ROIContours: Array of ROI contour data with points and metadata
|
||||||
|
* - visible: Visibility state
|
||||||
|
* - ReferencedSOPInstanceUIDsSet: Set of referenced SOP instance UIDs
|
||||||
|
*/
|
||||||
|
export default async function loadRTStruct(extensionManager, rtStructDisplaySet, headers) {
|
||||||
const dataSource = extensionManager.getActiveDataSource()[0];
|
const dataSource = extensionManager.getActiveDataSource()[0];
|
||||||
const { bulkDataURI } = dataSource.getConfig?.() || {};
|
const { bulkDataURI } = dataSource.getConfig?.() || {};
|
||||||
|
|
||||||
const { dicomLoaderService } = utilityModule.exports;
|
|
||||||
|
|
||||||
// Set here is loading is asynchronous.
|
// Set here is loading is asynchronous.
|
||||||
// If this function throws its set back to false.
|
// If this function throws its set back to false.
|
||||||
rtStructDisplaySet.isLoaded = true;
|
rtStructDisplaySet.isLoaded = true;
|
||||||
let instance = rtStructDisplaySet.instance;
|
let instance = rtStructDisplaySet.instance;
|
||||||
|
|
||||||
if (!bulkDataURI || !bulkDataURI.enabled) {
|
if (!bulkDataURI || !bulkDataURI.enabled) {
|
||||||
const segArrayBuffer = await dicomLoaderService.findDicomDataPromise(
|
instance = await getRTStructInstance({ extensionManager, rtStructDisplaySet, headers });
|
||||||
rtStructDisplaySet,
|
|
||||||
null,
|
|
||||||
headers
|
|
||||||
);
|
|
||||||
|
|
||||||
const dicomData = DicomMessage.readFile(segArrayBuffer);
|
|
||||||
const rtStructDataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
|
||||||
rtStructDataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
|
||||||
instance = rtStructDataset;
|
|
||||||
} else {
|
} else {
|
||||||
await checkAndLoadContourData(instance, dataSource);
|
instance = await checkAndLoadContourData({
|
||||||
|
instance,
|
||||||
|
dataSource,
|
||||||
|
extensionManager,
|
||||||
|
rtStructDisplaySet,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { StructureSetROISequence, ROIContourSequence, RTROIObservationsSequence } = instance;
|
const { StructureSetROISequence, ROIContourSequence, RTROIObservationsSequence } = instance;
|
||||||
@ -132,7 +196,6 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
|
|||||||
for (let i = 0; i < ROIContourSequence.length; i++) {
|
for (let i = 0; i < ROIContourSequence.length; i++) {
|
||||||
const ROIContour = ROIContourSequence[i];
|
const ROIContour = ROIContourSequence[i];
|
||||||
const { ContourSequence } = ROIContour;
|
const { ContourSequence } = ROIContour;
|
||||||
|
|
||||||
if (!ContourSequence) {
|
if (!ContourSequence) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -185,6 +248,18 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
|
|||||||
return structureSet;
|
return structureSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets metadata for ROI contour data and adds it to the structure set.
|
||||||
|
* Extracts ROI information from StructureSetROISequence and RTROIObservationsSequence,
|
||||||
|
* then creates a complete ROI contour data object with visualization properties.
|
||||||
|
*
|
||||||
|
* @function _setROIContourMetadata
|
||||||
|
* @param {Object} structureSet - The structure set object to add ROI contour to
|
||||||
|
* @param {Array} StructureSetROISequence - Array of structure set ROI definitions
|
||||||
|
* @param {Array} RTROIObservationsSequence - Array of RT ROI observations
|
||||||
|
* @param {Object} ROIContour - ROI contour object containing contour data
|
||||||
|
* @param {Array} contourPoints - Array of processed contour points
|
||||||
|
*/
|
||||||
function _setROIContourMetadata(
|
function _setROIContourMetadata(
|
||||||
structureSet,
|
structureSet,
|
||||||
StructureSetROISequence,
|
StructureSetROISequence,
|
||||||
@ -220,6 +295,14 @@ function _setROIContourMetadata(
|
|||||||
structureSet.ROIContours.push(ROIContourData);
|
structureSet.ROIContours.push(ROIContourData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the display color for ROI contour data.
|
||||||
|
* Uses ROIDisplayColor if available, otherwise converts RecommendedDisplayCIELabValue to RGB.
|
||||||
|
*
|
||||||
|
* @function _setROIContourDataColor
|
||||||
|
* @param {Object} ROIContour - ROI contour object containing color information
|
||||||
|
* @param {Object} ROIContourData - ROI contour data object to set color on
|
||||||
|
*/
|
||||||
function _setROIContourDataColor(ROIContour, ROIContourData) {
|
function _setROIContourDataColor(ROIContour, ROIContourData) {
|
||||||
let { ROIDisplayColor, RecommendedDisplayCIELabValue } = ROIContour;
|
let { ROIDisplayColor, RecommendedDisplayCIELabValue } = ROIContour;
|
||||||
|
|
||||||
@ -233,6 +316,15 @@ function _setROIContourDataColor(ROIContour, ROIContourData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets RT ROI observations metadata for ROI contour data.
|
||||||
|
* Finds matching RTROIObservations by ROINumber and adds observation details to contour data.
|
||||||
|
*
|
||||||
|
* @function _setROIContourRTROIObservations
|
||||||
|
* @param {Object} ROIContourData - ROI contour data object to add observations to
|
||||||
|
* @param {Array} RTROIObservationsSequence - Array of RT ROI observations
|
||||||
|
* @param {number} ROINumber - ROI number to match observations
|
||||||
|
*/
|
||||||
function _setROIContourRTROIObservations(ROIContourData, RTROIObservationsSequence, ROINumber) {
|
function _setROIContourRTROIObservations(ROIContourData, RTROIObservationsSequence, ROINumber) {
|
||||||
const RTROIObservations = RTROIObservationsSequence.find(
|
const RTROIObservations = RTROIObservationsSequence.find(
|
||||||
RTROIObservations => RTROIObservations.ReferencedROINumber === ROINumber
|
RTROIObservations => RTROIObservations.ReferencedROINumber === ROINumber
|
||||||
@ -252,6 +344,14 @@ function _setROIContourRTROIObservations(ROIContourData, RTROIObservationsSequen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a single object or array to an array.
|
||||||
|
* Utility function to ensure consistent array handling for DICOM sequences.
|
||||||
|
*
|
||||||
|
* @function _toArray
|
||||||
|
* @param {*} objOrArray - Object or array to convert
|
||||||
|
* @returns {Array} Array containing the input (if already array) or wrapped in array
|
||||||
|
*/
|
||||||
function _toArray(objOrArray) {
|
function _toArray(objOrArray) {
|
||||||
return Array.isArray(objOrArray) ? objOrArray : [objOrArray];
|
return Array.isArray(objOrArray) ? objOrArray : [objOrArray];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user