Skip to main content
Version: Version 3.3 - Segmentation Support 🚧

Measurement Service

Overview​

MeasurementService handles the internal measurement representation inside OHIF platform. Developers can add their custom sources with mappers to enable adding measurements inside OHIF. Currently, we are maintaining CornerstoneTools annotations and corresponding mappers can be found inside the cornerstone extension. However, MeasurementService can be configured to work with any custom tools given that its mappers is added to the MeasurementService. We can see the overall architecture of the MeasurementService below:

services-measurements

Events​

There are seven events that get publish in MeasurementService:

EventDescription
MEASUREMENT_UPDATEDFires when a measurement is updated
MEASUREMENT_ADDEDFires when a new measurement is added
RAW_MEASUREMENT_ADDEDFires when a raw measurement is added (e.g., dicom-sr)
MEASUREMENT_REMOVEDFires when a measurement is removed
MEASUREMENTS_CLEAREDFires when all measurements are deleted
JUMP_TO_MEASUREMENTFires when a measurement is requested to be jump to

API​

  • getMeasurements: returns array of measurements

  • getMeasurement(id): returns the corresponding measurement based on the provided Id.

  • remove(id, source): removes a measurement and broadcasts the MEASUREMENT_REMOVED event.

  • clearMeasurements: removes all measurements and broadcasts MEASUREMENTS_CLEARED event.

  • createSource(name, version): creates a new measurement source, generates a uid and adds it to the sources property of the service.

  • addMapping(source, definition, matchingCriteria, toSourceSchema, toMeasurementSchema): adds a new measurement matching criteria along with mapping functions. We will learn more about source/mappers below

  • update: updates the measurement details and fires MEASUREMENT_UPDATED

  • addRawMeasurement(source,definition,data,toMeasurementSchema,dataSource = {} : adds a raw measurement into a source so that it may be converted to/from annotation in the same way. E.g. import serialized data of the same form as the measurement source. Fires MEASUREMENT_UPDATED or MEASUREMENT_ADDED. Note that, MeasurementService handles finding the correct mapper upon new measurements; however, addRawMeasurement provides more flexibility. You can take a look into its usage in dicom-sr extension.

    • source: The measurement source instance.
    • definition: The source definition you want to add the measurement to.
    • data: The data you wish to add to the source.
    • toMeasurementSchema: A function to get the data into the same shape as the source definition.
  • jumpToMeasurement(viewportIndex, id): calls the listeners who have subscribed to JUMP_TO_MEASUREMENT.

Source / Mappers​

To create a custom measurement source and relevant mappers for each tool, you can take a look at the init.js inside the cornerstone extension. In which we are registering our CornerstoneTools-v4 measurement source to MeasurementService. Let's take a peek at the simplified implementation together. To achieve this, for each tool, we need to provide three mappers:

  • matchingCriteria: criteria used for finding the correct mapper for the drawn tool.
  • toAnnotation: tbd
  • toMeasurement: a function that converts the tool data to OHIF internal representation of measurement data.
extensions/cornerstone/src/utils/measurementServiceMappings/Length.js
function toMeasurement(
csToolsAnnotation,
DisplaySetService,
getValueTypeFromToolType
) {
const { element, measurementData } = csToolsAnnotation;

/** ... **/

const {
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = getSOPInstanceAttributes(element);

const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID
);

/** ... **/
return {
id: measurementData.id,
SOPInstanceUID,
FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
label: measurementData.label,
description: measurementData.description,
unit: measurementData.unit,
length: measurementData.length,
type: getValueTypeFromToolType(tool),
points: getPointsFromHandles(measurementData.handles),
};
}

//////////////////////////////////////////

// extensions/cornerstone/src/init.js

const Length = {
toAnnotation,
toMeasurement,
matchingCriteria: [
{
valueType: MeasurementService.VALUE_TYPES.POLYLINE,
points: 2,
},
],
};

const _initMeasurementService = (MeasurementService, DisplaySetService) => {
/** ... **/

const csToolsVer4MeasurementSource = MeasurementService.createSource(
'CornerstoneTools',
'4'
);

/* Mappings */
MeasurementService.addMapping(
csToolsVer4MeasurementSource,
'Length',
Length.matchingCriteria,
toAnnotation,
toMeasurement
);

/** Other tools **/
return csToolsVer4MeasurementSource;
};