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:
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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user