SPECT多帧图像融合更改
parent
e8f95d09ba
commit
04a0a1ccc3
|
|
@ -0,0 +1,343 @@
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
function getNumberValues(
|
||||||
|
dataSet,
|
||||||
|
tag,
|
||||||
|
minimumLength
|
||||||
|
){
|
||||||
|
const values = [];
|
||||||
|
const valueAsString = dataSet.string(tag);
|
||||||
|
|
||||||
|
if (!valueAsString) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const split = valueAsString.split('\\');
|
||||||
|
|
||||||
|
if (minimumLength && split.length < minimumLength) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < split.length; i++) {
|
||||||
|
values.push(parseFloat(split[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getNumberValues;
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function isNMReconstructable(imageSubType) {
|
||||||
|
return imageSubType === 'RECON TOMO' || imageSubType === 'RECON GATED TOMO';
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
|
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
|
||||||
|
import {
|
||||||
|
getImageTypeSubItemFromDataset,
|
||||||
|
extractOrientationFromDataset,
|
||||||
|
extractPositionFromDataset,
|
||||||
|
extractSpacingFromDataset,
|
||||||
|
extractSliceThicknessFromDataset,
|
||||||
|
} from './extractPositioningFromDataset';
|
||||||
function parseImageId(imageId) {
|
function parseImageId(imageId) {
|
||||||
// build a url by parsing out the url scheme and frame index from the imageId
|
// build a url by parsing out the url scheme and frame index from the imageId
|
||||||
const firstColonIndex = imageId.indexOf(':');
|
const firstColonIndex = imageId.indexOf(':');
|
||||||
|
|
@ -141,6 +148,19 @@ function populatePaletteColorLut(dataSet, imagePixelModule) {
|
||||||
imagePixelModule.bluePaletteColorLookupTableDescriptor
|
imagePixelModule.bluePaletteColorLookupTableDescriptor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSpacingBetweenSlices(dataSet) {
|
||||||
|
if (dataSet?.elements?.x00180088) {
|
||||||
|
return dataSet.floatString('x00180088');
|
||||||
|
}
|
||||||
|
const pixelMeasuresSequence = dataSet?.elements?.x00289110;
|
||||||
|
if (
|
||||||
|
pixelMeasuresSequence?.items?.length &&
|
||||||
|
pixelMeasuresSequence.items[0]?.dataSet?.elements?.x00180088
|
||||||
|
) {
|
||||||
|
return pixelMeasuresSequence.items[0].dataSet.floatString('x00180088');
|
||||||
|
}
|
||||||
|
}
|
||||||
function metaDataProvider(type, imageId) {
|
function metaDataProvider(type, imageId) {
|
||||||
const parsedImageId = parseImageId(imageId);
|
const parsedImageId = parseImageId(imageId);
|
||||||
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(parsedImageId.url);
|
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(parsedImageId.url);
|
||||||
|
|
@ -149,10 +169,47 @@ function metaDataProvider(type, imageId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type === 'imagePlaneModule') {
|
if (type === 'imagePlaneModule') {
|
||||||
const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
// const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
||||||
const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
// const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||||||
const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
// const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
||||||
const imagePixelSpacing = getNumberValues(dataSet, 'x00181164', 2);
|
const imagePixelSpacing = getNumberValues(dataSet, 'x00181164', 2);
|
||||||
|
const frameIndex = parsedImageId.frame !== undefined ? parsedImageId.frame - 1 : undefined;
|
||||||
|
const imageOrientationPatient = extractOrientationFromDataset(dataSet, frameIndex);
|
||||||
|
let imagePositionPatient = extractPositionFromDataset(dataSet, frameIndex);
|
||||||
|
const pixelSpacing = extractSpacingFromDataset(dataSet, frameIndex);
|
||||||
|
const sliceThickness = extractSliceThicknessFromDataset(dataSet, frameIndex);
|
||||||
|
|
||||||
|
const modality = dataSet.string("x00080060");
|
||||||
|
if (modality && modality.includes('NM') && parsedImageId.frame !== undefined && parsedImageId.frame > 1) {
|
||||||
|
const spacingBetweenSlices = getSpacingBetweenSlices(dataSet);
|
||||||
|
const step = spacingBetweenSlices !== undefined ? spacingBetweenSlices : sliceThickness;
|
||||||
|
if (imageOrientationPatient && imagePositionPatient && step !== undefined && frameIndex !== undefined) {
|
||||||
|
const rowCosines = [
|
||||||
|
parseFloat(imageOrientationPatient[0]),
|
||||||
|
parseFloat(imageOrientationPatient[1]),
|
||||||
|
parseFloat(imageOrientationPatient[2]),
|
||||||
|
];
|
||||||
|
const columnCosines = [
|
||||||
|
parseFloat(imageOrientationPatient[3]),
|
||||||
|
parseFloat(imageOrientationPatient[4]),
|
||||||
|
parseFloat(imageOrientationPatient[5]),
|
||||||
|
];
|
||||||
|
const normal = [
|
||||||
|
rowCosines[1] * columnCosines[2] - rowCosines[2] * columnCosines[1],
|
||||||
|
rowCosines[2] * columnCosines[0] - rowCosines[0] * columnCosines[2],
|
||||||
|
rowCosines[0] * columnCosines[1] - rowCosines[1] * columnCosines[0],
|
||||||
|
];
|
||||||
|
|
||||||
|
const offset = frameIndex * parseFloat(step);
|
||||||
|
|
||||||
|
imagePositionPatient = [
|
||||||
|
parseFloat(imagePositionPatient[0]) + normal[0] * offset,
|
||||||
|
parseFloat(imagePositionPatient[1]) + normal[1] * offset,
|
||||||
|
parseFloat(imagePositionPatient[2]) + normal[2] * offset,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const estimatedRadiographicMagnificationFactor = getNumberValues(dataSet, 'x00181114', 2);
|
const estimatedRadiographicMagnificationFactor = getNumberValues(dataSet, 'x00181114', 2);
|
||||||
let columnPixelSpacing = null;
|
let columnPixelSpacing = null;
|
||||||
|
|
||||||
|
|
@ -194,7 +251,7 @@ function metaDataProvider(type, imageId) {
|
||||||
rowCosines,
|
rowCosines,
|
||||||
columnCosines,
|
columnCosines,
|
||||||
imagePositionPatient,
|
imagePositionPatient,
|
||||||
sliceThickness: dataSet.floatString('x00180050'),
|
sliceThickness,
|
||||||
sliceLocation: dataSet.floatString('x00201041'),
|
sliceLocation: dataSet.floatString('x00201041'),
|
||||||
pixelSpacing,
|
pixelSpacing,
|
||||||
rowPixelSpacing,
|
rowPixelSpacing,
|
||||||
|
|
@ -224,6 +281,11 @@ function metaDataProvider(type, imageId) {
|
||||||
}
|
}
|
||||||
return imagePixelModule;
|
return imagePixelModule;
|
||||||
}
|
}
|
||||||
|
// if (type === "multiframeModule") {
|
||||||
|
// return {
|
||||||
|
// NumberOfFrames: dataSet.uint16("x00280008") || dataSet.string("x00280008")
|
||||||
|
// };
|
||||||
|
// }
|
||||||
// if (type === 'imagePixelModule') {
|
// if (type === 'imagePixelModule') {
|
||||||
// return {
|
// return {
|
||||||
// samplesPerPixel: dataSet.uint16('x00280002'),
|
// samplesPerPixel: dataSet.uint16('x00280002'),
|
||||||
|
|
@ -249,4 +311,4 @@ function metaDataProvider(type, imageId) {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
export default metaDataProvider;
|
export default metaDataProvider;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,31 @@
|
||||||
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
|
import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
|
||||||
|
import {
|
||||||
|
getImageTypeSubItemFromDataset,
|
||||||
|
extractOrientationFromDataset,
|
||||||
|
extractPositionFromDataset,
|
||||||
|
extractSpacingFromDataset,
|
||||||
|
extractSliceThicknessFromDataset,
|
||||||
|
} from "@/utils/extractPositioningFromDataset";
|
||||||
|
import getNumberValues from "@/utils/getNumberValues";
|
||||||
|
|
||||||
|
function getSpacingBetweenSlices(dataSet) {
|
||||||
|
if (dataSet?.elements?.x00180088) {
|
||||||
|
return dataSet.floatString("x00180088");
|
||||||
|
}
|
||||||
|
const pixelMeasuresSequence = dataSet?.elements?.x00289110;
|
||||||
|
if (
|
||||||
|
pixelMeasuresSequence?.items?.length &&
|
||||||
|
pixelMeasuresSequence.items[0]?.dataSet?.elements?.x00180088
|
||||||
|
) {
|
||||||
|
return pixelMeasuresSequence.items[0].dataSet.floatString("x00180088");
|
||||||
|
}
|
||||||
|
}
|
||||||
function parseImageId(imageId) {
|
function parseImageId(imageId) {
|
||||||
// build a url by parsing out the url scheme and frame index from the imageId
|
// build a url by parsing out the url scheme and frame index from the imageId
|
||||||
const firstColonIndex = imageId.indexOf(':');
|
const firstColonIndex = imageId.indexOf(":");
|
||||||
|
|
||||||
let url = imageId.substring(firstColonIndex + 1);
|
let url = imageId.substring(firstColonIndex + 1);
|
||||||
const frameIndex = url.indexOf('frame=');
|
const frameIndex = url.indexOf("frame=");
|
||||||
|
|
||||||
let frame;
|
let frame;
|
||||||
|
|
||||||
|
|
@ -21,127 +42,112 @@ function parseImageId(imageId) {
|
||||||
frame,
|
frame,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function getNumberValues(dataSet, tag, minimumLength) {
|
|
||||||
const values = [];
|
|
||||||
const valueAsString = dataSet.string(tag);
|
|
||||||
|
|
||||||
if (!valueAsString) {
|
function metaDataProvider(type, imageId) {
|
||||||
|
const parsedImageId = parseImageId(imageId);
|
||||||
|
const dataSet = cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.get(
|
||||||
|
parsedImageId.url
|
||||||
|
);
|
||||||
|
if (!dataSet) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const split = valueAsString.split('\\');
|
if (type === "imagePlaneModule") {
|
||||||
|
const frameIndex = parsedImageId.frame !== undefined ? parsedImageId.frame - 1 : undefined;
|
||||||
|
// const imageOrientationPatient = extractOrientationFromDataset(dataSet, frameIndex);
|
||||||
|
// let imagePositionPatient = extractPositionFromDataset(dataSet, frameIndex);
|
||||||
|
// const pixelSpacing = extractSpacingFromDataset(dataSet, frameIndex);
|
||||||
|
// const sliceThickness = extractSliceThicknessFromDataset(dataSet, frameIndex);
|
||||||
|
const imageOrientationPatient = extractOrientationFromDataset(dataSet);
|
||||||
|
let imagePositionPatient = extractPositionFromDataset(dataSet);
|
||||||
|
const pixelSpacing = extractSpacingFromDataset(dataSet);
|
||||||
|
const sliceThickness = extractSliceThicknessFromDataset(dataSet);
|
||||||
|
|
||||||
|
const modality = dataSet.string("x00080060");
|
||||||
|
if (
|
||||||
|
modality &&
|
||||||
|
modality.includes("NM") &&
|
||||||
|
parsedImageId.frame !== undefined &&
|
||||||
|
parsedImageId.frame > 1
|
||||||
|
) {
|
||||||
|
// 坐标堆叠 (位置相同) :NM 多帧 DICOM 的空间位置 ( imagePositionPatient ) 通常只存在于 Detector Information Sequence 序列的第一个 Item 中。原来的代码在提取位置时,所有帧都被赋予了第一帧的位置。因为所有帧都挤在一个坐标上,3D 引擎无法根据厚度和法向量空间展开它们,导致间距 (Spacing) 为 0
|
||||||
|
// 当识别到 NM 多帧图像且属于多帧切片时,通过 RowCosines 与 ColumnCosines 的叉乘计算出切片的 法向量(Normal) ,并结合 帧索引 (frameIndex) 以及 Spacing Between Slices (0018,0088)(层间距) 或者 Slice Thickness (0018,0050)(层厚) ,自动推算补全后续每一帧的 imagePositionPatient 真实三维坐标
|
||||||
|
//
|
||||||
|
if (
|
||||||
|
imageOrientationPatient &&
|
||||||
|
imagePositionPatient &&
|
||||||
|
sliceThickness !== undefined
|
||||||
|
) {
|
||||||
|
const spacingBetweenSlices = getSpacingBetweenSlices(dataSet);
|
||||||
|
const step =
|
||||||
|
spacingBetweenSlices !== undefined ? spacingBetweenSlices : sliceThickness;
|
||||||
|
const rowCosines = [
|
||||||
|
parseFloat(imageOrientationPatient[0]),
|
||||||
|
parseFloat(imageOrientationPatient[1]),
|
||||||
|
parseFloat(imageOrientationPatient[2]),
|
||||||
|
];
|
||||||
|
const columnCosines = [
|
||||||
|
parseFloat(imageOrientationPatient[3]),
|
||||||
|
parseFloat(imageOrientationPatient[4]),
|
||||||
|
parseFloat(imageOrientationPatient[5]),
|
||||||
|
];
|
||||||
|
const normal = [
|
||||||
|
rowCosines[1] * columnCosines[2] - rowCosines[2] * columnCosines[1],
|
||||||
|
rowCosines[2] * columnCosines[0] - rowCosines[0] * columnCosines[2],
|
||||||
|
rowCosines[0] * columnCosines[1] - rowCosines[1] * columnCosines[0],
|
||||||
|
];
|
||||||
|
|
||||||
if (minimumLength && split.length < minimumLength) {
|
const offset = frameIndex * parseFloat(step);
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < split.length; i++) {
|
|
||||||
values.push(parseFloat(split[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
imagePositionPatient = [
|
||||||
}
|
parseFloat(imagePositionPatient[0]) + normal[0] * offset,
|
||||||
function getImageTypeSubItemFromDataset(dataSet, index) {
|
parseFloat(imagePositionPatient[1]) + normal[1] * offset,
|
||||||
const imageType = dataSet.string('x00080008');
|
parseFloat(imagePositionPatient[2]) + normal[2] * offset,
|
||||||
if (imageType) {
|
];
|
||||||
const subTypes = imageType.split('\\');
|
|
||||||
if (subTypes.length > index) {
|
|
||||||
return subTypes[index];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}
|
// const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
||||||
function isNMReconstructable(imageSubType) {
|
// const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||||||
return imageSubType === 'RECON TOMO' || imageSubType === 'RECON GATED TOMO';
|
// const pixelSpacing = extractSpacingFromDataset(dataSet);
|
||||||
}
|
const imagePixelSpacing = getNumberValues(dataSet, "x00181164", 2);
|
||||||
function extractOrientationFromNMMultiframeDataset(dataSet) {
|
|
||||||
let imageOrientationPatient;
|
// let rowCosines = null;
|
||||||
const modality = dataSet.string('x00080060');
|
|
||||||
if (modality?.includes('NM')) {
|
// let columnCosines = null;
|
||||||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
|
||||||
if (imageSubType && isNMReconstructable(imageSubType)) {
|
// if (imageOrientationPatient) {
|
||||||
if (dataSet.elements.x00540022) {
|
// rowCosines = [
|
||||||
imageOrientationPatient = getNumberValues(dataSet.elements.x00540022.items[0].dataSet, 'x00200037', 6);
|
// parseFloat(imageOrientationPatient[0]),
|
||||||
}
|
// parseFloat(imageOrientationPatient[1]),
|
||||||
}
|
// parseFloat(imageOrientationPatient[2]),
|
||||||
}
|
// ];
|
||||||
return imageOrientationPatient;
|
// columnCosines = [
|
||||||
}
|
// parseFloat(imageOrientationPatient[3]),
|
||||||
function extractOrientationFromDataset(dataSet) {
|
// parseFloat(imageOrientationPatient[4]),
|
||||||
let imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
// parseFloat(imageOrientationPatient[5]),
|
||||||
if (!imageOrientationPatient && dataSet.elements.x00209116) {
|
// ];
|
||||||
imageOrientationPatient = getNumberValues(dataSet.elements.x00209116.items[0].dataSet, 'x00200037', 6);
|
// }
|
||||||
}
|
const estimatedRadiographicMagnificationFactor = getNumberValues(
|
||||||
if (!imageOrientationPatient) {
|
dataSet,
|
||||||
imageOrientationPatient =
|
"x00181114",
|
||||||
extractOrientationFromNMMultiframeDataset(dataSet);
|
2
|
||||||
}
|
);
|
||||||
return imageOrientationPatient;
|
let columnPixelSpacing = null;
|
||||||
}
|
|
||||||
function extractPositionFromNMMultiframeDataset(dataSet) {
|
let rowPixelSpacing = null;
|
||||||
let imagePositionPatient;
|
|
||||||
const modality = dataSet.string('x00080060');
|
if (pixelSpacing) {
|
||||||
if (modality?.includes('NM')) {
|
rowPixelSpacing = pixelSpacing[0];
|
||||||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
columnPixelSpacing = pixelSpacing[1];
|
||||||
if (imageSubType && isNMReconstructable(imageSubType)) {
|
|
||||||
if (dataSet.elements.x00540022) {
|
|
||||||
imagePositionPatient = getNumberValues(dataSet.elements.x00540022.items[0].dataSet, 'x00200032', 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return imagePositionPatient;
|
|
||||||
}
|
|
||||||
function extractPositionFromDataset(dataSet) {
|
|
||||||
let imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
|
||||||
if (!imagePositionPatient && dataSet.elements.x00209113) {
|
|
||||||
imagePositionPatient = getNumberValues(dataSet.elements.x00209113.items[0].dataSet, 'x00200032', 3);
|
|
||||||
}
|
|
||||||
if (!imagePositionPatient) {
|
|
||||||
imagePositionPatient = extractPositionFromNMMultiframeDataset(dataSet);
|
|
||||||
}
|
|
||||||
return imagePositionPatient;
|
|
||||||
}
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
return sliceThickness;
|
|
||||||
}
|
|
||||||
function extractSpacingFromDataset(dataSet) {
|
|
||||||
let rowPixelSpacing = null
|
|
||||||
let columnPixelSpacing = null
|
|
||||||
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) {
|
} else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) {
|
||||||
rowPixelSpacing = imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
|
rowPixelSpacing =
|
||||||
columnPixelSpacing = imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
|
imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
|
||||||
|
columnPixelSpacing =
|
||||||
|
imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
|
||||||
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
|
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
|
||||||
rowPixelSpacing = imagePixelSpacing[0];
|
rowPixelSpacing = imagePixelSpacing[0];
|
||||||
columnPixelSpacing = imagePixelSpacing[1];
|
columnPixelSpacing = imagePixelSpacing[1];
|
||||||
}
|
}
|
||||||
return rowPixelSpacing !== null ? [rowPixelSpacing, columnPixelSpacing] : undefined
|
|
||||||
}
|
|
||||||
function metaDataProvider(type, imageId) {
|
|
||||||
const parsedImageId = parseImageId(imageId);
|
|
||||||
const dataSet = cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.get(parsedImageId.url)
|
|
||||||
|
|
||||||
|
|
||||||
if (!dataSet) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (type === 'imagePlaneModule') {
|
|
||||||
const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
|
||||||
const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
|
||||||
const pixelSpacing = extractSpacingFromDataset(dataSet);
|
|
||||||
|
|
||||||
let rowCosines = null;
|
let rowCosines = null;
|
||||||
|
|
||||||
|
|
@ -160,34 +166,35 @@ function metaDataProvider(type, imageId) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
frameOfReferenceUID: dataSet.string('x00200052'),
|
frameOfReferenceUID: dataSet.string("x00200052"),
|
||||||
rows: dataSet.uint16('x00280010'),
|
rows: dataSet.uint16("x00280010"),
|
||||||
columns: dataSet.uint16('x00280011'),
|
columns: dataSet.uint16("x00280011"),
|
||||||
imageOrientationPatient,
|
imageOrientationPatient,
|
||||||
rowCosines,
|
rowCosines,
|
||||||
columnCosines,
|
columnCosines,
|
||||||
imagePositionPatient,
|
imagePositionPatient,
|
||||||
sliceThickness: dataSet.floatString('x00180050'),
|
sliceThickness,
|
||||||
sliceLocation: dataSet.floatString('x00201041'),
|
sliceLocation: dataSet.floatString("x00201041"),
|
||||||
pixelSpacing: pixelSpacing,
|
pixelSpacing,
|
||||||
rowPixelSpacing: pixelSpacing ? pixelSpacing[0] : null,
|
rowPixelSpacing,
|
||||||
columnPixelSpacing: pixelSpacing ? pixelSpacing[1] : null,
|
columnPixelSpacing,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (type === 'nmMultiframeGeometryModule') {
|
if (type === "nmMultiframeGeometryModule") {
|
||||||
const modality = dataSet.string('x00080060');
|
const modality = dataSet.string("x00080060");
|
||||||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
||||||
return {
|
return {
|
||||||
modality,
|
modality,
|
||||||
imageType: dataSet.string('x00080008'),
|
imageType: dataSet.string("x00080008"),
|
||||||
imageSubType,
|
imageSubType,
|
||||||
imageOrientationPatient: extractOrientationFromDataset(dataSet),
|
imageOrientationPatient: extractOrientationFromDataset(dataSet),
|
||||||
imagePositionPatient: extractPositionFromDataset(dataSet),
|
imagePositionPatient: extractPositionFromDataset(dataSet),
|
||||||
sliceThickness: extractSliceThicknessFromDataset(dataSet),
|
sliceThickness: extractSliceThicknessFromDataset(dataSet),
|
||||||
pixelSpacing: extractSpacingFromDataset(dataSet),
|
pixelSpacing: extractSpacingFromDataset(dataSet),
|
||||||
numberOfFrames: dataSet.uint16('x00280008'),
|
numberOfFrames: dataSet.uint16("x00280008"),
|
||||||
isNMReconstructable: isNMReconstructable(imageSubType) && modality.includes('NM'),
|
isNMReconstructable:
|
||||||
};
|
isNMReconstructable(imageSubType) && modality.includes("NM"),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default metaDataProvider;
|
export default metaDataProvider;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
{{ `${series.TaskInfo.SubjectCode} ${series.TaskInfo.TaskBlindName} ` }}
|
{{ `${series.TaskInfo.SubjectCode} ${series.TaskInfo.TaskBlindName} ` }}
|
||||||
</h2>
|
</h2>
|
||||||
<div v-if="!isMip && !isFusion">Series: #{{ series.SeriesNumber }}</div>
|
<div v-if="!isMip && !isFusion">Series: #{{ series.SeriesNumber }}</div>
|
||||||
<div v-if="series.Stack && !isMip">Image: #{{ `${series.SliceIndex + 1}/${series.Stack.length}` }}</div>
|
<div v-if="series.Stack && !isMip">Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total || series.Stack.length}` }}</div>
|
||||||
<div v-if="!isMip && !isFusion">{{ series.Modality }}</div>
|
<div v-if="!isMip && !isFusion">{{ series.Modality }}</div>
|
||||||
<div v-if="isFusion">{{ ctSeries.Modality }} / {{ series.Modality }}</div>
|
<div v-if="isFusion">{{ ctSeries.Modality }} / {{ series.Modality }}</div>
|
||||||
<div v-if="isMip">MIP</div>
|
<div v-if="isMip">MIP</div>
|
||||||
|
|
@ -103,7 +103,7 @@ import {
|
||||||
setMipTransferFunctionForVolumeActor,
|
setMipTransferFunctionForVolumeActor,
|
||||||
setPetTransferFunctionForVolumeActor
|
setPetTransferFunctionForVolumeActor
|
||||||
} from './helpers/index.js'
|
} from './helpers/index.js'
|
||||||
const { BlendModes, OrientationAxis } = Enums;
|
const { BlendModes, OrientationAxis } = Enums;
|
||||||
const { getColormap } = csUtils.colormap;
|
const { getColormap } = csUtils.colormap;
|
||||||
import { vec3, mat4 } from 'gl-matrix'
|
import { vec3, mat4 } from 'gl-matrix'
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -149,7 +149,8 @@ export default {
|
||||||
size: null,
|
size: null,
|
||||||
location: null,
|
location: null,
|
||||||
sliceThickness: null,
|
sliceThickness: null,
|
||||||
wwwc: null
|
wwwc: null,
|
||||||
|
total: null
|
||||||
},
|
},
|
||||||
digitPlaces: 2,
|
digitPlaces: 2,
|
||||||
orientationMarkers: [],
|
orientationMarkers: [],
|
||||||
|
|
@ -224,7 +225,9 @@ export default {
|
||||||
if (this.series) {
|
if (this.series) {
|
||||||
this.series.SliceIndex = detail.imageIndex
|
this.series.SliceIndex = detail.imageIndex
|
||||||
}
|
}
|
||||||
this.sliderInfo.height = detail.imageIndex * 100 / detail.numberOfSlices
|
this.imageInfo.total = detail.numberOfSlices
|
||||||
|
const maxIndex = detail.numberOfSlices > 1 ? detail.numberOfSlices - 1 : 1
|
||||||
|
this.sliderInfo.height = detail.imageIndex * 100 / maxIndex
|
||||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||||
const zoom = viewport.getZoom()
|
const zoom = viewport.getZoom()
|
||||||
|
|
@ -414,9 +417,6 @@ export default {
|
||||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||||
const voiRange = { lower: 0, upper: v }
|
const voiRange = { lower: 0, upper: v }
|
||||||
this.currentVoiUpper = v
|
this.currentVoiUpper = v
|
||||||
if (this.isFusion) {
|
|
||||||
console.log('voiChange', v)
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||||
if (!viewport) return
|
if (!viewport) return
|
||||||
|
|
@ -476,12 +476,21 @@ export default {
|
||||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||||
if (!viewport) return
|
if (!viewport) return
|
||||||
let volumeId = this.isFusion ? this.ptVolumeId : this.volumeId
|
|
||||||
|
|
||||||
viewport.setProperties(
|
if (this.isFusion) {
|
||||||
{ colormap: { opacity: Number(opacity) } },
|
const ctVolumeId = this.ctSeries?.SeriesInstanceUid
|
||||||
volumeId
|
const ptVolumeId = this.ptVolumeId
|
||||||
)
|
const topVolumeId = this.fusionCtOnTop ? ctVolumeId : ptVolumeId
|
||||||
|
const bottomVolumeId = this.fusionCtOnTop ? ptVolumeId : ctVolumeId
|
||||||
|
const topOpacity = Number(opacity)
|
||||||
|
const bottomOpacity = 1 - topOpacity
|
||||||
|
if (bottomVolumeId) {
|
||||||
|
viewport.setProperties({ colormap: { opacity: bottomOpacity } }, bottomVolumeId)
|
||||||
|
}
|
||||||
|
if (topVolumeId) {
|
||||||
|
viewport.setProperties({ colormap: { opacity: topOpacity } }, topVolumeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
viewport.render()
|
viewport.render()
|
||||||
},
|
},
|
||||||
applyFusionOpacity() {
|
applyFusionOpacity() {
|
||||||
|
|
@ -512,11 +521,14 @@ export default {
|
||||||
},
|
},
|
||||||
async createImageIdsAndCacheMetaData(obj) {
|
async createImageIdsAndCacheMetaData(obj) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
await createImageIdsAndCacheMetaData({
|
try {
|
||||||
modality: obj.Modality,
|
return await createImageIdsAndCacheMetaData({
|
||||||
imageIds: obj.ImageIds
|
modality: obj.Modality,
|
||||||
})
|
imageIds: obj.ImageIds
|
||||||
this.loading = false
|
})
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getFusionVolumes() {
|
getFusionVolumes() {
|
||||||
const ctVolumeId = this.ctSeries?.SeriesInstanceUid
|
const ctVolumeId = this.ctSeries?.SeriesInstanceUid
|
||||||
|
|
@ -531,8 +543,8 @@ export default {
|
||||||
setCtTransferFunctionForVolumeActor({ ...r, volumeId: ctVolumeId })
|
setCtTransferFunctionForVolumeActor({ ...r, volumeId: ctVolumeId })
|
||||||
if (this.fusionCtOnTop) {
|
if (this.fusionCtOnTop) {
|
||||||
this.topFusionVolumeActor = r.volumeActor
|
this.topFusionVolumeActor = r.volumeActor
|
||||||
this.applyFusionOpacity()
|
|
||||||
}
|
}
|
||||||
|
this.applyFusionOpacity()
|
||||||
console.log("融合ct渲染成功")
|
console.log("融合ct渲染成功")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -543,8 +555,8 @@ export default {
|
||||||
setPetColorMapTransferFunctionForVolumeActor({ ...r, volumeId: ptFusionVolumeId })
|
setPetColorMapTransferFunctionForVolumeActor({ ...r, volumeId: ptFusionVolumeId })
|
||||||
if (!this.fusionCtOnTop) {
|
if (!this.fusionCtOnTop) {
|
||||||
this.topFusionVolumeActor = r.volumeActor
|
this.topFusionVolumeActor = r.volumeActor
|
||||||
this.applyFusionOpacity()
|
|
||||||
}
|
}
|
||||||
|
this.applyFusionOpacity()
|
||||||
console.log("融合pet渲染成功")
|
console.log("融合pet渲染成功")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -709,8 +721,10 @@ export default {
|
||||||
clickSlider(e) {
|
clickSlider(e) {
|
||||||
const height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
|
const height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
|
||||||
this.sliderInfo.height = height
|
this.sliderInfo.height = height
|
||||||
let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
|
const totalSlices = this.imageInfo.total || this.series.Stack?.length || 1
|
||||||
sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
|
const maxIndex = totalSlices > 1 ? totalSlices - 1 : 0
|
||||||
|
let sliceIdx = Math.round(maxIndex * height / 100)
|
||||||
|
sliceIdx = sliceIdx > maxIndex ? maxIndex : sliceIdx < 0 ? 0 : sliceIdx
|
||||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||||
const viewport = renderingEngine.getViewport(
|
const viewport = renderingEngine.getViewport(
|
||||||
this.viewportId
|
this.viewportId
|
||||||
|
|
@ -725,7 +739,7 @@ export default {
|
||||||
},
|
},
|
||||||
sliderMousedown(e) {
|
sliderMousedown(e) {
|
||||||
const boxHeight = this.$refs['sliderBox'].clientHeight
|
const boxHeight = this.$refs['sliderBox'].clientHeight
|
||||||
this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
|
this.sliderInfo.oldB = this.sliderInfo.height * boxHeight / 100
|
||||||
this.sliderInfo.oldM = e.clientY
|
this.sliderInfo.oldM = e.clientY
|
||||||
this.sliderInfo.isMove = true
|
this.sliderInfo.isMove = true
|
||||||
e.stopImmediatePropagation()
|
e.stopImmediatePropagation()
|
||||||
|
|
@ -739,8 +753,10 @@ export default {
|
||||||
if (delta < 0) return
|
if (delta < 0) return
|
||||||
if (delta > boxHeight) return
|
if (delta > boxHeight) return
|
||||||
const height = delta * 100 / boxHeight
|
const height = delta * 100 / boxHeight
|
||||||
let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
|
const totalSlices = this.imageInfo.total || this.series.Stack?.length || 1
|
||||||
sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
|
const maxIndex = totalSlices > 1 ? totalSlices - 1 : 0
|
||||||
|
let sliceIdx = Math.round(maxIndex * height / 100)
|
||||||
|
sliceIdx = sliceIdx > maxIndex ? maxIndex : sliceIdx < 0 ? 0 : sliceIdx
|
||||||
this.sliderInfo.height = height
|
this.sliderInfo.height = height
|
||||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||||
const viewport = renderingEngine.getViewport(
|
const viewport = renderingEngine.getViewport(
|
||||||
|
|
@ -891,7 +907,9 @@ export default {
|
||||||
|
|
||||||
.opacity-slider {
|
.opacity-slider {
|
||||||
-webkit-appearance: slider-vertical;
|
-webkit-appearance: slider-vertical;
|
||||||
writing-mode: bt-lr;
|
appearance: slider-vertical;
|
||||||
|
writing-mode: vertical-lr;
|
||||||
|
direction: rtl;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
||||||
|
|
@ -3696,8 +3696,8 @@ export default {
|
||||||
if (cache.getVolume(volumeId)) {
|
if (cache.getVolume(volumeId)) {
|
||||||
volume = cache.getVolume(volumeId)
|
volume = cache.getVolume(volumeId)
|
||||||
} else {
|
} else {
|
||||||
await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
|
let imageIds = await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
|
||||||
let imageIds = this.sortImageIdsByImagePositionPatient(serie.ImageIds)
|
// imageIds = this.sortImageIdsByImagePositionPatient(imageIds)
|
||||||
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: imageIds })
|
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: imageIds })
|
||||||
volume.load()
|
volume.load()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue