Edit this page

NA-MIC Project Weeks

Back to Projects List

OHIF Viewer Plugin Architecture

Key Investigators

Description

The OHIF Viewer is a zero-footprint medical image viewer provided by the Open Health Imaging Foundation (OHIF). It is a configurable and extensible progressive web application with out-of-the-box support for image archives which support DICOMweb. We would like to make the OHIF Viewer easier to extend and customize in order to better support the workflow and feature needs of end users.

Objective

Some amount of extensibility is available via OHIF existing extensions. Our objective is specific to our overarching goal of integrating James A Petts existing Segmentation tools and UI, currently maintained here. For example, we would like it to be possible to add the following via plugins:

  1. Support for custom Commands, Hotkeys, and User Preferences
  2. Support for custom Side Panels
  3. Support for custom Toolbar Buttons
  4. Create and publish @ohif/extension-segmentation

Approach and Plan

The OHIF Viewer Platform is currently coupled to it’s various components’ dependencies and implementations. The goal is to refactor the Viewer to add it’s features and functionality via it’s own extension/module system. Then, migrate each of the existing components to do the same. Key concepts:

  1. Create a unit tested ExtensionManager responsible for extension and module registration
    • Enforces consistent module types/interfaces
  2. At the application level, indicate “active contexts” that modules can scope their behavior/availability to
    • Cornerstone Extension’s command can only run if “ACTIVE_VIEWPORT::CORNERSTONE” is an active context
  3. Command definitions are registered to a CommandsManager that other modules have access to
  4. Modules have the ability to register custom React Components
  5. Side Panels, Toolbar Buttons, and Hotkeys are primarily driven by configuration; but can specify already registered React Components for advanced customization.
  6. Utilize Extension/Module system to convert each of Jame’s segmentation features to the appropriate module.
    • Update implementation as necessary to accommodate his functionality’s needs.

Progress and Next Steps

Before Project Week:

At Project Week:

  1. The ExtensionManager was updated to support Panel Extensions
  2. The ExtensionManager was updated to support a preRegistration hook
    • Also allows for configuration to be passed to extension at Application level
  3. The ToolbarDefinition schema was updated to support nested menu items
  4. Form components were added to react-viewerbase to assist Extension Authors
  5. The existing MeasurementsTable was converted to a PanelModule
  6. Authored Segmentation functionality as an extension

Next Steps

Illustrations

Example of seg/contour plugin in 1.0

Features Visible in Example Image:

Extension Schema

An OHIF extension is a POJO (plain old JavasSript object) that has properties and methods that provide information to OHIF’s extension manager.

PreRegistration

  preRegistration(configuration = {}) {
  },

Panel Module

getPanelModule() {
    return {
      menuOptions: [
        {
          icon: 'th-list',
          label: 'Segments',
          target: 'segment-panel'
        },
        {
          icon: 'th',
          label: 'Contours',
          target: 'contour-panel'
        }
      ],
      components: [
        {
          id: 'segment-panel',
          from: 'right',
          width: '500px',
          component: SegmentationMenu // React Component
        },
        {
          id: 'contour-panel',
          from: 'right',
          width: '500px',
          component: RoiContourMenu // React Component
        }
      ],
      defaultContext: ['VIEWER']
    };
  }

Toolbar Module

getToolbarModule() {
    return {
      definitions: [
        {
          id: 'freehandRoiTools',
          label: 'ROI',
          icon: 'level',
          buttons: [
            {
              id: 'FreehandRoi',
              label: 'Draw',
              icon: 'level',
              type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
              commandName: 'setToolActive',
              commandOptions: { toolName: TOOL_NAMES.FREEHAND_ROI_3D_TOOL }
            },
            {
              id: 'FreehandRoiSculptor',
              label: 'Sculpt',
              icon: 'level',
              type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
              commandName: 'setToolActive',
              commandOptions: { toolName: TOOL_NAMES.FREEHAND_ROI_3D_SCULPTOR_TOOL }
            }
          ]
        },
        ...
      ],
      defaultContext: 'ACTIVE_VIEWPORT::CORNERSTONE'
    };
  },

Commands Module

/**
* Registers one or more named commands scoped to a context. Commands are
* the primary means for toolbar actions and actions that can be bound to hotkeys.
*/
getCommandsModule() {
  actions: {
    nextSegmentForActiveViewport: ({ viewports }) => {
      // Command implementation.
    },
    previousSegmentForActiveViewport: ({ viewports }) => {
      // Command implementations.
    },
    increaseBrushSize: () => {
      // Command implementations.
    },
    decreaseBrushSize: () => {
      // Command implementations.
    }
  }

  definitions: {
    nextSegmentForActiveViewport: {
      commandFn: actions.nextSegmentForActiveViewport,
      storeContexts: ['viewports']
    },
    previousSegmentForActiveViewport: {
      commandFn: actions.previousSegmentForActiveViewport,
      storeContexts: ['viewports']
    },
    increaseBrushSize: {
      commandFn: actions.increaseBrushSize
    },
    decreaseBrushSize: {
      commandFn: actions.decreaseBrushSize
    }
  }
  defaultContext: 'ACTIVE_VIEWPORT::CORNERSTONE'
};





Background and References