344 lines
10 KiB
JavaScript
344 lines
10 KiB
JavaScript
import getNumberValues from "./getNumberValues";
|
||
import isNMReconstructable from "./isNMReconstructable";
|
||
|
||
/**
|
||
* Get a subpart of Image Type dicom tag defined by index
|
||
* @param {*} dataSet
|
||
* @param {*} index 0 based index of the subtype
|
||
*/
|
||
function getImageTypeSubItemFromDataset(dataSet, index) {
|
||
const imageType = dataSet.string("x00080008");
|
||
|
||
if (imageType) {
|
||
const subTypes = imageType.split("\\");
|
||
|
||
if (subTypes.length > index) {
|
||
return subTypes[index];
|
||
}
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
// DICOM 标准中针对 Enhanced CT/MR/PET 等“增强型多帧”(Enhanced Multi-frame)影像, 强制要求为每一帧显式记录真实的独立空间坐标 。
|
||
|
||
// - 它们的数据通常存放在: Per-frame Functional Groups Sequence (5200,9230) -> 对应的 Frame Item -> Plane Position Sequence (0020,9113) -> ImagePositionPatient (0020,0032) 。
|
||
function getSharedFunctionalGroupsDataSet(dataSet) {
|
||
const seq = dataSet?.elements?.x52009229;
|
||
if (!seq?.items?.length) {
|
||
return;
|
||
}
|
||
return seq.items[0].dataSet;
|
||
}
|
||
|
||
function getPerFrameFunctionalGroupsDataSet(dataSet, frameIndex) {
|
||
const seq = dataSet?.elements?.x52009230;
|
||
if (!seq?.items?.length) {
|
||
return;
|
||
}
|
||
if (
|
||
!Number.isInteger(frameIndex) ||
|
||
frameIndex < 0 ||
|
||
frameIndex >= seq.items.length
|
||
) {
|
||
return;
|
||
}
|
||
return seq.items[frameIndex].dataSet;
|
||
}
|
||
|
||
function getOrientationFromPlaneOrientationSequence(dataSet) {
|
||
const seq = dataSet?.elements?.x00209116;
|
||
if (!seq?.items?.length) {
|
||
return;
|
||
}
|
||
return getNumberValues(seq.items[0].dataSet, "x00200037", 6);
|
||
}
|
||
|
||
function getPositionFromPlanePositionSequence(dataSet) {
|
||
const seq = dataSet?.elements?.x00209113;
|
||
if (!seq?.items?.length) {
|
||
return;
|
||
}
|
||
return getNumberValues(seq.items[0].dataSet, "x00200032", 3);
|
||
}
|
||
|
||
function getPixelSpacingFromPixelMeasuresSequence(dataSet) {
|
||
const seq = dataSet?.elements?.x00289110;
|
||
if (!seq?.items?.length) {
|
||
return;
|
||
}
|
||
return getNumberValues(seq.items[0].dataSet, "x00280030", 2);
|
||
}
|
||
|
||
function getSliceThicknessFromPixelMeasuresSequence(dataSet) {
|
||
const seq = dataSet?.elements?.x00289110;
|
||
if (!seq?.items?.length) {
|
||
return;
|
||
}
|
||
if (!seq.items[0]?.dataSet?.elements?.x00180050) {
|
||
return;
|
||
}
|
||
return seq.items[0].dataSet.floatString("x00180050");
|
||
}
|
||
|
||
/**
|
||
* Extracts the orientation from NM multiframe dataset, if image type
|
||
* equal to RECON TOMO or RECON GATED TOMO
|
||
* @param {*} dataSet
|
||
* @returns
|
||
*/
|
||
function extractOrientationFromNMMultiframeDataset(dataSet) {
|
||
let imageOrientationPatient;
|
||
const modality = dataSet.string("x00080060");
|
||
|
||
if (modality?.includes("NM")) {
|
||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
||
|
||
if (imageSubType && isNMReconstructable(imageSubType)) {
|
||
if (dataSet.elements.x00540022) {
|
||
imageOrientationPatient = getNumberValues(
|
||
dataSet.elements.x00540022.items[0].dataSet,
|
||
"x00200037",
|
||
6
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
return imageOrientationPatient;
|
||
}
|
||
|
||
/**
|
||
* Extracts the position from NM multiframe dataset, if image type
|
||
* equal to RECON TOMO or RECON GATED TOMO
|
||
* @param {*} dataSet
|
||
* @returns
|
||
*/
|
||
function extractPositionFromNMMultiframeDataset(dataSet) {
|
||
let imagePositionPatient;
|
||
const modality = dataSet.string("x00080060");
|
||
|
||
if (modality?.includes("NM")) {
|
||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
||
|
||
if (imageSubType && isNMReconstructable(imageSubType)) {
|
||
if (dataSet.elements.x00540022) {
|
||
imagePositionPatient = getNumberValues(
|
||
dataSet.elements.x00540022.items[0].dataSet,
|
||
"x00200032",
|
||
3
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
return imagePositionPatient;
|
||
}
|
||
|
||
function isMultiFrame(dataSet) {
|
||
const numberOfFrames =
|
||
dataSet.string("x00280008") || dataSet.uint16("x00280008");
|
||
return numberOfFrames !== undefined && Number(numberOfFrames) > 1;
|
||
}
|
||
|
||
/**
|
||
* Extract orientation information from a dataset. It tries to get the orientation
|
||
* from the Detector Information Sequence (for NM images) if image type equal
|
||
* to RECON TOMO or RECON GATED TOMO
|
||
* @param {*} dataSet
|
||
* @returns
|
||
*/
|
||
function extractOrientationFromDataset(dataSet) {
|
||
// let imageOrientationPatient;
|
||
|
||
// if (isMultiFrame(dataSet)) {
|
||
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
|
||
// dataSet,
|
||
// frameIndex
|
||
// );
|
||
// imageOrientationPatient =
|
||
// getOrientationFromPlaneOrientationSequence(perFrameDataSet);
|
||
|
||
// if (!imageOrientationPatient) {
|
||
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
|
||
// imageOrientationPatient =
|
||
// getOrientationFromPlaneOrientationSequence(sharedDataSet);
|
||
// }
|
||
// }
|
||
|
||
// if (!imageOrientationPatient) {
|
||
// imageOrientationPatient = getNumberValues(dataSet, "x00200037", 6);
|
||
// }
|
||
let imageOrientationPatient = getNumberValues(dataSet, "x00200037", 6)
|
||
// Trying to get the orientation from the Plane Orientation Sequence
|
||
if (!imageOrientationPatient && dataSet.elements.x00209116) {
|
||
imageOrientationPatient = getNumberValues(
|
||
dataSet.elements.x00209116.items[0].dataSet,
|
||
"x00200037",
|
||
6
|
||
);
|
||
}
|
||
|
||
// If orientation not valid to this point, trying to get the orientation
|
||
// from the Detector Information Sequence (for NM images) with image type
|
||
// equal to RECON TOMO or RECON GATED TOMO
|
||
|
||
if (!imageOrientationPatient) {
|
||
imageOrientationPatient =
|
||
extractOrientationFromNMMultiframeDataset(dataSet);
|
||
}
|
||
|
||
return imageOrientationPatient;
|
||
}
|
||
|
||
/**
|
||
* Extract position information from a dataset. It tries to get the position
|
||
* from the Detector Information Sequence (for NM images) if image type equal
|
||
* to RECON TOMO or RECON GATED TOMO
|
||
* @param {*} dataSet
|
||
* @returns
|
||
*/
|
||
function extractPositionFromDataset(dataSet) {
|
||
// let imagePositionPatient;
|
||
|
||
// if (isMultiFrame(dataSet)) {
|
||
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
|
||
// dataSet,
|
||
// frameIndex
|
||
// );
|
||
// imagePositionPatient =
|
||
// getPositionFromPlanePositionSequence(perFrameDataSet);
|
||
|
||
// if (!imagePositionPatient) {
|
||
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
|
||
// imagePositionPatient =
|
||
// getPositionFromPlanePositionSequence(sharedDataSet);
|
||
// }
|
||
// }
|
||
|
||
// if (!imagePositionPatient) {
|
||
// imagePositionPatient = getNumberValues(dataSet, "x00200032", 3);
|
||
// }
|
||
let imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||
// Trying to get the position from the Plane Position Sequence
|
||
if (!imagePositionPatient && dataSet.elements.x00209113) {
|
||
imagePositionPatient = getNumberValues(
|
||
dataSet.elements.x00209113.items[0].dataSet,
|
||
"x00200032",
|
||
3
|
||
);
|
||
}
|
||
|
||
// If position not valid to this point, trying to get the position
|
||
// from the Detector Information Sequence (for NM images)
|
||
if (!imagePositionPatient) {
|
||
imagePositionPatient = extractPositionFromNMMultiframeDataset(dataSet);
|
||
}
|
||
|
||
return imagePositionPatient;
|
||
}
|
||
|
||
/**
|
||
* Extract the pixelSpacing information. If exists, extracts this information
|
||
* from Pixel Measures Sequence
|
||
* @param {*} dataSet
|
||
* @returns
|
||
*/
|
||
function extractSpacingFromDataset(dataSet) {
|
||
// let pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
||
|
||
// // If pixelSpacing not valid to this point, trying to get the spacing
|
||
// // from the Pixel Measures Sequence
|
||
// if (!pixelSpacing && dataSet.elements.x00289110) {
|
||
// pixelSpacing = getNumberValues(
|
||
// dataSet.elements.x00289110.items[0].dataSet,
|
||
// 'x00280030',
|
||
// 2
|
||
// );
|
||
// }
|
||
|
||
// return pixelSpacing;
|
||
|
||
let rowPixelSpacing = null;
|
||
let columnPixelSpacing = null;
|
||
// let pixelSpacing;
|
||
|
||
// if (isMultiFrame(dataSet)) {
|
||
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
|
||
// dataSet,
|
||
// frameIndex
|
||
// );
|
||
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
|
||
// pixelSpacing =
|
||
// getPixelSpacingFromPixelMeasuresSequence(perFrameDataSet) ||
|
||
// getPixelSpacingFromPixelMeasuresSequence(sharedDataSet);
|
||
// }
|
||
|
||
// if (!pixelSpacing) {
|
||
// pixelSpacing =
|
||
// getNumberValues(dataSet, "x00280030", 2) ||
|
||
// getPixelSpacingFromPixelMeasuresSequence(dataSet);
|
||
// }
|
||
let pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
||
const imagePixelSpacing = getNumberValues(dataSet, "x00181164", 2);
|
||
const estimatedRadiographicMagnificationFactor = getNumberValues(
|
||
dataSet,
|
||
"x00181114",
|
||
2
|
||
);
|
||
if (pixelSpacing) {
|
||
rowPixelSpacing = pixelSpacing[0];
|
||
columnPixelSpacing = pixelSpacing[1];
|
||
} else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) {
|
||
rowPixelSpacing =
|
||
imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
|
||
columnPixelSpacing =
|
||
imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
|
||
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
|
||
rowPixelSpacing = imagePixelSpacing[0];
|
||
columnPixelSpacing = imagePixelSpacing[1];
|
||
}
|
||
return rowPixelSpacing !== null
|
||
? [rowPixelSpacing, columnPixelSpacing]
|
||
: undefined;
|
||
}
|
||
|
||
/**
|
||
* Extract the sliceThickness information. If exists, extracts this information
|
||
* from Pixel Measures Sequence
|
||
* @param {*} dataSet
|
||
* @returns
|
||
*/
|
||
function extractSliceThicknessFromDataset(dataSet) {
|
||
let sliceThickness;
|
||
if (dataSet.elements.x00180050) {
|
||
sliceThickness = dataSet.floatString('x00180050');
|
||
}
|
||
else if (dataSet.elements.x00289110 &&
|
||
dataSet.elements.x00289110.items.length &&
|
||
dataSet.elements.x00289110.items[0].dataSet.elements.x00180050) {
|
||
sliceThickness =
|
||
dataSet.elements.x00289110.items[0].dataSet.floatString('x00180050');
|
||
}
|
||
|
||
// if (sliceThickness === undefined && isMultiFrame(dataSet)) {
|
||
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
|
||
// dataSet,
|
||
// frameIndex
|
||
// );
|
||
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
|
||
// sliceThickness =
|
||
// getSliceThicknessFromPixelMeasuresSequence(perFrameDataSet) ||
|
||
// getSliceThicknessFromPixelMeasuresSequence(sharedDataSet);
|
||
// }
|
||
|
||
return sliceThickness;
|
||
}
|
||
|
||
export {
|
||
getImageTypeSubItemFromDataset,
|
||
extractOrientationFromDataset,
|
||
extractPositionFromDataset,
|
||
extractSpacingFromDataset,
|
||
extractSliceThicknessFromDataset,
|
||
};
|