@cornerstonejs/core
Version:
Cornerstone3D Core
236 lines (235 loc) • 10.9 kB
JavaScript
import * as metaData from '../../metaData.js';
import * as CONSTANTS from '../../constants/index.js';
import * as Enums from '../../enums/index.js';
import { vec3 } from 'gl-matrix';
const { MPR_CAMERA_VALUES } = CONSTANTS;
const { OrientationAxis } = Enums;
export function calculateCameraPosition(rowCosineVec, colCosineVec, scanAxisNormal, orientation) {
let referenceCameraValues;
switch (orientation) {
case OrientationAxis.AXIAL:
case OrientationAxis.AXIAL_REFORMAT:
referenceCameraValues = MPR_CAMERA_VALUES.axial;
break;
case OrientationAxis.SAGITTAL:
case OrientationAxis.SAGITTAL_REFORMAT:
referenceCameraValues = MPR_CAMERA_VALUES.sagittal;
break;
case OrientationAxis.CORONAL:
case OrientationAxis.CORONAL_REFORMAT:
referenceCameraValues = MPR_CAMERA_VALUES.coronal;
break;
case OrientationAxis.REFORMAT:
const autoDetected = getOrientationFromScanAxisNormal(scanAxisNormal);
switch (autoDetected) {
case OrientationAxis.AXIAL:
referenceCameraValues = MPR_CAMERA_VALUES.axial;
break;
case OrientationAxis.SAGITTAL:
referenceCameraValues = MPR_CAMERA_VALUES.sagittal;
break;
case OrientationAxis.CORONAL:
referenceCameraValues = MPR_CAMERA_VALUES.coronal;
break;
default:
referenceCameraValues = MPR_CAMERA_VALUES.axial;
}
break;
default:
referenceCameraValues = MPR_CAMERA_VALUES.axial;
break;
}
const normalizedRowCosine = vec3.normalize(vec3.create(), rowCosineVec);
const normalizedColCosine = vec3.normalize(vec3.create(), colCosineVec);
const normalizedScanAxis = vec3.normalize(vec3.create(), scanAxisNormal);
const inputVectors = [
normalizedRowCosine,
normalizedColCosine,
normalizedScanAxis,
];
const referenceVectors = [
vec3.fromValues(referenceCameraValues.viewRight[0], referenceCameraValues.viewRight[1], referenceCameraValues.viewRight[2]),
vec3.fromValues(referenceCameraValues.viewUp[0], referenceCameraValues.viewUp[1], referenceCameraValues.viewUp[2]),
vec3.fromValues(referenceCameraValues.viewPlaneNormal[0], referenceCameraValues.viewPlaneNormal[1], referenceCameraValues.viewPlaneNormal[2]),
];
const usedInputIndices = new Set();
const findBestMatch = (refVector) => {
let bestMatch = 0;
let bestDot = -2;
let shouldInvert = false;
inputVectors.forEach((inputVec, index) => {
if (usedInputIndices.has(index)) {
return;
}
const dot = vec3.dot(refVector, inputVec);
const absDot = Math.abs(dot);
if (absDot > bestDot) {
bestDot = absDot;
bestMatch = index;
shouldInvert = dot < 0;
}
});
usedInputIndices.add(bestMatch);
const matchedVector = vec3.clone(inputVectors[bestMatch]);
if (shouldInvert) {
vec3.negate(matchedVector, matchedVector);
}
return matchedVector;
};
const viewRight = findBestMatch(referenceVectors[0]);
const viewUp = findBestMatch(referenceVectors[1]);
const viewPlaneNormal = findBestMatch(referenceVectors[2]);
return {
viewPlaneNormal: [
viewPlaneNormal[0],
viewPlaneNormal[1],
viewPlaneNormal[2],
],
viewUp: [viewUp[0], viewUp[1], viewUp[2]],
viewRight: [viewRight[0], viewRight[1], viewRight[2]],
};
}
export function getCameraVectors(viewport, config) {
if (!viewport.getActors()?.length) {
return;
}
if (viewport.type !== Enums.ViewportType.ORTHOGRAPHIC) {
console.warn('Viewport should be a volume viewport');
}
let imageId = viewport.getCurrentImageId();
if (!imageId) {
imageId = viewport.getImageIds()?.[0];
}
if (!imageId) {
return;
}
const { imageOrientationPatient } = metaData.get(Enums.MetadataModules.IMAGE_PLANE, imageId);
const rowCosineVec = vec3.fromValues(imageOrientationPatient[0], imageOrientationPatient[1], imageOrientationPatient[2]);
const colCosineVec = vec3.fromValues(imageOrientationPatient[3], imageOrientationPatient[4], imageOrientationPatient[5]);
const scanAxisNormal = vec3.cross(vec3.create(), rowCosineVec, colCosineVec);
let { orientation } = config || {};
const { useViewportNormal } = config || {};
let normalPlaneForOrientation = scanAxisNormal;
if (useViewportNormal) {
normalPlaneForOrientation = viewport.getCamera().viewPlaneNormal;
}
if (!orientation || orientation === OrientationAxis.REFORMAT) {
orientation = getOrientationFromScanAxisNormal(normalPlaneForOrientation);
}
return calculateCameraPosition(rowCosineVec, colCosineVec, scanAxisNormal, orientation);
}
export function getOrientationFromScanAxisNormal(scanAxisNormal) {
const normalizedScanAxis = vec3.normalize(vec3.create(), scanAxisNormal);
const axialNormal = vec3.fromValues(MPR_CAMERA_VALUES.axial.viewPlaneNormal[0], MPR_CAMERA_VALUES.axial.viewPlaneNormal[1], MPR_CAMERA_VALUES.axial.viewPlaneNormal[2]);
const sagittalNormal = vec3.fromValues(MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[0], MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[1], MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[2]);
const coronalNormal = vec3.fromValues(MPR_CAMERA_VALUES.coronal.viewPlaneNormal[0], MPR_CAMERA_VALUES.coronal.viewPlaneNormal[1], MPR_CAMERA_VALUES.coronal.viewPlaneNormal[2]);
const axialDot = Math.abs(vec3.dot(normalizedScanAxis, axialNormal));
const sagittalDot = Math.abs(vec3.dot(normalizedScanAxis, sagittalNormal));
const coronalDot = Math.abs(vec3.dot(normalizedScanAxis, coronalNormal));
if (axialDot >= sagittalDot && axialDot >= coronalDot) {
return OrientationAxis.AXIAL;
}
else if (sagittalDot >= coronalDot) {
return OrientationAxis.SAGITTAL;
}
else {
return OrientationAxis.CORONAL;
}
}
export function getAcquisitionPlaneReformatOrientation(imageOrientationPatient) {
if (!imageOrientationPatient || imageOrientationPatient.length !== 6) {
return null;
}
const rowVec = vec3.fromValues(imageOrientationPatient[0], imageOrientationPatient[1], imageOrientationPatient[2]);
const colVec = vec3.fromValues(imageOrientationPatient[3], imageOrientationPatient[4], imageOrientationPatient[5]);
const scanAxisNormal = vec3.create();
vec3.cross(scanAxisNormal, rowVec, colVec);
vec3.normalize(rowVec, rowVec);
vec3.normalize(colVec, colVec);
vec3.normalize(scanAxisNormal, scanAxisNormal);
const negRowVec = vec3.create();
vec3.negate(negRowVec, rowVec);
const negColVec = vec3.create();
vec3.negate(negColVec, colVec);
const negScanAxisNormal = vec3.create();
vec3.negate(negScanAxisNormal, scanAxisNormal);
const acquisitionVectors = [
{ vec: rowVec, name: 'row' },
{ vec: colVec, name: 'col' },
{ vec: scanAxisNormal, name: 'scanAxis' },
{ vec: negRowVec, name: '-row' },
{ vec: negColVec, name: '-col' },
{ vec: negScanAxisNormal, name: '-scanAxis' },
];
const standardViews = [
{
name: 'axial',
viewPlaneNormal: vec3.fromValues(MPR_CAMERA_VALUES.axial.viewPlaneNormal[0], MPR_CAMERA_VALUES.axial.viewPlaneNormal[1], MPR_CAMERA_VALUES.axial.viewPlaneNormal[2]),
viewUp: vec3.fromValues(MPR_CAMERA_VALUES.axial.viewUp[0], MPR_CAMERA_VALUES.axial.viewUp[1], MPR_CAMERA_VALUES.axial.viewUp[2]),
},
{
name: 'sagittal',
viewPlaneNormal: vec3.fromValues(MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[0], MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[1], MPR_CAMERA_VALUES.sagittal.viewPlaneNormal[2]),
viewUp: vec3.fromValues(MPR_CAMERA_VALUES.sagittal.viewUp[0], MPR_CAMERA_VALUES.sagittal.viewUp[1], MPR_CAMERA_VALUES.sagittal.viewUp[2]),
},
{
name: 'coronal',
viewPlaneNormal: vec3.fromValues(MPR_CAMERA_VALUES.coronal.viewPlaneNormal[0], MPR_CAMERA_VALUES.coronal.viewPlaneNormal[1], MPR_CAMERA_VALUES.coronal.viewPlaneNormal[2]),
viewUp: vec3.fromValues(MPR_CAMERA_VALUES.coronal.viewUp[0], MPR_CAMERA_VALUES.coronal.viewUp[1], MPR_CAMERA_VALUES.coronal.viewUp[2]),
},
];
let bestAlignment = -Infinity;
let bestViewPlaneNormal = null;
let bestViewUp = null;
for (const standardView of standardViews) {
let bestPairScore = -Infinity;
let bestPairViewPlaneNormal = null;
let bestPairViewUp = null;
for (let i = 0; i < acquisitionVectors.length; i++) {
for (let j = 0; j < acquisitionVectors.length; j++) {
if (i === j)
continue;
const v1 = acquisitionVectors[i].vec;
const v2 = acquisitionVectors[j].vec;
const dotProduct = Math.abs(vec3.dot(v1, v2));
if (dotProduct > 0.1)
continue;
const score1 = Math.abs(vec3.dot(v1, standardView.viewPlaneNormal));
const score2 = Math.abs(vec3.dot(v2, standardView.viewUp));
const totalScore = score1 + score2;
const score1Swapped = Math.abs(vec3.dot(v2, standardView.viewPlaneNormal));
const score2Swapped = Math.abs(vec3.dot(v1, standardView.viewUp));
const totalScoreSwapped = score1Swapped + score2Swapped;
if (totalScoreSwapped > totalScore &&
totalScoreSwapped > bestPairScore) {
bestPairScore = totalScoreSwapped;
bestPairViewPlaneNormal = v2;
bestPairViewUp = v1;
}
else if (totalScore > bestPairScore) {
bestPairScore = totalScore;
bestPairViewPlaneNormal = v1;
bestPairViewUp = v2;
}
}
}
if (bestPairScore > bestAlignment &&
bestPairViewPlaneNormal &&
bestPairViewUp) {
bestAlignment = bestPairScore;
bestViewPlaneNormal = bestPairViewPlaneNormal;
bestViewUp = bestPairViewUp;
}
}
if (!bestViewPlaneNormal || !bestViewUp) {
return null;
}
return {
viewPlaneNormal: [
bestViewPlaneNormal[0],
bestViewPlaneNormal[1],
bestViewPlaneNormal[2],
],
viewUp: [bestViewUp[0], bestViewUp[1], bestViewUp[2]],
};
}