fix(dicom): Update multiframe DICOM JSON parsing for correct image ID generation (#4307)

This commit is contained in:
Igor Octaviano 2024-07-26 13:47:49 -03:00 committed by GitHub
parent 9f0828ae27
commit 16b7aa4f65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 15 deletions

View File

@ -4,26 +4,34 @@
* The JSON file can be used to load the study into the OHIF Viewer. You can get more detail * The JSON file can be used to load the study into the OHIF Viewer. You can get more detail
* in the DICOM JSON Data source on docs.ohif.org * in the DICOM JSON Data source on docs.ohif.org
* *
* Usage: node dicomStudyToJSONLaunch.js <studyFolder> <urlPrefix> <outputJSONPath> * Usage: node dicomStudyToJSONLaunch.js <studyFolder> <urlPrefix> <outputJSONPath> <optional scheme>
* *
* params: * params:
* - studyFolder: path to the study folder * - studyFolder: path to the study folder which contains the DICOM files
* - urlPrefix: prefix to the url that will be used to load the study into the viewer. For instance * - urlPrefix: prefix to the url that will be used to load the study into the viewer. For instance
* we use https://ohif-assets.s3.us-east-2.amazonaws.com/dicom-json/data as the urlPrefix for the * we use https://ohif-assets.s3.us-east-2.amazonaws.com/dicom-json/data as the urlPrefix for the
* example since the data is hosted on S3 and each study is in a folder. So the url in the generated * example since the data is hosted on S3 and each study is in a folder. So the url in the generated
* json file for the first instance of the first series of the first study will be * json file for the first instance of the first series of the first study will be
* dicomweb:https://ohif-assets.s3.us-east-2.amazonaws.com/dicom-json/data/Series1/Instance1 * dicomweb:https://ohif-assets.s3.us-east-2.amazonaws.com/dicom-json/data/Series1/Instance1
*
* as you see the dicomweb is a prefix that is used to load the data into the viewer, which is suited when
* the .dcm file is hosted statically and can be accessed via a URL (like our example above)
* However, you can specify a new scheme bellow.
*
* - outputJSONPath: path to the output JSON file * - outputJSONPath: path to the output JSON file
* - scheme: default dicomweb if not provided
*/ */
const dcmjs = require('dcmjs'); const dcmjs = require('dcmjs');
const path = require('path'); const path = require('path');
const fs = require('fs').promises; const fs = require('fs').promises;
const args = process.argv.slice(2); const args = process.argv.slice(2);
const [studyDirectory, urlPrefix, outputPath] = args; const [studyDirectory, urlPrefix, outputPath, scheme = 'dicomweb'] = args;
if (args.length !== 3) { if (args.length < 3 || args.length > 4) {
console.error('Usage: node dicomStudyToJSONLaunch.js <studyFolder> <urlPrefix> <outputJSONPath>'); console.error(
'Usage: node dicomStudyToJSONLaunch.js <studyFolder> <urlPrefix> <outputJSONPath> [scheme]'
);
process.exit(1); process.exit(1);
} }
@ -31,7 +39,7 @@ const model = {
studies: [], studies: [],
}; };
async function convertDICOMToJSON(studyDirectory, urlPrefix, outputPath) { async function convertDICOMToJSON(studyDirectory, urlPrefix, outputPath, scheme) {
try { try {
const files = await recursiveReadDir(studyDirectory); const files = await recursiveReadDir(studyDirectory);
console.debug('Processing...'); console.debug('Processing...');
@ -42,7 +50,7 @@ async function convertDICOMToJSON(studyDirectory, urlPrefix, outputPath) {
const dicomDict = dcmjs.data.DicomMessage.readFile(arrayBuffer.buffer); const dicomDict = dcmjs.data.DicomMessage.readFile(arrayBuffer.buffer);
const instance = dcmjs.data.DicomMetaDictionary.naturalizeDataset(dicomDict.dict); const instance = dcmjs.data.DicomMetaDictionary.naturalizeDataset(dicomDict.dict);
instance.fileLocation = createImageId(file, urlPrefix, studyDirectory); instance.fileLocation = createImageId(file, urlPrefix, studyDirectory, scheme);
processInstance(instance); processInstance(instance);
} }
} }
@ -77,10 +85,10 @@ async function recursiveReadDir(dir) {
return results; return results;
} }
function createImageId(fileLocation, urlPrefix, studyDirectory) { function createImageId(fileLocation, urlPrefix, studyDirectory, scheme) {
const relativePath = path.relative(studyDirectory, fileLocation); const relativePath = path.relative(studyDirectory, fileLocation);
const normalizedPath = path.normalize(relativePath).replace(/\\/g, '/'); const normalizedPath = path.normalize(relativePath).replace(/\\/g, '/');
return `dicomweb:${urlPrefix}${normalizedPath}`; return `${scheme}:${urlPrefix}${normalizedPath}`;
} }
function processInstance(instance) { function processInstance(instance) {
@ -262,4 +270,4 @@ function createInstanceMetaDataMultiFrame(instance) {
return instances; return instances;
} }
convertDICOMToJSON(studyDirectory, urlPrefix, outputPath); convertDICOMToJSON(studyDirectory, urlPrefix, outputPath, scheme);

View File

@ -59,8 +59,6 @@ const findStudies = (key, value) => {
}; };
function createDicomJSONApi(dicomJsonConfig) { function createDicomJSONApi(dicomJsonConfig) {
const { wadoRoot } = dicomJsonConfig;
const implementation = { const implementation = {
initialize: async ({ query, url }) => { initialize: async ({ query, url }) => {
if (!url) { if (!url) {
@ -89,7 +87,8 @@ function createDicomJSONApi(dicomJsonConfig) {
SeriesInstanceUID = series.SeriesInstanceUID; SeriesInstanceUID = series.SeriesInstanceUID;
series.instances.forEach(instance => { series.instances.forEach(instance => {
const { url: imageId, metadata: naturalizedDicom } = instance; const { metadata: naturalizedDicom } = instance;
const imageId = getImageId({ instance, config: dicomJsonConfig });
// Add imageId specific mapping to this data as the URL isn't necessarliy WADO-URI. // Add imageId specific mapping to this data as the URL isn't necessarliy WADO-URI.
metadataProvider.addImageIdToUIDs(imageId, { metadataProvider.addImageIdToUIDs(imageId, {
@ -228,7 +227,7 @@ function createDicomJSONApi(dicomJsonConfig) {
const obj = { const obj = {
...modifiedMetadata, ...modifiedMetadata,
url: instance.url, url: instance.url,
imageId: instance.url, imageId: getImageId({ instance, config: dicomJsonConfig }),
...series, ...series,
...study, ...study,
}; };
@ -257,7 +256,15 @@ function createDicomJSONApi(dicomJsonConfig) {
return imageIds; return imageIds;
} }
displaySet.images.forEach(instance => { const { StudyInstanceUID, SeriesInstanceUID } = displaySet;
const study = findStudies('StudyInstanceUID', StudyInstanceUID)[0];
const series = study.series.find(s => s.SeriesInstanceUID === SeriesInstanceUID);
let instances = displaySet.images;
if (series.instances.length > displaySet.images.length) {
instances = series.instances;
}
instances.forEach(instance => {
const NumberOfFrames = instance.NumberOfFrames; const NumberOfFrames = instance.NumberOfFrames;
if (NumberOfFrames > 1) { if (NumberOfFrames > 1) {