DICOM Basics using Java - Push Operations (C-STORE)


This article is part of my series of articles on the DICOM standard that I am currently working on (a number of them have already been completed). If you are totally new to DICOM, please have a quick look at my earlier article titled “Introduction to the DICOM Standard” for a quick introduction to the standard. It may also be useful to look at my other tutorials that have been completed so far to get up to speed on a number of topics including DICOM Encoding, SOPs and IODs. My DICOM networking-related tutorials on DICOM Verification and on DICOM Associations should also provide some foundations to help understand the material that is covered here. This tutorial also assumes that you know the basics of Java or any equivalent object-oriented language such as C# or C++. A basic understanding of networking will also be useful to have but is not mandatory.

Introduction

In this tutorial, we are going to look at a DICOM composite service called C-STORE that helps push data to a remote server for storage (see my tutorial on DICOM Associations if you want to understand what 'composite' and 'normalized' services are). We are going to explore how to build a C-STORE SCU client that can perform such an operation.

The PixelMed Java DICOM Toolkit - Quick Overview

For the purposes of illustrating many aspects of DICOM that I plan to cover in this tutorial series, I will be using a DICOM toolkit called PixelMed Java DICOM Toolkit. This is a completely stand-alone DICOM toolkit that provides functionality for DICOM file and directory processing, image viewing as well as DICOM networking-related operations. This toolkit is completely free for both commercial or non-profit use. It is well documented and also has a small discussion forum and mailing list for users. The list of features contained within this toolkit is quite comprehensive. Please keep in mind that the use of this toolkit in my tutorial does not in anyway imply my official endorsement of it for implementing a production application. Every situation is unique, and only you are ultimately in the best position to decide that. This article is also not meant to be a tutorial on this toolkit, and my focus here is simply to tie DICOM theory to what a practical (although simple) implementation might look like. So, if your goal is to learn how to use the PixelMed library, I would encourage you to visit its website or check out the discussion forum or StackOverflow discussion pages for any assistance.

“A good song takes on more meaning as the years pass by.” ~ Bruce Springsteen

Before We Get Started…

Much like my previous programming examples, I will use the most bare minimum code and approach to help illustrate the concepts that I cover in this tutorial. This means that the code I write here is best suited to simply show the concept that I am trying to explain and is not necessarily the most efficient code to deploy in real life and in your production application.

To get started, you will need to configure a few things on your machine including a Java development environment as well as the PixelMed toolkit before you can run the example if you want to try this out yourself.

  • Download and install the Eclipse Java IDE from here (or use any other IDE you prefer)
  • Download the PixelMed toolkit library from here
  • Ensure that the PixelMed.jar library is included in your Java project’s class path (some examples may require additonal runtime dependencies such as JAI Image IO Tools that can be found on PixelMed software download. Look for a tar compressed file called pixelmedjavadicom_dependencyrelease.YYYYMMDD.tar.bz2 or something similar)
  • You can find the source code and images used in this tutorial on GitHub
  • You can download more DICOM images from this site if you want as well

PACS Server Requirement

In addition to the tools described above, you will also need a DICOM server to execute some of the operations described in this tutorial. If you don't have access to one, you can download one of the many open source PACS servers available on the Internet. Orthanc Server is one such tool and is relatively straightforward set up and get going. Please see my article on getting started with Orthanc Server for more information.

Pushing Data for Storage using C-STORE

If you recall my tutorial on DICOM associations, you will recall that before any two DICOM devices can exchange service requests and results between each other, an association first needs to be established. During the association establishment/negotiation, several activities happen. The two devices first check to see if they are accesible and can actually 'speak' DICOM (done through “DICOM ping” which is more formally known as “C-Echo” which we saw in an earlier tutorial). They also check to see if they are allowed to communicate with one another from a DICOM security standpoint. This is done by checking whether the DICOM AE configurations have been set up on both sides. They then check to see if they support the DICOM operation that is desired often referred to as the "abstract syntax". Then, as a last step, they must check to see if they can agree on a "transfer syntax" for the information being exchanged (such as the VR encoding, compression and the byte ordering to be used). All these steps have to be completed before they proceed to performing the actual operations that are desired.

During the operational execution phase, the two DICOM peers exchange something called DICOM Message Service Elements (DIMSEs) with one another. These objects help indicate the actual operation that is required to be peformed, and they are accompanied by additional data referred to as Information Object Definitions (IODs) such as textual information or images that these operations are performed on. Together, these service elements and information objects they act on combine to form what are referred to in DICOM as Service Object Pairs (SOPs). The type of DIMSEs exchanged vary according to the type of operation being performed. For instance, when a CT scanner decides to transmit a series of images to the PACS storage server, it establishes an association first, and then send a 'C-STORE-RQ' DIMSE command followed by a CT image IOD instance that needs to be stored. The C-Store SCP responds back with a success or a failure response (using a 'C-STORE-RSP' object) indicating the result of the operation. The C-Store SCU may continue to transmit a series of more commands for every image that it requires to be store, and the process continues back and forth between the two devices along the same pattern.

DICOM Store Service Diagram

The structure of the C-Store message request and message response objects is shown below. The tables shown are screen captures from the DICOM standard part 7 document that covers message exchange fundamentals.The client may also initiate a cancel operation anytime while the store operation is in progress (using a 'C-CANCEL-RQ' command) at which time the C-STORE-SCP will then cancel its store operation and return a status of 'cancelled'. Please see the official DICOM documentation for more details as the specification is huge and I can only provide an overview in this article.

DICOM Store Request and Response

“To realise the soothing power of simple life is to find the secret of happiness!” ~ Mehmet Murat ildan

Example of C-STORE Operation

Let us have quick look at some code now. Here, I use the StorageSOPClassSCU class provided by the PixelMed toolkit. There are many overloads for the constructors for this class including those that . I use the one that is hopefully the simplest for you to understand what happens during the C-STORE operation. Please explore the PixelMed documentation for more information.

    package com.saravanansubramanian.dicom.pixelmedtutorial;

    import com.pixelmed.dicom.SetOfDicomFiles;
    import com.pixelmed.network.MultipleInstanceTransferStatusHandler;
    import com.pixelmed.network.StorageSOPClassSCU;

    public class DicomCStoreFunctionalityDemo {
        
        public static void main(String arg[]) {
            
            try {
                
                
                String dicomFile1 = "C:\\SaravananWebsiteWork\\javatutorialsproject\\sample_images\\MR-MONO2-16-head";
                String dicomFile2 = "C:\\SaravananWebsiteWork\\javatutorialsproject\\sample_images\\Saravanan.dcm";
                String dicomFile3 = "C:\\SaravananWebsiteWork\\javatutorialsproject\\sample_images\\CT-MONO2-16-brain";
                String dicomFile4 = "C:\\SaravananWebsiteWork\\javatutorialsproject\\sample_images\\CT-MONO2-16-ankle";
                
                SetOfDicomFiles dicomFilesToTransmit = new SetOfDicomFiles();
                dicomFilesToTransmit.add(dicomFile1);
                dicomFilesToTransmit.add(dicomFile2);
                dicomFilesToTransmit.add(dicomFile3);
                dicomFilesToTransmit.add(dicomFile4);
                
                System.out.println("Initiating C-STORE operation...");
                
                //note, behind the scenes this operation transmits the files one by one even it appears to be 'batched'
                // there is a constructor overload that takes just one file at a time. See PixelMed documentation
                //the API construct is a bit different in PixelMed for those you are used to invoking a 'command' method
                //You can also pass an association in one of the overloads and listen to events. See my tutorial on it
                new StorageSOPClassSCU("localhost", 
                        4242, 
                        "Orthanc",
                        "JavaTestClient",
                        dicomFilesToTransmit, 
                        0, //compression level
                        new OurTransferUpdateStatusHandler());
                
            }
            catch (Exception e) {
                //in real-life, do something about this exception
                e.printStackTrace(System.err);
                System.exit(0);
            }
        }
        
    }

    class OurTransferUpdateStatusHandler extends MultipleInstanceTransferStatusHandler {

        @Override
        public void updateStatus(int nRemaining,
                                int nCompleted,
                                int nFailed,
                                int nWarning,
                                java.lang.String sopInstanceUID) {
            
            System.out.println("SOP Instance ID being transferred:" + sopInstanceUID);
            System.out.println("Items in Queue:" + nRemaining);
            System.out.println("Transfers Completed:" + nCompleted);
            System.out.println("Failed Transmits:" + nFailed);
            System.out.println("Warnings:" + nWarning);
            
        }
        
    }

“We are not rich by what we possess but by what we can do without” ~ Immanuel Kant

Results of running the code example is shown below. All these images were successfully pushed to the Orthanc Server running locally on my machine. Sending these images again will have no effect as the server simply ignore them as they have the same SOP instance UID and therefore there is no need to process them again. Remember, DICOM images once modified can no longer have the same SOP instance UID. Sometimes, if the file you are transmitting already exists on the C-STORE SCP, you may receive either a warning or an error, and this is perfectly okay and you should design your software to continue to transmit the rest of the images and not halt the operation entirely as a result of a single error. Please see DICOM documentation for more guidance and tips on this aspect


Initiating C-STORE operation...

SOP Instance ID being tansferred:1.3.6.1.4.1.5962.99.1.4024.1631.1524442889034.1.1.2322.3232.2234
Remaining in queue:3
Completed:1
Failed Transmits:0
Warnings:0

SOP Instance ID being tansferred:2.16.840.1.113662.2.1.4519.41582.4105152.419990505.410523251
Remaining in queue:2
Completed:2
Failed Transmits:0
Warnings:0

SOP Instance ID being tansferred:1.2.840.113619.2.1.2411.1031152382.365.1.736169244
Remaining in queue:1
Completed:3
Failed Transmits:0
Warnings:0

SOP Instance ID being tansferred:1.2.840.113619.2.1.3352.1015047400.4.3.834485381
Remaining in queue:0
Completed:4
Failed Transmits:0
Warnings:0

C-STORE operation completed successfully...

Testing Tools for DICOM Store Operations

When you need to troubleshoot store operations in DICOM, it will helpful to use one of the many useful DICOM testing tools out there. The one that I have used in the past and have liked is DCMTK. The toolkit comes with many standalone testing utilities that help you test various aspects related to DICOM processing through a command line interface. The storescu command is the one that I will use here to push a local DICOM file to Dr. Dave Harvey's free online DICOM test server provided here. The remote server is listening to requests on port 104 in this example. The console output generated when running this command is shown below for reference.


C:\SaravananDicomTestingTools\DCMTK-3.6.5-Win64-Dynamic\bin>storescu.exe -aet OurStoreScu "www.dicomserver.co.uk" 104 -aec MEDCONNECTIONS C:\DICOMImages\1.2.826.0.1.3680043.11.106\2.25.101703809854595919801950834747690813074.dcm -v
I: checking input files ...
I: Requesting Association
I: Association Accepted (Max Send PDV: 65524)
I: Sending file: C:\Junk\DICOMImages\1.2.826.0.1.3680043.11.106\2.25.101703809854595919801950834747690813074.dcm
I: Converting transfer syntax: Little Endian Explicit -> Little Endian Explicit
I: Sending Store Request (MsgID 1, OPb)
XMIT: .........................................................................................................................................................................
I: Received Store Response (Unknown Status: 0x111)
I: Releasing Association

Conclusion

This concludes the article on how the C-STORE operation works in DICOM. This is one of the most important operations in DICOM, and is often used to transmit DICOM images from MRIs, CTs and other modalities to PACS serves and other remote destinations. In my next tutorial in this series on the DICOM standard, I will cover how other composite operations including "C-GET" and "C-MOVE" employ the "C-STORE" as a 'sub-operation' behind the scenes during image retrieval operations as well. See you then!