fix: 🐛 Add DicomLoaderService & FileLoaderService to fix SR, PDF, and SEG support in local file and WADO-RS-only use cases (#862)

* fix: 🐛 Local file: failing when retrieving segmentation data

Fix segmentation data retrieval issues for local file. Changed from
fecthing to use cornerstone loadAndCache method

BREAKING CHANGE: DICOM Seg

Closes: part of #838

* Switch SEG retrieval to WADO-RS

* Forgot a debugger

* refactor: 💡 Code refactor. Minor changes into methods

* fix: 🐛 Load local files: PDF

Items:
1. FileLoaderService: used for serveral operations on local files(load it,  get list of studies, group them, accepting dicom and pdf)
2. DicomLoaderService: used for loading dicom based on dataset and studies. Depending on type of dicom loader might change. WIP
3. Refactor PDF and handleSegmentationStorage to use DicomLoaderService

* fix: 🐛 Code review

* fix: 🐛 Code review. Changed:Folder organization and dicom file

Move fileLoaderService and others to a specific folder. When loading
dicom file change to only retrieve the file(not use cornerstone to cache
or anything else).

* fix: 🐛 Code review. Move dicomLoaderService to core

Moved dicomLoaderService to ohif/core and localFileLoaders to a specific
folder.

* fix: 🐛 Code review

Simplified method to get study for dicom file. Added error handling on
file loading. DicomLoaderService to be exposed on ohif/core/utils
instead.

* fix: 🐛 Reduce local load to one method only

Reduced local file load to one method only

* fix: 🐛 HTML to use dicomLoaderService. Prefer wadors than (uri)

* fix: 🐛 Code implementation for multiframe files

* fix: 🐛 Code review. Default local loader to dicom

Closes: 838

* fix: 🐛 Code review. Use relative path to require DICOMWeb

Closes: 838

* fix: 🐛 Code review. Fix unit test. Added DicomLoaderService mod

Closes: 838

* fix: 🐛 Code review. Add 'Seg' on left thumb

When getting/creating dataset get modality for file/image read

Closes: 838
This commit is contained in:
ladeirarodolfo authored and Erik Ziegler committed 2019-09-27 13:47:08 +02:00
1 parent e66c27affb
commit e7e1a8a6cd
16 files changed
+629 -272

No files matched your search

@@ -0,0 +1,192 @@
import * as dcmjs from 'dcmjs';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader';
const DICOMFileLoader = new (class extends FileLoader {
fileType = 'application/dicom';
loadFile(file, imageId) {
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
}
getDataset(image, imageId) {
let dataset = {};
try {
const dicomData = dcmjs.data.DicomMessage.readFile(image);
dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
dicomData.dict
);
dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(
dicomData.meta
);
} catch (e) {
console.log('Error on getting dicom file dataset. It defaults to empty');
}
// Set imageId on dataset to be consumed later on
dataset.imageId = imageId;
return dataset;
}
getStudies(dataset, imageId) {
return this.getStudyFromDataset(dataset);
}
getStudyFromDataset(dataset = {}) {
const {
StudyInstanceUID,
StudyDate,
StudyTime,
AccessionNumber,
ReferringPhysicianName,
PatientName,
PatientID,
PatientBirthDate,
PatientSex,
StudyID,
StudyDescription,
/*
NumberOfStudyRelatedSeries,
NumberOfStudyRelatedInstances,
Modality,
ModalitiesInStudy,
*/
SeriesInstanceUID,
SeriesDescription,
SeriesNumber,
SOPInstanceUID,
SOPClassUID,
Rows,
Columns,
NumberOfFrames,
InstanceNumber,
imageId,
Modality,
/*ImageType,
InstanceNumber,
ImagePositionPatient,
ImageOrientationPatient,
FrameOfReferenceUID,
SliceLocation,
SamplesPerPixel,
PhotometricInterpretation,
PlanarConfiguration,
PixelSpacing,
PixelAspectRatio,
BitsAllocated,
BitsStored,
HighBit,
PixelRepresentation,
SmallestPixelValue,
LargestPixelValue,
WindowCenter,
WindowWidth,
RescaleIntercept,
RescaleSlope,
RescaleType,
Laterality,
ViewPosition,
AcquisitionDateTime,
FrameIncrementPointer,
FrameTime,
FrameTimeVector,
SliceThickness,
SpacingBetweenSlices,
LossyImageCompression,
DerivationDescription,
LossyImageCompressionRatio,
LossyImageCompressionMethod,
EchoNumber,
ContrastBolusAgent,
*/
} = dataset;
const instance = {
sopInstanceUid: SOPInstanceUID,
sopClassUid: SOPClassUID,
rows: Rows,
columns: Columns,
numberOfFrames: NumberOfFrames,
instanceNumber: InstanceNumber,
url: imageId,
modality: Modality,
/*
TODO: in case necessary to uncoment this block, double check every property
imageType: ImageType || DICOMWeb.getString(dataset['00080008']),
instanceNumber: InstanceNumber || DICOMWeb.getNumber(dataset['00200013']),
imagePositionPatient: ImagePositionPatient || DICOMWeb.getString(dataset['00200032']),
imageOrientationPatient: ImageOrientationPatient || DICOMWeb.getString(dataset['00200037']),
frameOfReferenceUID: FrameOfReferenceUID || DICOMWeb.getString(dataset['00200052']),
sliceLocation: SliceLocation || DICOMWeb.getNumber(dataset['00201041']),
samplesPerPixel: SamplesPerPixel || DICOMWeb.getNumber(dataset['00280002']),
photometricInterpretation: PhotometricInterpretation || DICOMWeb.getString(dataset['00280004']),
planarConfiguration: PlanarConfiguration || DICOMWeb.getNumber(dataset['00280006']),
pixelSpacing: PixelSpacing || DICOMWeb.getString(dataset['00280030']),
pixelAspectRatio: PixelAspectRatio || DICOMWeb.getString(dataset['00280034']),
bitsAllocated: BitsAllocated || DICOMWeb.getNumber(dataset['00280100']),
bitsStored: BitsStored || DICOMWeb.getNumber(dataset['00280101']),
highBit: HighBit || DICOMWeb.getNumber(dataset['00280102']),
pixelRepresentation: PixelRepresentation || DICOMWeb.getNumber(dataset['00280103']),
smallestPixelValue: SmallestPixelValue || DICOMWeb.getNumber(dataset['00280106']),
largestPixelValue: LargestPixelValue || DICOMWeb.getNumber(dataset['00280107']),
windowCenter: WindowCenter || DICOMWeb.getString(dataset['00281050']),
windowWidth: WindowWidth || DICOMWeb.getString(dataset['00281051']),
rescaleIntercept: RescaleIntercept || DICOMWeb.getNumber(dataset['00281052']),
rescaleSlope: RescaleSlope || DICOMWeb.getNumber(dataset['00281053']),
rescaleType: RescaleType || DICOMWeb.getNumber(dataset['00281054']),
sourceImageInstanceUid: getSourceImageInstanceUid(dataset),
laterality: Laterality || DICOMWeb.getString(dataset['00200062']),
viewPosition: ViewPosition || DICOMWeb.getString(dataset['00185101']),
acquisitionDateTime: AcquisitionDateTime || DICOMWeb.getString(dataset['0008002A']),
frameIncrementPointer: FrameIncrementPointer || getFrameIncrementPointer(dataset['00280009']),
frameTime: FrameTime || DICOMWeb.getNumber(dataset['00181063']),
frameTimeVector: FrameTimeVector || parseFloatArray(
DICOMWeb.getString(dataset['00181065'])
),
sliceThickness: SliceThickness || DICOMWeb.getNumber(dataset['00180050']),
spacingBetweenSlices: SpacingBetweenSlices || DICOMWeb.getString(dataset['00180088']),
lossyImageCompression: LossyImageCompression || DICOMWeb.getString(dataset['00282110']),
derivationDescription: DerivationDescription || DICOMWeb.getString(dataset['00282111']),
lossyImageCompressionRatio: LossyImageCompressionRatio || DICOMWeb.getString(dataset['00282112']),
lossyImageCompressionMethod: LossyImageCompressionMethod || DICOMWeb.getString(dataset['00282114']),
echoNumber: EchoNumber || DICOMWeb.getString(dataset['00180086']),
contrastBolusAgent: ContrastBolusAgent || DICOMWeb.getString(dataset['00180010']),
radiopharmaceuticalInfo: getRadiopharmaceuticalInfo(dataset),
wadouri: WADOProxy.convertURL(wadouri, server),
wadorsuri: WADOProxy.convertURL(wadorsuri, server),*/
};
const series = {
seriesInstanceUid: SeriesInstanceUID,
seriesDescription: SeriesDescription,
seriesNumber: SeriesNumber,
instances: [instance],
};
const study = {
studyInstanceUid: StudyInstanceUID,
studyDate: StudyDate,
studyTime: StudyTime,
accessionNumber: AccessionNumber,
referringPhysicianName: ReferringPhysicianName,
patientName: PatientName,
patientId: PatientID,
patientBirthdate: PatientBirthDate,
patientSex: PatientSex,
studyId: StudyID,
studyDescription: StudyDescription,
/*
TODO: in case necessary to uncomment this block, double check every property
numberOfStudyRelatedSeries: NumberOfStudyRelatedSeries || DICOMWeb.getString(dataset['00201206']),
numberOfStudyRelatedInstances: NumberOfStudyRelatedInstances || DICOMWeb.getString(dataset['00201208']),
modality: Modality || DICOMWeb.getString(dataset['00080060']),
modalitiesInStudy: ModalitiesInStudy || DICOMWeb.getString(dataset['00080061']),
modalities:
*/
seriesList: [series],
};
return study;
}
})();
export default DICOMFileLoader;
@@ -0,0 +1,6 @@
export default class FileLoader {
fileType;
loadFile(file, imageId) { }
getDataset(image, imageId) { }
getStudies(dataset, imageId) { }
}
@@ -0,0 +1,82 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader';
import PDFFileLoader from './pdfFileLoader';
import DICOMFileLoader from './dicomFileLoader';
class FileLoaderService extends FileLoader {
fileType;
loader;
constructor(file) {
super();
const fileType = file && file.type;
this.loader = this.getLoader(fileType);
this.fileType = this.loader.fileType;
}
static groupSeries(studies) {
const groupBy = (list, groupByKey, listKey) => {
let nonKeyCounter = 1;
return list.reduce((acc, obj) => {
let key = obj[groupByKey];
const list = obj[listKey];
// in case key not found, group it using counter
key = !!key ? key : '' + nonKeyCounter++;
if (!acc[key]) {
acc[key] = { ...obj };
acc[key][listKey] = [];
}
acc[key][listKey].push(...list);
return acc;
}, {});
};
const studiesGrouped = Object.values(
groupBy(studies, 'studyInstanceUid', 'seriesList')
);
const result = studiesGrouped.map(studyGroup => {
const seriesGrouped = groupBy(
studyGroup.seriesList,
'seriesInstanceUid',
'instances'
);
studyGroup.seriesList = Object.values(seriesGrouped);
return studyGroup;
});
return result;
}
addFile(file) {
return cornerstoneWADOImageLoader.wadouri.fileManager.add(file);
}
loadFile(file, imageId) {
return this.loader.loadFile(file, imageId);
}
getDataset(image, imageId) {
return this.loader.getDataset(image, imageId);
}
getStudies(dataset, imageId) {
return this.loader.getStudies(dataset, imageId);
}
getLoader(fileType) {
if (fileType === 'application/pdf') {
return PDFFileLoader;
} else {
// Default to dicom loader
return DICOMFileLoader;
}
}
}
export default FileLoaderService;
@@ -0,0 +1,58 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader';
const PDFFileLoader = new (class extends FileLoader {
fileType = 'application/pdf';
loadFile(file, imageId) {
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
}
getDataset(image, imageId) {
const dataset = {};
dataset.imageId = image.imageId || imageId;
return dataset;
}
getStudies(dataset, imageId) {
return this.getDefaultStudy(imageId);
}
getDefaultStudy(imageId) {
const study = {
studyInstanceUid: '',
studyDate: '',
studyTime: '',
accessionNumber: '',
referringPhysicianName: '',
patientName: '',
patientId: '',
patientBirthdate: '',
patientSex: '',
studyId: '',
studyDescription: '',
seriesList: [
{
seriesInstanceUid: '',
seriesDescription: '',
seriesNumber: '',
instances: [
{
sopInstanceUid: '',
sopClassUid: '1.2.840.10008.5.1.4.1.1.104.1',
rows: '',
columns: '',
numberOfFrames: 0,
instanceNumber: 1,
getImageId: () => imageId,
isLocalFile: true,
},
],
},
],
};
return study;
}
})();
export default PDFFileLoader;