Hanging Protocol Service
#
OverviewHangingProtocolService
is a migration of the OHIF-v1
hanging protocol engine.
This service handles the arrangement of the images in the viewport.
In short, the registered protocols will get matched with the Series that are available
for the series. Each protocol gets a point, and they are ranked. The winning protocol gets
applied and its settings run for the viewports.
You can read more about hanging protocols here. In short
with OHIF-v3
hanging protocols you can:
- Define what layout of the viewport should the viewer starts with (2x2 layout)
- Define which series gets displayed in which position of the layout
- Apply certain initial viewport settings; e.g., inverting the contrast
- Enable certain tools based on what series are displayed: link prostate T2 and ADC MRI.
#
Skeleton of A Hanging ProtocolYou can find the skeleton of the hanging protocols here:
const deafultProtocol = { id: 'defaultProtocol', locked: true, hasUpdatedPriorsInformation: false, name: 'Default', createdDate: '2021-02-23T19:22:08.894Z', modifiedDate: '2021-02-23T19:22:08.894Z', availableTo: {}, editableBy: {}, protocolMatchingRules: [], stages: [ { id: 'nwzau7jDkEkL8djfr', name: 'oneByOne', viewportStructure: { type: 'grid', properties: { rows: 1, columns: 1, }, }, viewports: [ { viewportSettings: [], imageMatchingRules: [], seriesMatchingRules: [], studyMatchingRules: [], }, ], createdDate: '2021-02-23T19:22:08.894Z', }, ], numberOfPriorsReferenced: -1,};
Let's discuss each property in depth.
id
: unique identifier for the protocolprotocolMatchingRules
: A list of criteria for the protocol along with the provided points for ranking.weight
: weight for the matching rule. Eventually, all the registered protocols get sorted based on the weights, and the winning protocol gets applied to the viewer.attriubte
: tag that needs to be matched against. This can be either Study-level metadata or a custom attribute. Learn more about custom attribute matchingconstraint
: the constraint that needs to be satisfied for the attribute. It accept avalidator
which can be [equals
,doesNotEqual
,contains
,doesNotContain
,startsWith
,endsWidth
]A sample of the matching rule is:
{ id: 'wauZK2QNEfDPwcAQo', weight: 1, attribute: 'StudyInstanceUID', constraint: { equals: { value: '1.3.6.1.4.1.25403.345050719074.3824.20170125112931.11', }, }, required: true,}
stages
: Each protocol can defined one or more stages. Each stage defines a certain layout and viewport rules. Therefore, thestages
property is array of objects, each object being one stage.viewportStructure
: Defines the layout of the viewer. You can define the number ofrows
andcolumns
. There should berows * columns
number of viewport configuration in theviewports
property. Note that order of viewports are rows first then columns.viewportSettings
: custom settings to be applied to the viewport. This can be avoi
being applied to the viewer or a tool to get enabled. We will discuss viewport-specific settings belowimageMatchingRules (comming soon)
: setting the image slice for the viewport.seriesMatchingRules
: the most important rule that matches series in the viewport. For instance, the following stage configuration will create a one-by-two layout and put the series whose description containst2
on the left, and a series with description that containsadc
on the right. (order of viewports are rows, first then columns)stages: [ { id: 'hYbmMy3b7pz7GLiaT', name: 'oneByThree', viewportStructure: { type: 'grid', properties: { rows: 1, columns: 2, }, }, viewports: [ // viewport 1 { viewportSettings: [], imageMatchingRules: [], seriesMatchingRules: [ { id: 'vSjk7NCYjtdS3XZAw', weight: 1, attribute: 'SeriesDescription', constraint: { contains: { value: 't2', }, }, required: false, }, ], studyMatchingRules: [], }, // viewport 2 { viewportSettings: [], imageMatchingRules: [], seriesMatchingRules: [ { id: 'vSjk7NCYjtdS3XZAw', weight: 1, attribute: 'SeriesDescription', constraint: { contains: { value: 'ADC', }, }, required: true, }, ], studyMatchingRules: [], }, ], },]
#
EventsThere are two events that get publish in HangingProtocolService
:
Event | Description |
---|---|
NEW_LAYOUT | Fires when a new layout is requested by the HangingProtocolService |
STAGE_CHANGE | Fires when the the stage is changed in the hanging protocols |
#
APIgetState
: returns an array:[matchDetails, hpAlreadyApplied]
:matchDetails
: matching details for the serieshpAlreadyApplied
: An array which tracks whether HPServices has been applied on each viewport.
addProtocols
: adds provided protocols to the list of registered protocols for matchingrun(studyMetaData, protocol)
: runs the HPService with the provided studyMetaData and optional protocol. If protocol is not given, HP Matching engine will search all the registered protocols for the best matching one based on the constraints.addCustomAttribute
: adding a custom attribute for matching. (see below)addCustomViewportSetting
: adding a custom setting to a viewport (initialvoi
). Below, we explain in detail how to add custom viewport settings via protocol definitions.addCustomViewportSetting
is another way to set these settings which is exposed by API
Default initialization of the modes handles running the HangingProtocolService
#
Custom AttributeIn some situations, you might want to match based on a custom attribute and not the DICOM tags. For instance,
if you have assigned a timepointId
to each study and you want to match based on it.
Good news is that, in OHIF-v3
you can define you custom attribute and use it for matching.
There are various ways that you can let HangingProtocolService
know of you custom attribute.
We will show how to add it inside the mode configuration.
const deafultProtocol = { id: 'defaultProtocol', /** ... **/ protocolMatchingRules: [ { id: 'vSjk7NCYjtdS3XZAw', weight: 3, attribute: 'timepoint', constraint: { equals: { value: 'first', }, }, required: false, }, ], stages: [ /** ... **/ ], numberOfPriorsReferenced: -1,}
// Custom function for custom attributeconst getTimePointUID = (metaData) => { // requesting the timePoint Id return myBackEndAPI(metaData)}
export default function mode() { return { id: 'myMode', /** .. **/ routes: [ { path: 'myModeRoute', init: async ({}) => { const { DicomMetadataStore, HangingProtocolService } = servicesManager.services
const onSeriesAdded = ({ StudyInstanceUID, madeInClient = false, }) => { const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID)
// Adding custom attribute to the hangingprotocol HangingProtocolService.addCustomAttribute( 'timepoint', 'timepoint', (metaData) => getFirstMeasurementSeriesInstanceUID(metaData) )
HangingProtocolService.run(studyMetadata) }
DicomMetadataStore.subscribe( DicomMetadataStore.EVENTS.SERIES_ADDED, onSeriesAdded ) }, }, ], /** ... **/ }}
#
Viewport SettingsYou can define custom settings to be applied to each viewport. There are two types of settings:
viewport settings
: Currently we support two viewport settingsvoi
: applying an initialvoi
by setting the windowWidth and windowCenterinverted
: inverting the viewport color (e.g., for PET images)
props settings
: Running commands after the first render; e.g., enabling the link tool
Examples of each settings are :
viewportSettings: [ { options: { itemId: 'SyncScroll', interactionType: 'toggle', commandName: 'toggleSynchronizer', commandOptions: { toggledState: true }, }, commandName: 'setToolActive', type: 'props', },]
and
viewportSettings: [ { options: { voi: { windowWidth: 400, windowCenter: 40, }, }, commandName: '', type: 'viewport', },]