-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue 45: Add ultrasound directional support to measurement service
- Loading branch information
1 parent
cd5028f
commit 4083f52
Showing
4 changed files
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
206 changes: 206 additions & 0 deletions
206
extensions/cornerstone/src/utils/measurementServiceMappings/UltrasoundDirectional.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
import SUPPORTED_TOOLS from './constants/supportedTools'; | ||
import { getDisplayUnit } from './utils'; | ||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; | ||
import { utils } from '@ohif/core'; | ||
|
||
const UltrasoundDirectional = { | ||
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, viewportId } = csToolsEventDetail; | ||
const { metadata, data, annotationUID } = annotation; | ||
|
||
if (!metadata || !data) { | ||
console.warn('Length 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, | ||
}; | ||
}, | ||
}; | ||
|
||
function getMappedAnnotations(annotation, DisplaySetService) { | ||
const { metadata, data } = annotation; | ||
const { cachedStats } = data; | ||
const { referencedImageId } = metadata; | ||
const targets = Object.keys(cachedStats); | ||
|
||
if (!targets.length) { | ||
return; | ||
} | ||
|
||
const annotations = []; | ||
Object.keys(cachedStats).forEach(targetId => { | ||
const targetStats = cachedStats[targetId]; | ||
|
||
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; | ||
const { xValues, yValues, units, isUnitless, isHorizontal } = targetStats; | ||
|
||
annotations.push({ | ||
SeriesInstanceUID, | ||
SOPInstanceUID, | ||
SeriesNumber, | ||
frameNumber, | ||
xValues, | ||
yValues, | ||
units, | ||
isUnitless, | ||
isHorizontal, | ||
}); | ||
}); | ||
|
||
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:UltrasoundDirectional'); | ||
|
||
mappedAnnotations.forEach(annotation => { | ||
const { xValues, yValues, units, isUnitless } = annotation; | ||
if (isUnitless) { | ||
columns.push('Length' + units[0]); | ||
values.push(utils.roundNumber(xValues[0], 2)); | ||
} else { | ||
const dist1 = Math.abs(xValues[1] - xValues[0]); | ||
const dist2 = Math.abs(yValues[1] - yValues[0]); | ||
columns.push('Time' + units[0]); | ||
values.push(utils.roundNumber(dist1, 2)); | ||
columns.push('Length' + units[1]); | ||
values.push(utils.roundNumber(dist2, 2)); | ||
} | ||
}); | ||
|
||
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) { | ||
if (!mappedAnnotations || !mappedAnnotations.length) { | ||
return ''; | ||
} | ||
|
||
const displayText = []; | ||
|
||
const { xValues, yValues, units, isUnitless, SeriesNumber, SOPInstanceUID, frameNumber } = | ||
mappedAnnotations[0]; | ||
|
||
const instance = displaySet.images.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})`; | ||
|
||
if (xValues === undefined || yValues === undefined) { | ||
return displayText; | ||
} | ||
|
||
if (isUnitless) { | ||
displayText.push(`${utils.roundNumber(xValues[0], 2)} ${units[0]} ${seriesText}`); | ||
} else { | ||
const dist1 = Math.abs(xValues[1] - xValues[0]); | ||
const dist2 = Math.abs(yValues[1] - yValues[0]); | ||
displayText.push(`${utils.roundNumber(dist1)} ${units[0]} ${seriesText}`); | ||
displayText.push(`${utils.roundNumber(dist2)} ${units[1]} ${seriesText}`); | ||
} | ||
|
||
return displayText; | ||
} | ||
|
||
export default UltrasoundDirectional; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,4 +12,5 @@ export default [ | |
'SplineROI', | ||
'LivewireContour', | ||
'Probe', | ||
'UltrasoundDirectionalTool', | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters