feat(Thumbnail): Display set messages support & displaying of series inconsistencies in the thumbnail (#3499)

Co-authored-by: Igor Octaviano <igoroctaviano@gmail.com>
This commit is contained in:
rodrigobasilio2022andIgor Octaviano authored and GitHub committed 2023-08-11 19:16:25 -03:00
1 parent aedb9d1576
commit 5302e5b62b
31 files changed
+1126 -12

No files matched your search

@@ -0,0 +1,20 @@
import { vec3 } from 'gl-matrix';
/**
* Calculates the scanAxisNormal based on a image orientation vector extract from a frame
* @param {*} imageOrientation
* @returns
*/
export default function calculateScanAxisNormal(imageOrientation) {
const rowCosineVec = vec3.fromValues(
imageOrientation[0],
imageOrientation[1],
imageOrientation[2]
);
const colCosineVec = vec3.fromValues(
imageOrientation[3],
imageOrientation[4],
imageOrientation[5]
);
return vec3.cross(vec3.create(), rowCosineVec, colCosineVec);
}
@@ -0,0 +1,26 @@
import toNumber from '@ohif/core/src/utils/toNumber';
/**
* Check if all voxels in series images has same number of components (samplesPerPixel)
* @param {*} instances
* @returns
*/
export default function areAllImageComponentsEqual(
instances: Array<any>
): boolean {
if (!instances?.length) {
return false;
}
const firstImage = instances[0];
const firstImageSamplesPerPixel = toNumber(firstImage.SamplesPerPixel);
for (let i = 1; i < instances.length; i++) {
const instance = instances[i];
const { SamplesPerPixel } = instance;
if (SamplesPerPixel !== firstImageSamplesPerPixel) {
return false;
}
}
return true;
}
@@ -0,0 +1,27 @@
import toNumber from '@ohif/core/src/utils/toNumber';
/**
* Check if the frames in a series has different dimensions
* @param {*} instances
* @returns
*/
export default function areAllImageDimensionsEqual(
instances: Array<any>
): boolean {
if (!instances?.length) {
return false;
}
const firstImage = instances[0];
const firstImageRows = toNumber(firstImage.Rows);
const firstImageColumns = toNumber(firstImage.Columns);
for (let i = 1; i < instances.length; i++) {
const instance = instances[i];
const { Rows, Columns } = instance;
if (Rows !== firstImageRows || Columns !== firstImageColumns) {
return false;
}
}
return true;
}
@@ -0,0 +1,31 @@
import toNumber from '@ohif/core/src/utils/toNumber';
import { _isSameOrientation } from '@ohif/core/src/utils/isDisplaySetReconstructable';
/**
* Check is the series has frames with different orientations
* @param {*} instances
* @returns
*/
export default function areAllImageOrientationsEqual(
instances: Array<any>
): boolean {
if (!instances?.length) {
return false;
}
const firstImage = instances[0];
const firstImageOrientationPatient = toNumber(
firstImage.ImageOrientationPatient
);
for (let i = 1; i < instances.length; i++) {
const instance = instances[i];
const imageOrientationPatient = toNumber(instance.ImageOrientationPatient);
if (
!_isSameOrientation(imageOrientationPatient, firstImageOrientationPatient)
) {
return false;
}
}
return true;
}
@@ -0,0 +1,76 @@
import { vec3 } from 'gl-matrix';
import toNumber from '@ohif/core/src/utils/toNumber';
import { _getPerpendicularDistance } from '@ohif/core/src/utils/isDisplaySetReconstructable';
import calculateScanAxisNormal from '../calculateScanAxisNormal';
/**
* Checks if there is a position shift between consecutive frames
* @param {*} previousPosition
* @param {*} actualPosition
* @param {*} scanAxisNormal
* @param {*} averageSpacingBetweenFrames
* @returns
*/
function _checkSeriesPositionShift(
previousPosition,
actualPosition,
scanAxisNormal,
averageSpacingBetweenFrames
) {
// predicted position should be the previous position added by the multiplication
// of the scanAxisNormal and the average spacing between frames
const predictedPosition = vec3.scaleAndAdd(
vec3.create(),
previousPosition,
scanAxisNormal,
averageSpacingBetweenFrames
);
return (
vec3.distance(actualPosition, predictedPosition) >
averageSpacingBetweenFrames
);
}
/**
* Checks if a series has position shifts between consecutive frames
* @param {*} instances
* @returns
*/
export default function areAllImagePositionsEqual(
instances: Array<any>
): boolean {
if (!instances?.length) {
return false;
}
const firstImageOrientationPatient = toNumber(
instances[0].ImageOrientationPatient
);
const scanAxisNormal = calculateScanAxisNormal(firstImageOrientationPatient);
const firstImagePositionPatient = toNumber(instances[0].ImagePositionPatient);
const lastIpp = toNumber(
instances[instances.length - 1].ImagePositionPatient
);
const averageSpacingBetweenFrames =
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
(instances.length - 1);
let previousImagePositionPatient = firstImagePositionPatient;
for (let i = 1; i < instances.length; i++) {
const instance = instances[i];
const imagePositionPatient = toNumber(instance.ImagePositionPatient);
if (
_checkSeriesPositionShift(
previousImagePositionPatient,
imagePositionPatient,
scanAxisNormal,
averageSpacingBetweenFrames
)
) {
return false;
}
previousImagePositionPatient = imagePositionPatient;
}
return true;
}
@@ -0,0 +1,67 @@
import {
_getPerpendicularDistance,
_getSpacingIssue,
reconstructionIssues,
} from '@ohif/core/src/utils/isDisplaySetReconstructable';
import { DisplaySetMessage } from '@ohif/core';
import toNumber from '@ohif/core/src/utils/toNumber';
import { DisplaySetMessageList } from '@ohif/core';
/**
* Checks if series has spacing issues
* @param {*} instances
* @param {*} warnings
*/
export default function areAllImageSpacingEqual(
instances: Array<any>,
messages: DisplaySetMessageList
): void {
if (!instances?.length) {
return;
}
const firstImagePositionPatient = toNumber(instances[0].ImagePositionPatient);
const lastIpp = toNumber(
instances[instances.length - 1].ImagePositionPatient
);
const averageSpacingBetweenFrames =
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
(instances.length - 1);
let previousImagePositionPatient = firstImagePositionPatient;
const issuesFound = [];
for (let i = 1; i < instances.length; i++) {
const instance = instances[i];
const imagePositionPatient = toNumber(instance.ImagePositionPatient);
const spacingBetweenFrames = _getPerpendicularDistance(
imagePositionPatient,
previousImagePositionPatient
);
const spacingIssue = _getSpacingIssue(
spacingBetweenFrames,
averageSpacingBetweenFrames
);
if (spacingIssue) {
const issue = spacingIssue.issue;
// avoid multiple warning of the same thing
if (!issuesFound.includes(issue)) {
issuesFound.push(issue);
if (issue === reconstructionIssues.MISSING_FRAMES) {
messages.addMessage(DisplaySetMessage.CODES.MISSING_FRAMES);
} else if (issue === reconstructionIssues.IRREGULAR_SPACING) {
messages.addMessage(DisplaySetMessage.CODES.IRREGULAR_SPACING);
}
}
// we just want to find issues not how many
if (issuesFound.length > 1) {
break;
}
}
previousImagePositionPatient = imagePositionPatient;
}
}
@@ -0,0 +1,32 @@
import {
hasPixelMeasurements,
hasOrientation,
hasPosition,
} from '@ohif/core/src/utils/isDisplaySetReconstructable';
import { DisplaySetMessage, DisplaySetMessageList } from '@ohif/core';
/**
* Check various multi frame issues. It calls OHIF core functions
* @param {*} multiFrameInstance
* @param {*} warnings
*/
export default function checkMultiFrame(
multiFrameInstance,
messages: DisplaySetMessageList
): void {
if (!hasPixelMeasurements(multiFrameInstance)) {
messages.addMessage(
DisplaySetMessage.CODES.MULTIFRAME_NO_PIXEL_MEASUREMENTS
);
}
if (!hasOrientation(multiFrameInstance)) {
messages.addMessage(DisplaySetMessage.CODES.MULTIFRAME_NO_ORIENTATION);
}
if (!hasPosition(multiFrameInstance)) {
messages.addMessage(
DisplaySetMessage.CODES.MULTIFRAME_NO_POSITION_INFORMATION
);
}
}
@@ -0,0 +1,37 @@
import areAllImageDimensionsEqual from './areAllImageDimensionsEqual';
import areAllImageComponentsEqual from './areAllImageComponentsEqual';
import areAllImageOrientationsEqual from './areAllImageOrientationsEqual';
import areAllImagePositionsEqual from './areAllImagePositionsEqual';
import areAllImageSpacingEqual from './areAllImageSpacingEqual';
import { DisplaySetMessage, DisplaySetMessageList } from '@ohif/core';
/**
* Runs various checks in a single frame series
* @param {*} instances
* @param {*} warnings
*/
export default function checkSingleFrames(
instances: Array<any>,
messages: DisplaySetMessageList
): void {
if (instances.length > 2) {
if (!areAllImageDimensionsEqual(instances)) {
messages.addMessage(DisplaySetMessage.CODES.INCONSISTENT_DIMENSIONS);
}
if (!areAllImageComponentsEqual(instances)) {
messages.addMessage(DisplaySetMessage.CODES.INCONSISTENT_COMPONENTS);
}
if (!areAllImageOrientationsEqual(instances)) {
messages.addMessage(DisplaySetMessage.CODES.INCONSISTENT_ORIENTATIONS);
}
if (!areAllImagePositionsEqual(instances)) {
messages.addMessage(
DisplaySetMessage.CODES.INCONSISTENT_POSITION_INFORMATION
);
}
areAllImageSpacingEqual(instances, messages);
}
}