Instance metadata/metadata providers overhaul (#1481)
* Instance metadata plus metadata provider overhaul. Fix consumption of wado-uri urls fallbacks + datatype agnosticism. WIP DICOMify things. fix various issues with naturalized variable naming migration. Remove metadata provider. Fix consumption of multiframe images and addition of CWIL metadata. Fix strange build issues. Fix CWIL style windowWidth to array from naturalized DICOM. Fix PT, CT, CR and DX issues for cornerstone + DX issues for vtkjs. Move color palette fetching down to the natuaralized JSON level. Remove unused StudyMetadataSummary Remove redundant dicom metadata dictionary. Working local + json routes. Fix SR read. Finished first round of testing + cleaned up debugging etc. * data => metadata for instance naturalizedJSON * Update dcmjs version * Correct github isssues. * Fix erroneously replaced files. * Danny's recommended changes. * Instance metadata plus metadata provider overhaul. Fix consumption of wado-uri urls fallbacks + datatype agnosticism. WIP DICOMify things. fix various issues with naturalized variable naming migration. Remove metadata provider. Fix consumption of multiframe images and addition of CWIL metadata. Fix strange build issues. Fix CWIL style windowWidth to array from naturalized DICOM. Fix PT, CT, CR and DX issues for cornerstone + DX issues for vtkjs. Move color palette fetching down to the natuaralized JSON level. Remove unused StudyMetadataSummary Remove redundant dicom metadata dictionary. Working local + json routes. Fix SR read. Finished first round of testing + cleaned up debugging etc. * data => metadata for instance naturalizedJSON * Update dcmjs version * Correct github isssues. * Fix erroneously replaced files. * Danny's recommended changes. * Update JSON CI * Update casing of import. * Fix jump for SR. * Fix unit tests for measurements service * Fix json CI test. * fix: update yarn lock * Fix local non-encapsulated pdf view * CI updated to new sucess message. Co-authored-by: Danny <danny.ri.brown@gmail.com>
This commit is contained in:
1 parent
8833731ad3
commit
1d85f7a74e
142 files changed
+5322
-8570
No files matched your search
@@ -1,21 +1,25 @@
|
||||
/**
|
||||
* Checks if a series is reconstructable to a 3D volume.
|
||||
*
|
||||
* @param {Object} series The `OHIFSeriesMetadata` object.
|
||||
* @param {Object[]} instances The `OHIFInstanceMetadata` object
|
||||
* @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
|
||||
*/
|
||||
export default function isDisplaySetReconstructable(series, instances) {
|
||||
// Can't reconstruct if we only have one image.
|
||||
|
||||
const modality = series._data.modality; // TODO -> Is there a better way to get this?
|
||||
const isMultiframe = instances[0].getRawValue('x00280008') > 1;
|
||||
|
||||
if (!constructableModalities.includes(modality)) {
|
||||
export default function isDisplaySetReconstructable(instances) {
|
||||
if (!instances.length) {
|
||||
return { value: false };
|
||||
}
|
||||
|
||||
const firstInstance = instances[0].getData().metadata;
|
||||
|
||||
const Modality = firstInstance.Modality;
|
||||
const isMultiframe = firstInstance.NumberOfFrames > 1;
|
||||
|
||||
if (!constructableModalities.includes(Modality)) {
|
||||
return { value: false };
|
||||
}
|
||||
|
||||
// Can't reconstruct if we only have one image.
|
||||
if (!isMultiframe && instances.length === 1) {
|
||||
return { values: false };
|
||||
return { value: false };
|
||||
}
|
||||
|
||||
if (isMultiframe) {
|
||||
@@ -26,34 +30,36 @@ export default function isDisplaySetReconstructable(series, instances) {
|
||||
}
|
||||
|
||||
function processMultiframe(instance) {
|
||||
//TODO: deal with multriframe checks! return true for now.
|
||||
return { value: true };
|
||||
//TODO: deal with multriframe checks! return false for now as can't reconstruct.
|
||||
return { value: false };
|
||||
}
|
||||
|
||||
function processSingleframe(instances) {
|
||||
const firstImage = instances[0];
|
||||
const firstImageRows = firstImage.getTagValue('x00280010');
|
||||
const firstImageColumns = firstImage.getTagValue('x00280011');
|
||||
const firstImageSamplesPerPixel = firstImage.getTagValue('x00280002');
|
||||
// Note: No need to unpack iop, can compare string form.
|
||||
const firstImageOrientationPatient = firstImage.getTagValue('x00200037');
|
||||
const firstImage = instances[0].getData().metadata;
|
||||
const firstImageRows = firstImage.Rows;
|
||||
const firstImageColumns = firstImage.Columns;
|
||||
const firstImageSamplesPerPixel = firstImage.SamplesPerPixel;
|
||||
const firstImageOrientationPatient = firstImage.ImageOrientationPatient;
|
||||
const firstImagePositionPatient = firstImage.ImagePositionPatient;
|
||||
|
||||
// Can't reconstruct if we:
|
||||
// -- Have a different dimensions within a displaySet.
|
||||
// -- Have a different number of components within a displaySet.
|
||||
// -- Have different orientations within a displaySet.
|
||||
for (let i = 1; i < instances.length; i++) {
|
||||
const instance = instances[i];
|
||||
const rows = instance.getTagValue('x00280010');
|
||||
const columns = instance.getTagValue('x00280011');
|
||||
const samplesPerPixel = instance.getTagValue('x00280002');
|
||||
const imageOrientationPatient = instance.getTagValue('x00200037');
|
||||
const instance = instances[i].getData().metadata;
|
||||
const {
|
||||
Rows,
|
||||
Columns,
|
||||
SamplesPerPixel,
|
||||
ImageOrientationPatient,
|
||||
} = instance;
|
||||
|
||||
if (
|
||||
rows !== firstImageRows ||
|
||||
columns !== firstImageColumns ||
|
||||
samplesPerPixel !== firstImageSamplesPerPixel ||
|
||||
imageOrientationPatient !== firstImageOrientationPatient
|
||||
Rows !== firstImageRows ||
|
||||
Columns !== firstImageColumns ||
|
||||
SamplesPerPixel !== firstImageSamplesPerPixel ||
|
||||
!_isSameOrientation(ImageOrientationPatient, firstImageOrientationPatient)
|
||||
) {
|
||||
return { value: false };
|
||||
}
|
||||
@@ -61,28 +67,32 @@ function processSingleframe(instances) {
|
||||
|
||||
let missingFrames = 0;
|
||||
|
||||
// Check if frame spacing is approximately equal within a tolerance.
|
||||
// Check if frame spacing is approximately equal within a spacingTolerance.
|
||||
// If spacing is on a uniform grid but we are missing frames,
|
||||
// Allow reconstruction, but pass back the number of missing frames.
|
||||
if (instances.length > 2) {
|
||||
const firstIpp = _getImagePositionPatient(firstImage);
|
||||
const lastIpp = _getImagePositionPatient(instances[instances.length - 1]);
|
||||
const lastIpp = instances[instances.length - 1].getData().metadata
|
||||
.ImagePositionPatient;
|
||||
|
||||
// We can't reconstruct if we are missing imagePositionPatient values
|
||||
if (!firstIpp || !lastIpp) {
|
||||
// We can't reconstruct if we are missing ImagePositionPatient values
|
||||
if (!firstImagePositionPatient || !lastIpp) {
|
||||
return { value: false };
|
||||
}
|
||||
|
||||
const averageSpacingBetweenFrames =
|
||||
_getPerpendicularDistance(firstIpp, lastIpp) / (instances.length - 1);
|
||||
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
|
||||
(instances.length - 1);
|
||||
|
||||
let previousIpp = firstIpp;
|
||||
let previousImagePositionPatient = firstImagePositionPatient;
|
||||
|
||||
for (let i = 1; i < instances.length; i++) {
|
||||
const instance = instances[i];
|
||||
const ipp = _getImagePositionPatient(instance);
|
||||
const instance = instances[i].getData().metadata;
|
||||
const { ImagePositionPatient } = instance;
|
||||
|
||||
const spacingBetweenFrames = _getPerpendicularDistance(ipp, previousIpp);
|
||||
const spacingBetweenFrames = _getPerpendicularDistance(
|
||||
ImagePositionPatient,
|
||||
previousImagePositionPatient
|
||||
);
|
||||
const spacingIssue = _getSpacingIssue(
|
||||
spacingBetweenFrames,
|
||||
averageSpacingBetweenFrames
|
||||
@@ -98,15 +108,28 @@ function processSingleframe(instances) {
|
||||
}
|
||||
}
|
||||
|
||||
previousIpp = ipp;
|
||||
previousImagePositionPatient = ImagePositionPatient;
|
||||
}
|
||||
}
|
||||
|
||||
return { value: true, missingFrames };
|
||||
}
|
||||
|
||||
// TODO: Is 10% a reasonable tolerance for spacing?
|
||||
const tolerance = 0.1;
|
||||
function _isSameOrientation(iop1, iop2) {
|
||||
if (iop1 === undefined || !iop2 === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
Math.abs(iop1[0] - iop2[0]) < iopTolerance &&
|
||||
Math.abs(iop1[1] - iop2[1]) < iopTolerance &&
|
||||
Math.abs(iop1[2] - iop2[2]) < iopTolerance
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Is 10% a reasonable spacingTolerance for spacing?
|
||||
const spacingTolerance = 0.1;
|
||||
const iopTolerance = 0.01;
|
||||
|
||||
/**
|
||||
* Checks for spacing issues.
|
||||
@@ -118,7 +141,7 @@ const tolerance = 0.1;
|
||||
*/
|
||||
function _getSpacingIssue(spacing, averageSpacing) {
|
||||
const equalWithinTolerance =
|
||||
Math.abs(spacing - averageSpacing) < averageSpacing * tolerance;
|
||||
Math.abs(spacing - averageSpacing) < averageSpacing * spacingTolerance;
|
||||
|
||||
if (equalWithinTolerance) {
|
||||
return;
|
||||
@@ -131,7 +154,7 @@ function _getSpacingIssue(spacing, averageSpacing) {
|
||||
const errorForEachSpacing =
|
||||
Math.abs(spacing - numberOfSpacings * averageSpacing) / numberOfSpacings;
|
||||
|
||||
if (errorForEachSpacing < tolerance * averageSpacing) {
|
||||
if (errorForEachSpacing < spacingTolerance * averageSpacing) {
|
||||
return {
|
||||
issue: reconstructionIssues.MISSING_FRAMES,
|
||||
missingFrames: numberOfSpacings - 1,
|
||||
@@ -141,18 +164,6 @@ function _getSpacingIssue(spacing, averageSpacing) {
|
||||
return { issue: reconstructionIssues.IRREGULAR_SPACING };
|
||||
}
|
||||
|
||||
function _getImagePositionPatient(instance) {
|
||||
const tagValue = instance
|
||||
.getTagValue('x00200032');
|
||||
if (!tagValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
return tagValue
|
||||
.split('\\')
|
||||
.map(element => Number(element));
|
||||
}
|
||||
|
||||
function _getPerpendicularDistance(a, b) {
|
||||
return Math.sqrt(
|
||||
Math.pow(a[0] - b[0], 2) +
|
||||
|
||||
Reference in new issue
Block a user