Implementing support for loading DICOM object from custom web API

Hello,

In this post I am sharing some feedback about my experience hacking the OHIF codebase as an outside want-to-be contributor, as well as asking for guidance.

My lab develops a web backend service which stores DICOM instances. The DICOM can be queried and retrieved for over an HTTP API.

I want to implement support for using OHIF as a front-end to our service. So far, I was able to get query working but not “loading” (retrieval of the actual images from our backend to OHIF).

What Worked

It took me ~5 hours to get OHIF to be able to query our backend. I was able to create an extension by running yarn run cli create-extension, yarn run cli lin-extension ..., and making some edits to the config:

diff --git a/platform/app/public/config/default.js b/platform/app/public/config/default.js
index 97f506e70..ae01e34f6 100644
--- a/platform/app/public/config/default.js
+++ b/platform/app/public/config/default.js
@@ -24,6 +24,8 @@ window.config = {
   },
   // filterQueryParam: false,
   defaultDataSourceName: 'dicomweb',
+  // defaultDataSourceName: 'trying-chris',
+
   /* Dynamic config allows user to pass "configUrl" query string this allows to load config without recompiling application. The regex will ensure valid configuration source */
   // dangerouslyUseDynamicConfig: {
   //   enabled: true,
@@ -35,6 +37,14 @@ window.config = {
   //   regex: /.*/,
   // },
   dataSources: [
+    {
+      namespace: 'ohif-extension-fnndsc-chris.dataSourcesModule.chris',
+      sourceName: 'trying-chris',
+      configuration: {
+        friendlyName: 'PACSFiles from cube.chrisproject.org',
+        name: 'trying-chris',
+      }
+    },
     {
       namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
       sourceName: 'dicomweb',

The extension created by the template did not work out-of-the-box. I created a bug report for the problem: [Bug] New extension template outdated dependency version @ohif/i18n · Issue #3656 · OHIF/Viewers · GitHub

I followed the documentation Module: Data Source | OHIF and was able to get query working by implementing query.studies.search and query.series.search.

What Didn’t Go Well

While the documentation mentions that retrieve.series.metadata “is a crucial end point,” I did not know how to implement it because I couldn’t find the interface for the function’s return type. Another instance of missing documentation/docstring is where the documentation says “retrieve.bulkDataURI: used to render RTSTUCTURESET in the viewport” however it is not explained what RTSTUCTURESET is nor what retrieve.bulkDataURI is supposed to be.

The documentation did a good job giving an overview of the codebase organization, extensions architecture, and how extensions are used by modes. However, after another ~5 hours of reading code and reverse-engineering, I am still unable to understand how it all “comes together” at a lower level. Some (possibly misguided) questions I have include:

  • Most broadly: what functions, interfaces, and configurations do I need to implement (in other words, where is the code I need to change) in order to change how OHIF loads image data?
  • What is a DisplaySet (is it the same thing as an imageSet)? DisplaySet is mentioned in the docs for SOP Class Handler and Viewport, but what DisplaySet is/represents is not explained. Also, there’s a syntax error in the example for an SOP class handler*.
  • How are “managers” and “services” to be used by extensions? From reading the source code, I can see that it’s necessary for retrieve.series.metadata to call DicomMetadataStore.addSeriesMetadata and DicomMetadataStore.addInstances, but without a parameter type interface I don’t know what shape the parameters should have. I also found it confusing how query.studies.search and query.series.search are to simply return the data however retrieve.series.metadata should call methods of DicomMetadataStore instead of returning data.

*Discourse has a silly limitation where I can only supply 2 URLs per post…

Right now I’m stuck and can’t figure out where to go from here.

About Our Backend

Backend repo: on Github, FNNDSC/ChRIS_ultron_backEnd

For the most part, it’s a simple JSON web API. Example query:

# Search for DICOM instances where study date is "2022-12-06" and modality is "MR"
curl -u chris:chris1234 'https://cube.chrisproject.org/api/v1/pacsfiles/search/?StudyDate=2011-12-06&Modality=MR' -H 'Accept: application/json'

Example DICOM object retrieval:

curl -u chris:chris1234 'https://cube.chrisproject.org/api/v1/pacsfiles/2925/0054-1.3.12.2.1107.5.2.32.35235.2011120608042135173930307.dcm'