Mastering application development with DICOM integration

by | 15.07.2026

Modern medical diagnostics is often based on imaging techniques such as computed tomography (CT) and magnetic resonance imaging (MRI). The images produced in this way must be stored, exchanged and made available to medical staff in various applications.

This is precisely where Digital Imaging and Communications in Medicine (DICOM) comes into play. The standard governs the storage and exchange of information in medical image data management. For application development, however, DICOM presents a number of specific challenges and a considerable degree of complexity.

In this blog post, I would like to show where this complexity stems from and how it can be managed during development. Fully automated integration tests are a key component. Using an example, I will demonstrate how to set up a suitable test environment.

This post is not intended to provide a detailed introduction to DICOM. The standard is simply too extensive for that. Furthermore, the examples shown are primarily intended to aid understanding and are not intended to be production-ready.

The complexity of DICOM

What makes application development with DICOM integrations so complex?

Since 1993, the DICOM standard has been continuously developed whilst remaining backwards compatible. Over the same period, web development has evolved from comparatively static pages using HTML and Perl to microservice applications using TypeScript.

This backwards compatibility makes it possible to continue using medical devices reliably that have been in use for more than 30 years. At the same time, however, older concepts are retained within the standard. Improved features are often developed in parallel, rather than completely replacing existing approaches.

Furthermore, DICOM defines both its own network protocol and its own data format. The relevant expertise is less widespread amongst developers, and the available software tools are also more specialised.

In today’s application development, for example, Representational State Transfer (REST) APIs and JSON data formats are preferred. The DICOMweb standard addresses this need. It offers modern API concepts and seeks to abstract the immutable DICOM core.

Another distinctive feature is DICOM compatibility. This does not necessarily mean that a system supports the entire standard. Instead, a defined subset may suffice. This makes sense, as a CT scanner, for example, only needs to comply with those parts of the standard that are necessary to provide CT images.

DICOM file management software, on the other hand, can support CT and MRI images in various combinations – that is, neither, just one, or both – and still be DICOM-compliant.

When establishing a connection, the communicating parties negotiate their capabilities and the permitted services with one another. This is precisely where surprises can arise during system integration: two DICOM-compliant systems are not automatically compatible with one another in all the required functions.

Overcoming DICOM complexity

The greatest challenges in DICOM development therefore lie in the complexity that has built up over decades due to backwards compatibility, system integration with numerous configuration options, and the comparatively limited availability of expertise.

Fully automated testing is an effective way of addressing these challenges. Whilst it cannot eliminate complexity and boundary conditions, it can highlight them at an early stage if they lead to errors.

Even unusual customer systems and specialised configurations with corresponding field errors can be specifically simulated. At the same time, automated tests help to build up knowledge within the team. Developers with little DICOM experience can gradually familiarise themselves with the specifics of the standard and receive rapid feedback through a reliable test harness.

Integration tests are particularly important in this regard. They can be fully automated whilst simultaneously reflecting the complexity of system integration.

Unit tests, on the other hand, are only of limited suitability for many DICOM scenarios. A large proportion of the relevant tests concern network communication and image files. In traditional unit tests, both of these would usually be mocked, thereby obscuring precisely those aspects that are particularly prone to errors in DICOM.

Unit tests that work with real files are, admittedly, possible. Whether they still correspond to the traditional definition of a unit test is not a matter for discussion here. Furthermore, due to the necessary I/O operations, they lose some of their speed advantage.

Nevertheless, such tests can still be useful as a supplement. In the following example, however, we will focus on DICOM integration tests, as they are of greater significance for the challenges described.

DICOM integration tests

A simple integration test consists of a setup, the actual test execution and a verification step. This example uses .NET. For communication, we use DICOM exclusively, along with the FellowOakDicom library.

Setting up the Orthanc server

We use Orthanc, a lightweight open-source DICOM server, as our DICOM server. It also includes a web viewer, which is helpful during development. Orthanc can be easily integrated into a pipeline using Docker. The `docker-compose.yml` file:

services:
  orthanc:
    image: orthancteam/orthanc
    ports:
      - "4242:4242"
    volumes:
      - orthanc-db:/var/lib/orthanc/db
      - ./orthanc.json:/etc/orthanc/orthanc.json
    environment:
      ORTHANC__NAME: "TestOrthanc"
      ORTHANC__DICOM_AET: "ORTHANC"
volumes:
  orthanc-db:

The server loads a configuration file on start-up. This allows us to replicate any configuration from real customer systems or, as in our case, a simple test configuration.

To set up the test environment, we import a simple test file from https://github.com/robyoung/dicom-test-files. To do this, we use the C-Store service defined by the DICOM standard. With FellowOakDicom, all we need to do is create a client, load the file and send it to our Orthanc server as a CStoreRequest.

var file = DicomFile.Open("CT_small.dcm");
var request = new DicomCStoreRequest(file);
var client = DicomClientFactory.Create("localhost", 4242, false, "SCU", "SCP");
await client.AddRequestAsync(request);
await client.SendAsync();

We can use the Orthanc Server’s web UI to check straight away whether our setup is working.

{
  "Name": "Orthanc",
  "DicomAet": "ORTHANC",
  "DicomPort": 4242,
  "DicomCheckCalledAet": false,
  "DicomModalities": {
    "SCU": [ "SCU", "host.docker.internal", 11112 ]
  },
  "RemoteAccessAllowed": true
}

Verification

The test run involves calling our production code. In this case, we assume that our application has loaded a file via DICOM, enriched it with additional information, and subsequently saved it back to the DICOM server. One possible example is colour markings within the image.

In the verification step, we reload this specific CT image and can carry out a binary comparison with a reference image.

The simplest approach would be to download the image from its fixed storage location via the Orthanc Server’s REST API and use it for the comparison.

But what happens if we use a different test server that does not offer this option? Or if this very server functionality is the subject of our development?

In that case, we must load the file via DICOM. I will conclude by demonstrating how this works using the example of a CT image.

Loading a CT image via DICOM

The DICOM data model is not file-based. Instead, DICOM organises the data logically into a hierarchy. An instance is subordinate to a series. The series belongs to a study, and the study, in turn, belongs to a patient.

In our example, the instance is a CT image. However, an instance could also be a Structured Report – that is, a machine-readable report which is not itself an image.

The storage location is determined on the basis of the metadata and with the help of the C-Find service. We can then load the CT image using its instance ID.

var studyReadRequest = new DicomCFindRequest(DicomQueryRetrieveLevel.Study)
{
    Dataset = new DicomDataset
    {{ DicomTag.QueryRetrieveLevel, "STUDY" },{ DicomTag.PatientName, "Jan Kasper" },{ DicomTag.StudyInstanceUID, "" },},
};
var studyUid = string.Empty;
studyReadRequest.OnResponseReceived += async (req, resp) =>
{
    var requestSeriesRead = CreateSeriesReadRequest();
    requestSeriesRead.OnResponseReceived += async (req, resp) =>
    {
            studyUid = resp.Dataset.GetSingleValue<string>(DicomTag.StudyInstanceUID); 
    };
    await client.AddRequestAsync(requestSeriesRead);
    await client.SendAsync();
};
await client.AddRequestAsync(request);
await client.SendAsync();

For a C-Find query, we need a DICOM dataset. This specifies what data we are looking for and what information we would like to receive in response.
In our example, we are searching for the existing studies for the patient Jan Kasper. Once we have received the studies, we select the one we are looking for. For the sake of simplicity, we will use the first study in this example.

Using the same logic, we then query the series. This time, however, we narrow down the search using the study ID rather than the patient’s name. We continue this process until the image we are looking for is uniquely identified by its study ID, series ID and instance ID.

A CT image can be downloaded via the C-GET service. C-GET was added at a later stage and is therefore not supported by all systems. This is precisely where the complexity of DICOM becomes apparent once again. In some cases, it may only become apparent during system integration that this feature is not available on the target server. Furthermore, we must configure our DICOM client so that it offers the server the option to save CT files.

var cget = new DicomCGetRequest(studyUid, seriesUid, sopUid);
client.OnCStoreRequest += OnCStoreRequestAsync;
var pcs = DicomPresentationContext.GetScpRolePresentationContextsFromStorageUids(
    DicomStorageCategory.Image,
    DicomTransferSyntax.ExplicitVRLittleEndian,
    DicomTransferSyntax.ImplicitVRLittleEndian,
    DicomTransferSyntax.ImplicitVRBigEndian
);
client.AdditionalPresentationContexts.AddRange(pcs);
await client.AddRequestAsync(cget);
await client.SendAsync();

Making DICOM complexity manageable

DICOM is complex. Decades of backwards compatibility, varying functional scopes and numerous configuration options make integration a challenging task. This is precisely why it is not enough to simply assume that two DICOM-compatible systems will communicate with one another without any problems.

Fully automated integration tests provide certainty here. Using Docker, Orthanc and FellowOakDicom, a test environment can be set up in .NET in which real-world configurations can be replicated, sample data imported and key DICOM workflows automatically tested.

This does not eliminate the complexity of DICOM. However, it makes it visible at an earlier stage, reproducible and therefore more manageable. This is precisely a key prerequisite for the development of high-quality DICOM applications.

 

Notes:

You can find further information on DICOM here.

Are you looking for a team for your software development or modernisation? Then download the t2informatik Snapshot or talk to us about your project.

Would you, as an opinion leader or communicator, like to discuss DICOM? Then please feel free to share this post within your network.

Jan Kasper has published two more posts on the t2informatik Blog:

t2informatik Blog: 3D visualisation with VTK and Avalonia UI

3D visualisation with VTK and Avalonia UI

t2informatik Blog: Bridge code chronicle or a C++/CLI experience report

3D visualisation with VTK and Avalonia UI

Jan Kasper
Jan Kasper

Jan Phillip Kasper is a software developer. He started out in the embedded sector with C++ and later switched to C# because of the powerful tooling. His first technical love is unit testing, and he takes quiet delight in tracking down bizarre errors in system testing. And in his private life? He builds technical toys with his sons and even lets them play with them occasionally.

In the t2informatik Blog, we publish articles for people in organisations. For these people, we develop and modernise software. Pragmatic. ✔️ Personal. ✔️ Professional. ✔️ Click here to find out more.