feat: Ultrasound Pleura B-line Annotation Mode & Extension (#5061)
This commit is contained in:
1 parent
9b8eef9474
commit
226f9b0773
46 files changed
+7123
-3660
No files matched your search
@@ -0,0 +1,179 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { utils } from '@ohif/core';
|
||||
import { getIsLocked } from './utils/getIsLocked';
|
||||
import { getIsVisible } from './utils/getIsVisible';
|
||||
import { annotation } from '@cornerstonejs/tools';
|
||||
const UltrasoundPleuraBLine = {
|
||||
toAnnotation: measurement => {},
|
||||
|
||||
/**
|
||||
* Maps cornerstone annotation event data to measurement service format.
|
||||
*
|
||||
* @param {Object} cornerstone Cornerstone event data
|
||||
* @return {Measurement} Measurement instance
|
||||
*/
|
||||
toMeasurement: (
|
||||
csToolsEventDetail,
|
||||
displaySetService,
|
||||
CornerstoneViewportService,
|
||||
getValueTypeFromToolType,
|
||||
customizationService
|
||||
) => {
|
||||
const { annotation } = csToolsEventDetail;
|
||||
const { metadata, data, annotationUID } = annotation;
|
||||
const isLocked = getIsLocked(annotationUID);
|
||||
const isVisible = getIsVisible(annotationUID);
|
||||
if (!metadata || !data) {
|
||||
console.warn('UltrasoundPleuraBLine tool: Missing metadata or data');
|
||||
return null;
|
||||
}
|
||||
|
||||
const { toolName, referencedImageId, FrameOfReferenceUID } = metadata;
|
||||
const validToolType = SUPPORTED_TOOLS.includes(toolName);
|
||||
|
||||
if (!validToolType) {
|
||||
throw new Error('Tool not supported');
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } =
|
||||
getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
let displaySet;
|
||||
|
||||
if (SOPInstanceUID) {
|
||||
displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
} else {
|
||||
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID);
|
||||
}
|
||||
|
||||
const { points } = data.handles;
|
||||
|
||||
const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
|
||||
|
||||
const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService);
|
||||
const getReport = () =>
|
||||
_getReport(mappedAnnotations, points, FrameOfReferenceUID, customizationService);
|
||||
|
||||
return {
|
||||
uid: annotationUID,
|
||||
SOPInstanceUID,
|
||||
FrameOfReferenceUID,
|
||||
points,
|
||||
metadata,
|
||||
referenceSeriesUID: SeriesInstanceUID,
|
||||
referenceStudyUID: StudyInstanceUID,
|
||||
frameNumber: mappedAnnotations?.[0]?.frameNumber || 1,
|
||||
toolName: metadata.toolName,
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
label: data.label,
|
||||
displayText: displayText,
|
||||
data: data.cachedStats,
|
||||
type: getValueTypeFromToolType(toolName),
|
||||
getReport,
|
||||
isLocked,
|
||||
isVisible,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
const { metadata } = annotation;
|
||||
const { annotationType } = annotation.data;
|
||||
const { referencedImageId } = metadata;
|
||||
|
||||
const annotations = [];
|
||||
if (!referencedImageId) {
|
||||
throw new Error('Non-acquisition plane measurement mapping not supported');
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
|
||||
getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
frameNumber
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
SOPInstanceUID,
|
||||
SeriesNumber,
|
||||
frameNumber,
|
||||
annotationType,
|
||||
});
|
||||
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/*
|
||||
This function is used to convert the measurement data to a format that is
|
||||
suitable for the report generation (e.g. for the csv report). The report
|
||||
returns a list of columns and corresponding values.
|
||||
*/
|
||||
function _getReport(mappedAnnotations, points, FrameOfReferenceUID, customizationService) {
|
||||
const columns = [];
|
||||
const values = [];
|
||||
|
||||
// Add Type
|
||||
columns.push('AnnotationType');
|
||||
values.push('Cornerstone:UltrasoundPleuraBLine');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { annotationType } = annotation;
|
||||
columns.push('AnnotationType');
|
||||
values.push(annotationType);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
columns.push('FrameOfReferenceUID');
|
||||
values.push(FrameOfReferenceUID);
|
||||
}
|
||||
|
||||
if (points) {
|
||||
columns.push('points');
|
||||
values.push(points.map(p => p.join(' ')).join(';'));
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
values,
|
||||
};
|
||||
}
|
||||
|
||||
function getDisplayText(mappedAnnotations, displaySet, customizationService) {
|
||||
const displayText = {
|
||||
primary: [],
|
||||
secondary: [],
|
||||
};
|
||||
|
||||
if (!mappedAnnotations || !mappedAnnotations.length) {
|
||||
return displayText;
|
||||
}
|
||||
|
||||
const { annotationType, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.instances.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
let InstanceNumber;
|
||||
if (instance) {
|
||||
InstanceNumber = instance.InstanceNumber;
|
||||
}
|
||||
|
||||
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
|
||||
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
|
||||
const seriesText = `S: ${SeriesNumber}${instanceText}${frameText}`;
|
||||
|
||||
displayText.primary.push(`Annotation : ${annotationType}`);
|
||||
displayText.secondary.push(seriesText);
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
export default UltrasoundPleuraBLine;
|
||||
+1
@@ -12,6 +12,7 @@ const supportedTools = [
|
||||
'SplineROI',
|
||||
'LivewireContour',
|
||||
'UltrasoundDirectionalTool',
|
||||
'UltrasoundPleuraBLineTool',
|
||||
'SCOORD3DPoint',
|
||||
'SegmentBidirectional',
|
||||
];
|
||||
|
||||
+18
@@ -13,6 +13,7 @@ import LivewireContour from './LivewireContour';
|
||||
import Probe from './Probe';
|
||||
import UltrasoundDirectional from './UltrasoundDirectional';
|
||||
import SegmentBidirectional from './SegmentBidirectional';
|
||||
import UltrasoundPleuraBLine from './UltrasoundPleuraBLine';
|
||||
|
||||
const measurementServiceMappingsFactory = (
|
||||
measurementService: MeasurementService,
|
||||
@@ -296,6 +297,23 @@ const measurementServiceMappingsFactory = (
|
||||
},
|
||||
],
|
||||
},
|
||||
UltrasoundPleuraBLine: {
|
||||
toAnnotation: UltrasoundPleuraBLine.toAnnotation,
|
||||
toMeasurement: csToolsAnnotation =>
|
||||
UltrasoundPleuraBLine.toMeasurement(
|
||||
csToolsAnnotation,
|
||||
displaySetService,
|
||||
cornerstoneViewportService,
|
||||
_getValueTypeFromToolType,
|
||||
customizationService
|
||||
),
|
||||
matchingCriteria: [
|
||||
{
|
||||
valueType: MeasurementService.VALUE_TYPES.POLYLINE,
|
||||
points: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return factories;
|
||||
|
||||
Reference in new issue
Block a user