Skip to content

Commit

Permalink
fix: unify handling of annotation units and remove 'MO' (#161)
Browse files Browse the repository at this point in the history
* fix: unify handling of annotation units and remove 'MO'

* fix: only use SUV as PT unit if image is pre-scaled
  • Loading branch information
pwespi committed Aug 10, 2022
1 parent 0ddab21 commit 7fddeab
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 106 deletions.
6 changes: 3 additions & 3 deletions common/reviews/api/tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,7 @@ export class EllipticalROITool extends AnnotationTool {
hasMoved?: boolean;
} | null;
// (undocumented)
_getTextLines: (data: any, targetId: any) => any[];
_getTextLines: (data: any, targetId: string, isPreScaled: boolean) => string[];
// (undocumented)
handleSelectedCallback: (evt: EventTypes_2.MouseDownEventType, annotation: EllipticalROIAnnotation, handle: ToolHandle, interactionType?: string) => void;
// (undocumented)
Expand Down Expand Up @@ -3074,7 +3074,7 @@ export class ProbeTool extends AnnotationTool {
// (undocumented)
getHandleNearImagePoint(element: HTMLDivElement, annotation: ProbeAnnotation, canvasCoords: Types_2.Point2, proximity: number): ToolHandle | undefined;
// (undocumented)
_getTextLines(data: any, targetId: any): any[];
_getTextLines(data: any, targetId: string, isPreScaled: boolean): string[] | undefined;
// (undocumented)
_getValueForModality(value: any, imageVolume: any, modality: any): {};
// (undocumented)
Expand Down Expand Up @@ -3359,7 +3359,7 @@ export class RectangleROITool extends AnnotationTool {
height: number;
};
// (undocumented)
_getTextLines: (data: any, targetId: string) => any[];
_getTextLines: (data: any, targetId: string, isPreScaled: boolean) => string[] | undefined;
// (undocumented)
handleSelectedCallback: (evt: EventTypes_2.MouseDownEventType, annotation: RectangleROIAnnotation, handle: ToolHandle, interactionType?: string) => void;
// (undocumented)
Expand Down
2 changes: 1 addition & 1 deletion packages/tools/src/tools/annotation/AngleTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ class AngleTool extends AnnotationTool {
return renderStatus;
};

// text line for the current active length annotation
// text line for the current active angle annotation
_getTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId];
const { angle } = cachedVolumeStats;
Expand Down
5 changes: 4 additions & 1 deletion packages/tools/src/tools/annotation/DragProbeTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnota
import ProbeTool from './ProbeTool';
import { ProbeAnnotation } from '../../types/ToolSpecificAnnotationTypes';
import { StyleSpecifier } from '../../types/AnnotationStyle';
import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';

export default class DragProbeTool extends ProbeTool {
static toolName = 'DragProbe';
Expand Down Expand Up @@ -168,7 +169,9 @@ export default class DragProbeTool extends ProbeTool {

renderStatus = true;

const textLines = this._getTextLines(data, targetId);
const isPreScaled = isViewportPreScaled(viewport, targetId);

const textLines = this._getTextLines(data, targetId, isPreScaled);
if (textLines) {
const textCanvasCoordinates = [
canvasCoordinates[0] + 6,
Expand Down
46 changes: 13 additions & 33 deletions packages/tools/src/tools/annotation/EllipticalROITool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import {
import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
import { pointInShapeCallback } from '../../utilities/';
import { StyleSpecifier } from '../../types/AnnotationStyle';
import { getModalityUnit } from '../../utilities/getModalityUnit';
import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';

const { transformWorldToIndex } = csUtils;

Expand Down Expand Up @@ -868,7 +870,9 @@ export default class EllipticalROITool extends AnnotationTool {

renderStatus = true;

const textLines = this._getTextLines(data, targetId);
const isPreScaled = isViewportPreScaled(viewport, targetId);

const textLines = this._getTextLines(data, targetId, isPreScaled);
if (!textLines || textLines.length === 0) {
continue;
}
Expand Down Expand Up @@ -912,55 +916,31 @@ export default class EllipticalROITool extends AnnotationTool {
return renderStatus;
};

_getTextLines = (data, targetId) => {
_getTextLines = (data, targetId: string, isPreScaled: boolean): string[] => {
const cachedVolumeStats = data.cachedStats[targetId];
const { area, mean, stdDev, max, isEmptyArea, Modality, areaUnit } =
cachedVolumeStats;

const textLines = [];
let areaLine, meanLine, stdDevLine, maxLine;
const textLines: string[] = [];
const unit = getModalityUnit(Modality, isPreScaled);

if (area) {
areaLine = isEmptyArea
const areaLine = isEmptyArea
? `Area: Oblique not supported`
: `Area: ${area.toFixed(2)} ${areaUnit}\xb2`;
textLines.push(areaLine);
}

if (mean) {
meanLine = `Mean: ${mean.toFixed(2)}`;
textLines.push(`Mean: ${mean.toFixed(2)} ${unit}`);
}

if (max) {
maxLine = `Max: ${max.toFixed(2)}`;
textLines.push(`Max: ${max.toFixed(2)} ${unit}`);
}

if (stdDev) {
stdDevLine = `StdDev: ${stdDev.toFixed(2)}`;
}

let unit;
if (Modality === 'PT') {
unit = 'SUV';
} else if (Modality === 'CT') {
unit = 'HU';
} else {
unit = 'MO';
}

if (areaLine) {
textLines.push(areaLine);
}

if (meanLine) {
textLines.push(meanLine + ' ' + unit);
}

if (maxLine) {
textLines.push(maxLine + ' ' + unit);
}

if (stdDevLine) {
textLines.push(stdDevLine + ' ' + unit);
textLines.push(`Std Dev: ${stdDev.toFixed(2)} ${unit}`);
}

return textLines;
Expand Down
78 changes: 37 additions & 41 deletions packages/tools/src/tools/annotation/ProbeTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import {
} from '../../types';
import { ProbeAnnotation } from '../../types/ToolSpecificAnnotationTypes';
import { StyleSpecifier } from '../../types/AnnotationStyle';
import { getModalityUnit } from '../../utilities/getModalityUnit';
import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';

const { transformWorldToIndex } = csUtils;

Expand Down Expand Up @@ -502,7 +504,9 @@ export default class ProbeTool extends AnnotationTool {

renderStatus = true;

const textLines = this._getTextLines(data, targetId);
const isPreScaled = isViewportPreScaled(viewport, targetId);

const textLines = this._getTextLines(data, targetId, isPreScaled);
if (textLines) {
const textCanvasCoordinates = [
canvasCoordinates[0] + 6,
Expand All @@ -524,7 +528,11 @@ export default class ProbeTool extends AnnotationTool {
return renderStatus;
};

_getTextLines(data, targetId) {
_getTextLines(
data,
targetId: string,
isPreScaled: boolean
): string[] | undefined {
const cachedVolumeStats = data.cachedStats[targetId];
const { index, Modality, value, SUVBw, SUVLbm, SUVBsa } = cachedVolumeStats;

Expand All @@ -533,28 +541,21 @@ export default class ProbeTool extends AnnotationTool {
}

const textLines = [];
const unit = getModalityUnit(Modality, isPreScaled);

textLines.push(`(${index[0]}, ${index[1]}, ${index[2]})`);

if (Modality === 'PT') {
// Check if we have scaling for the other 2 SUV types for the PET.
// If we have scaling, value should be undefined
if (value) {
textLines.push(`${value.toFixed(2)} SUV`);
} else {
textLines.push(`${SUVBw.toFixed(2)} SUV bw`);

if (SUVLbm) {
textLines.push(`${SUVLbm.toFixed(2)} SUV lbm`);
}
if (SUVBsa) {
textLines.push(`${SUVBsa.toFixed(2)} SUV bsa`);
}
// Check if we have scaling for the other 2 SUV types for the PET.
if (Modality === 'PT' && isPreScaled === true && SUVBw !== undefined) {
textLines.push(`${SUVBw.toFixed(2)} SUV bw`);
if (SUVLbm) {
textLines.push(`${SUVLbm.toFixed(2)} SUV lbm`);
}
if (SUVBsa) {
textLines.push(`${SUVBsa.toFixed(2)} SUV bsa`);
}
} else if (Modality === 'CT') {
textLines.push(`${value.toFixed(2)} HU`);
} else {
textLines.push(`${value.toFixed(2)} MO`);
textLines.push(`${value.toFixed(2)} ${unit}`);
}

return textLines;
Expand All @@ -563,33 +564,28 @@ export default class ProbeTool extends AnnotationTool {
_getValueForModality(value, imageVolume, modality) {
const values = {};

if (modality === 'PT') {
// Check if we have scaling for the other 2 SUV types for the PET.
if (
imageVolume.scaling.PET &&
(imageVolume.scaling.PET.suvbwToSuvbsa ||
imageVolume.scaling.PET.suvbwToSuvlbm)
) {
const { suvbwToSuvlbm, suvbwToSuvbsa } = imageVolume.scaling.PET;

values['SUVBw'] = value;
values['value'] = value;

if (suvbwToSuvlbm) {
const SUVLbm = value * suvbwToSuvlbm;
// Check if we have scaling for the other 2 SUV types for the PET.
if (
modality === 'PT' &&
imageVolume.scaling.PET &&
(imageVolume.scaling.PET.suvbwToSuvbsa ||
imageVolume.scaling.PET.suvbwToSuvlbm)
) {
const { suvbwToSuvlbm, suvbwToSuvbsa } = imageVolume.scaling.PET;

values['SUVLbm'] = SUVLbm;
}
values['SUVBw'] = value;

if (suvbwToSuvlbm) {
const SUVBsa = value * suvbwToSuvbsa;
if (suvbwToSuvlbm) {
const SUVLbm = value * suvbwToSuvlbm;
values['SUVLbm'] = SUVLbm;
}

values['SUVBsa'] = SUVBsa;
}
} else {
values['value'] = value;
if (suvbwToSuvbsa) {
const SUVBsa = value * suvbwToSuvbsa;
values['SUVBsa'] = SUVBsa;
}
} else {
values['value'] = value;
}

return values;
Expand Down
44 changes: 17 additions & 27 deletions packages/tools/src/tools/annotation/RectangleROITool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import {
AnnotationModifiedEventDetail,
} from '../../types/EventTypes';
import { StyleSpecifier } from '../../types/AnnotationStyle';
import { getModalityUnit } from '../../utilities/getModalityUnit';
import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScaled';

const { transformWorldToIndex } = csUtils;

Expand Down Expand Up @@ -766,7 +768,9 @@ export default class RectangleROITool extends AnnotationTool {

renderStatus = true;

const textLines = this._getTextLines(data, targetId);
const isPreScaled = isViewportPreScaled(viewport, targetId);

const textLines = this._getTextLines(data, targetId, isPreScaled);
if (!textLines || textLines.length === 0) {
continue;
}
Expand Down Expand Up @@ -831,41 +835,27 @@ export default class RectangleROITool extends AnnotationTool {
*
* @param data - The annotation tool-specific data.
* @param targetId - The volumeId of the volume to display the stats for.
* @param isPreScaled - Whether the viewport is pre-scaled or not.
*/
_getTextLines = (data, targetId: string) => {
_getTextLines = (
data,
targetId: string,
isPreScaled: boolean
): string[] | undefined => {
const cachedVolumeStats = data.cachedStats[targetId];
const { area, mean, max, stdDev, Modality, areaUnit } = cachedVolumeStats;

if (mean === undefined) {
return;
}

const textLines = [];

const areaLine = `Area: ${area.toFixed(2)} ${areaUnit}\xb2`;
let meanLine = `Mean: ${mean.toFixed(2)}`;
let maxLine = `Max: ${max.toFixed(2)}`;
let stdDevLine = `Std Dev: ${stdDev.toFixed(2)}`;

// Give appropriate units for the modality.
if (Modality === 'PT') {
meanLine += ' SUV';
maxLine += ' SUV';
stdDevLine += ' SUV';
} else if (Modality === 'CT') {
meanLine += ' HU';
maxLine += ' HU';
stdDevLine += ' HU';
} else {
meanLine += ' MO';
maxLine += ' MO';
stdDevLine += ' MO';
}
const textLines: string[] = [];
const unit = getModalityUnit(Modality, isPreScaled);

textLines.push(areaLine);
textLines.push(maxLine);
textLines.push(meanLine);
textLines.push(stdDevLine);
textLines.push(`Area: ${area.toFixed(2)} ${areaUnit}\xb2`);
textLines.push(`Mean: ${mean.toFixed(2)} ${unit}`);
textLines.push(`Max: ${max.toFixed(2)} ${unit}`);
textLines.push(`Std Dev: ${stdDev.toFixed(2)} ${unit}`);

return textLines;
};
Expand Down
11 changes: 11 additions & 0 deletions packages/tools/src/utilities/getModalityUnit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function getModalityUnit(modality: string, isPreScaled: boolean): string {
if (modality === 'CT') {
return 'HU';
} else if (modality === 'PT' && isPreScaled === true) {
return 'SUV';
} else {
return '';
}
}

export { getModalityUnit };
24 changes: 24 additions & 0 deletions packages/tools/src/utilities/viewport/isViewportPreScaled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
cache,
StackViewport,
Types,
VolumeViewport,
} from '@cornerstonejs/core';

function isViewportPreScaled(
viewport: Types.IStackViewport | Types.IVolumeViewport,
targetId: string
): boolean {
if (viewport instanceof VolumeViewport) {
const volumeId = targetId.split('volumeId:')[1];
const volume = cache.getVolume(volumeId);
return volume.scaling && Object.keys(volume.scaling).length > 0;
} else if (viewport instanceof StackViewport) {
const { preScale } = viewport.getImageData();
return preScale.scaled;
} else {
throw new Error('Viewport is not a valid type');
}
}

export { isViewportPreScaled };

0 comments on commit 7fddeab

Please sign in to comment.