import {custom} from '../app/custom.js';
import {
safeGet,
safeGetAll
} from '../dicom/dataElement.js';
import {
NormalisedManufacturers,
getNormalisedManufacturer
} from './dicomManufacturer.js';
// doc imports
/* eslint-disable no-unused-vars */
import {DataElement} from '../dicom/dataElement.js';
/* eslint-enable no-unused-vars */
/**
* Related DICOM tag keys.
*/
const TagKeys = {
SOPClassUID: '00080016',
SOPInstanceUID: '00080018',
Manufacturer: '00080070',
AcquisitionTime: '00080032',
DimensionIndexSequence: '00209222',
DimensionIndexPointer: '00209165',
SharedFunctionalGroupsSequence: '52009229',
PerFrameFunctionalGroupsSequence: '52009230',
MRDiffusionSequence: '00189117',
DiffusionBValue: '00189087',
DiffusionBValueAT: '(0018,9087)',
FrameContentSequence: '00209111',
DimensionIndexValues: '00209157'
};
/**
* Private b-value tag rules. Tested in order.
* Rules are either `{manufacturer, key}` or `{uidPrefix, key}`.
*
* @type {object[]}
*/
const LocalBValueRules = [
{
manufacturer: NormalisedManufacturers.SIEMENS,
key: '0019100C'
},
{
manufacturer: NormalisedManufacturers.GE,
key: '00431039'
},
{
manufacturer: NormalisedManufacturers.HITASHI,
key: '00291030'
}
];
/**
* Related SOP class UIDs.
*/
const SOPClassUIDs = {
MR: '1.2.840.10008.5.1.4.1.1.4',
EnhancedMR: '1.2.840.10008.5.1.4.1.1.4.1',
MRSpectroscopy: '1.2.840.10008.5.1.4.1.1.4.2',
EnhancedMRColorImage: '1.2.840.10008.5.1.4.1.1.4.3'
};
/**
* Get the diffusion b-value from a functional DICOM sequence.
*
* @param {Object<string, DataElement>} elements The DICOM tags.
* @returns {string|undefined} The value, if present.
*/
function getDiffusionBValueFromFunctionalSeq(elements) {
let res;
const diffSeq = safeGetAll(elements, TagKeys.MRDiffusionSequence);
if (typeof diffSeq !== 'undefined') {
// should only contain one item
res = safeGet(diffSeq[0], TagKeys.DiffusionBValue);
}
return res;
}
/**
* Get the diffusion b-value from standard DICOM tag.
*
* @param {Object<string, DataElement>} elements The DICOM tags.
* @returns {string|undefined} The value, if present.
*/
function getStandardDiffusionBValueFromEMR(elements) {
let res;
// from Shared Functional Groups Sequence
const sharedGroupSeq =
safeGetAll(elements, TagKeys.SharedFunctionalGroupsSequence);
if (typeof sharedGroupSeq !== 'undefined') {
// should only contain one item
res = getDiffusionBValueFromFunctionalSeq(sharedGroupSeq[0]);
}
// from Per Frame Functional Groups Sequence
if (typeof res === 'undefined') {
const perFrameGroupSeq =
safeGetAll(elements, TagKeys.PerFrameFunctionalGroupsSequence);
if (typeof perFrameGroupSeq !== 'undefined') {
// TODO: go to specific frame number?
for (const group of perFrameGroupSeq) {
res = getDiffusionBValueFromFunctionalSeq(group);
if (typeof res !== 'undefined') {
break;
}
}
}
}
return res;
}
/**
* Get the diffusion b-value from MR from non standard
* and/or private DICOM tags.
*
* @param {Object<string, DataElement>} elements The DICOM tags.
* @returns {string|undefined} The value, if present.
*/
function getNonStandardDiffusionBValueFromMR(elements) {
let res;
// manufacturers private tag
if (typeof res === 'undefined') {
const manufacturer = getNormalisedManufacturer(elements);
const sopInstanceUID = safeGet(elements, TagKeys.SOPInstanceUID);
let rules;
if (typeof custom.privateBValueRules !== 'undefined') {
rules = custom.privateBValueRules;
} else {
rules = LocalBValueRules;
}
for (const rule of rules) {
if (typeof rule.uidPrefix !== 'undefined' &&
typeof sopInstanceUID !== 'undefined' &&
sopInstanceUID.startsWith(rule.uidPrefix)) {
res = safeGet(elements, rule.key);
} else if (typeof rule.manufacturer !== 'undefined' &&
typeof manufacturer !== 'undefined' &&
rule.manufacturer === manufacturer) {
res = safeGet(elements, rule.key);
}
// break at first valid result
if (typeof res !== 'undefined' &&
!isNaN(parseInt(res, 10))) {
// TODO just break or exit function?
break;
}
}
}
// b-value at root level
if (typeof res === 'undefined') {
res = safeGet(elements, TagKeys.DiffusionBValue);
}
return res;
}
/**
* Get the b-value from the frame content sequence if
* the dimension index sequence has a pointer to the b-value.
*
* @param {Object<string, DataElement>} elements The DICOM tags.
* @returns {string|undefined} The value, if present.
*/
function getDiffusionBValueFromFrameContent(elements) {
let res;
// check if the dim pointer is for b-value
let gotBValuePointer = false;
const indexSeq = safeGetAll(elements, TagKeys.DimensionIndexSequence);
if (typeof indexSeq !== 'undefined') {
for (const dimIndex of indexSeq) {
const pointer = safeGet(dimIndex, TagKeys.DimensionIndexPointer);
if (typeof pointer !== 'undefined' &&
pointer === TagKeys.DiffusionBValueAT
) {
gotBValuePointer = true;
break;
}
}
}
// get from per frame functional group
if (gotBValuePointer) {
const perFrameGroupSeq =
safeGetAll(elements, TagKeys.PerFrameFunctionalGroupsSequence);
if (typeof perFrameGroupSeq !== 'undefined') {
const group = perFrameGroupSeq[0];
const frameContentSeq = safeGetAll(group, TagKeys.FrameContentSequence);
if (typeof frameContentSeq !== 'undefined') {
// should be only one
const dimValues =
safeGetAll(frameContentSeq[0], TagKeys.DimensionIndexValues);
if (typeof dimValues !== 'undefined') {
// does not follow order set in DimensionIndexSequence...
res = dimValues[2];
}
}
}
}
return res;
}
/**
* Get the diffusion b-value from Enhanced MR from non standard
* and/or private DICOM tags.
*
* @param {Object<string, DataElement>} elements The DICOM tags.
* @returns {string|undefined} The value, if present.
*/
function getNonStandardDiffusionBValueFromEMR(elements) {
let res;
// manufacturer
const manufacturer = getNormalisedManufacturer(elements);
// philips can use frame content
if (typeof manufacturer !== 'undefined' &&
manufacturer === NormalisedManufacturers.PHILIPS) {
res = getDiffusionBValueFromFrameContent(elements);
}
return res;
}
/**
* Get the diffusion b-value.
*
* @param {Object<string, DataElement>} elements The DICOM tags.
* @returns {number|undefined} The value, if present.
*/
function getDiffusionBValue(elements) {
let res;
// SOP class UID
const sopClassUID = safeGet(elements, TagKeys.SOPClassUID);
if (sopClassUID === SOPClassUIDs.EnhancedMR ||
sopClassUID === SOPClassUIDs.MRSpectroscopy ||
sopClassUID === SOPClassUIDs.EnhancedMRColorImage
) {
// standard tag
res = getStandardDiffusionBValueFromEMR(elements);
// if not found, check non standard tag
if (typeof res === 'undefined') {
res = getNonStandardDiffusionBValueFromEMR(elements);
if (typeof res !== 'undefined') {
console.log('got b-value for enhanced MR from non standard tag');
}
}
} else if (sopClassUID === SOPClassUIDs.MR) {
// non standard for MR
res = getNonStandardDiffusionBValueFromMR(elements);
// if not found, check standard EMR tag
if (typeof res === 'undefined') {
if (typeof res === 'undefined') {
res = getStandardDiffusionBValueFromEMR(elements);
}
}
}
// cast to int
if (typeof res !== 'undefined') {
res = parseInt(res, 10);
}
return res;
}
/**
* Get the tag time value for MR images.
*
* @param {Object<string, DataElement>} elements The DICOM tags.
* @returns {number|undefined} The value, if present.
*/
function getMRVolumeIdTagValue(elements) {
let res;
// filter by SOP class UID
const sopClassUID = safeGet(elements, TagKeys.SOPClassUID);
if (sopClassUID === SOPClassUIDs.MR ||
sopClassUID === SOPClassUIDs.EnhancedMR ||
sopClassUID === SOPClassUIDs.MRSpectroscopy ||
sopClassUID === SOPClassUIDs.EnhancedMRColorImage
) {
// diffusion b-value
const bvalue = getDiffusionBValue(elements);
if (typeof bvalue !== 'undefined') {
res = bvalue;
}
}
return res;
}
/**
* Get the volume id from a list of tags. Default
* returns MR diffusion b-value.
*
* @param {Object<string, DataElement>} elements The DICOM elements.
* @returns {number|undefined} The id value if available.
*/
export function getVolumeIdTagValue(elements) {
let res;
if (typeof custom.getVolumeIdTagValue !== 'undefined') {
res = custom.getVolumeIdTagValue(elements);
} else {
// MR (and enhanced MR) volume id
const volumeId = getMRVolumeIdTagValue(elements);
if (typeof volumeId !== 'undefined') {
res = volumeId;
}
}
return res;
}
/**
* Get the volume id from a list of tags parsed after the load finishes.
* Default uses acquisition time.
*
* @param {Object<string, DataElement>} elements The DICOM elements.
* @returns {number|undefined} The id value if available.
*/
export function getPostLoadVolumeIdTagValue(elements) {
let res;
if (typeof custom.getPostLoadVolumeIdTagValue !== 'undefined') {
res = custom.getPostLoadVolumeIdTagValue(elements);
} else {
// constant acquisition time for volumes
const acqTime = safeGet(elements, TagKeys.AcquisitionTime);
if (typeof acqTime !== 'undefined') {
res = parseFloat(acqTime);
}
}
return res;
}