Saturday, December 6, 2025

Introduction to DICOM - Chapter 4 - DICOM Objects Explained: IODs, Modules, and How to Create a Secondary Capture Image (SC IOD) in C#


Chapter 4 – DICOM Objects
In chapter 3 we’ve learned about DICOM elements. Every element is one piece of typed data with a pre defined, well specified meaning. There are thousands of DICOM elements (See chapter 6 of the standard) from the very basic attributes of patient name and birth date to the most esoteric uses of 3D surface vortices. In this chapter we’re going to collect elements into image object that is called Secondary Capture Image. 

The guys at DICOM did a lot of very good work and created well defined classes for a very detailed Data Model. This is why I always advise to dig in the DICOM standard before designing your imaging device software because there’s a very good chance that the DICOM technical committees already did the work for you and you can save a lot of expansive design time this way. 

In a way DICOM objects definitions are similar to object oriented programming. I prefer though the analog to interfaces specifications. The motivation to adhere to a standard is to enable interoperability. By detailing information object definitions (IOD’s) DICOM enables us to exchange virtual objects between applications without knowing in advance anything about the application we are going to interface with. 

In this chapter I'm going to complete chapter’s 3 examples by adding elements to the object until it’s a valid Secondary Capture Image according to the DICOM standard. Secondary Capture Image is the simplest DICOM image object. Secondary Captures is not related to any specific device. It has the very basic set of elements that a DICOM application needs in order to display and archive a DICOM image properly.

Getting Oriented using the Image Plane Module

 Just before diving into how to get oriented using the DICOM Image Plane Module, so we can put the letters right in our viewer, I want to get equipped with few more latin words so we understand what Radiologists are mumbling. If you're a Doctor, please be patient with us programmers. 

Cuts! Three major cuts we have (or planes): 

Transverse (AKA Axial) divides head from feet
Axial Cut

Sagittall Cut - right between the eyes
Sagittal Cut

and Coronal Cut - the Filet
Coronal Cut



And now that we're done with Anatomy let's do some Geometry. In this post I'm going to start explaining the use of the Image Plane Module. To refresh on Modules read chapter 4 of the DICOM Tutorial. The Image Plane module is part of the CT Image IOD and the MR Image IOD and any other object that have a frame of reference, i.e. that has a spatial coordinates system related to the patient or in other words is a 3D scan of the body.

Sunday, June 30, 2024

MODALIZER+ Video tutorials on YouTube

Here's a nice thing about blogging, it keeps a log, Ha?! And like a log, it's a peace of history and as it's been running since 2008 I find it interesting (well, it's my blog). I'm coming to the point, and the point is that MODALIZER+, a software that my team and I develop for quite a while now is kind of documented here with all its evolvement, since it started as a bunch of tests of our SDK up to its current release which I'm quite happy with. And now as there are more and more users of MODALIZER+, we started making this video tutorials series on YouTube. We have two videos now and we're working on more to add to the channel. Check them out. The first two ore more focused on the software itself and the next ones are going also to focus on DICOM in general. Stay tuned and tell me what you think in the comments.


Tuesday, July 4, 2023

Indexing DICOM files in a directory into a CSV file using PowerShell and MODALIZER-SDK

This post is continues my series on PowerShell DICOM scripting. It covers a very common task that repeats itself in all kind of forms where I have a directory full of DICOM files that piled up somewhere and needs to be indexed and processed. PowerShell scripting comes handy in these cases where there's a need to do something fast and probably change it on the fly. Of course, you will need a DICOM SDK for custom development and scripting for this one.

#!PowerShell ... jeje ... this doesn't work on widows ;-) 

## How the PowerShell DICOM Script Works

# Well, this is something very useful (I think) for anyone managing a PACS.
# Lets say you have a directory full with DICOM files you have no idea what's in them (you do, right?) and you want to scan through them.
# So, here's a little powershell script for this:

# Ok, so first we get the directory to scan as a command line parameter
param ($ScanPath)

# This is a function. It extract one element from a DICOM object and return an empty string if the element doesn't exist or doesn't have a value. We use it later on.
function Get-Value($obj, $tag
{
  $e = $obj.GetElement($tag)
  if ($e -ne $null)
  {
    if ($e.Value -ne $null)
    { 
      return $e.Value
    }
    else
    {
      return ""
    }
  }
  else
  {
    return "";
  }
}

### Customizing the Extracted DICOM Tags

# Modify this array to add more tags. The pair items are used for headers (and readabity).
$tags = @(
    0x00080016, "sop class uid",
    0x00080018, "sop instance uid",
    0x00080020, "study date",
    0x00080030, "study time",
    0x00080050, "accession number",
    0x00100010, "patient name",
    0x00100020, "patient id",
    0x00100030, "patient birth date",
    0x00100040, "patient sex",
    0x0020000d, "study Instance UID",
    0x0020000e, "$series Instance UID")

# This prints a nice CSV header line. 
# Note all the '","' to enclose values in double quotes and separate them with commas. 
function Print-Header
{
    $line = '"Filename","Status'
    for (($i=1); ($i -lt $tags.Count); ($i+=2))
    {
        $line += '","'
        $line += $tags[$i]
    }
    $line += '"'
    $line
}

# This is where the element values are extracted and one line is returned. 
# The filename is in the first column and a status is in the second. Then the values from the tags array follow.
# The status  can be "OK" or "ERROR". You will also see errors in stderr.
function Parse-DicomFile($filename)
{
    $line = '"'
    $line += $filename
    $line += '","OK'
    try {
        $obj = New-Object -ComObject rzdcx.DCXOBJ
        $obj.openFile($filename)
        for (($i=0); ($i -lt $tags.Count); ($i+=2))
        {
            $line += '","'
            $line += Get-Value $obj $tags[$i]
        }
        $line+='"'

### Handling Errors During DICOM File Parsing

    } catch { $line = '"' + $filename+ '","ERROR"' }
    return $line
}

# Here it starts. We first print the header and then scan through the folder and extract the data from every DICOM file. 
# Note ignoring directories
Print-Header
Get-ChildItem -Recurse -Path $ScanPath | Foreach-Object {    
    if ($_.Attributes -ne "Directory") {
        Parse-DicomFile $_.FullName
    }

# Enjoy!!!



Wednesday, June 28, 2023

Introduction to DICOM - Chapter 5 – Solving a DICOM Communication Problem

Today we are going to diagnose a communication problem between two DICOM applications and hopefully find the reason for the problem and solve it. I know, we didn’t even start talking about the DICOM network protocol, but hey, we’re not going to read all this 3,000 pages standard together before getting our hands dirty, right?
In this post we'll discuss:
  1. Application Entities (AE’s) – the nodes in the DICOM network and their name – AE Title
  2. Association – a network peer-to-peer session between two DICOM applications
  3. Association Negotiation – The first part of the association in which the two AE’s agree on what can and can’t be done during the Association
  4. The Verification Service using the C-ECHO command – a DICOM Service Class that is used to verify a connection, sort of application level ‘ping’.
  5. The Storage Service using the C-STORE command – a DICOM Service that allows one AE to send a DICOM object to another AE
The C in C-ECHO and C-STORE commands stands for Composite. If you remember, in chapter 4 when discussing the DICOM Data Model, we said that DICOM applications exchange composite objects (the DICOM images that we already know) that are composites of modules from different IE's where IE's are the information entities of the Normalized DICOM data model.

Here's the story:
Complaint 20123

Burt Simpson from Springfield Memorial Hospital reports that he can’t send the screen capture to the PACS. He kept clicking the green “Send” button but he always gets the same error: “Operation Failed!”. The log file Burt copied from the system is attached.
You may ask yourself, what’s the point in analyzing a log of an application that we are never going to use? Well, the truth is that all DICOM logs look alike. Actually, most DICOM applications are quite similar because DICOM software implementations have common ancient ancestors. If it’s a C library it may be the DICOM test node, CTN. If it’s Java than it might be dcm4che. Even if it's PHP or other newer languages, the libraries were transcribed and ported from the old C implementations so all DICOM logs are similar.

Query/Retrieve part II - C-MOVE


In part I of this post, I was in a meeting with a customer reviewing their workstation code and while sitting there I was thinking to myself, why should my customers have to deal with so many details of the DICOM Q/R Service when all they really want is to retrieve a study just like they would have downloaded a zip file from a web site. And thus, later, back in my office I decided to extended the DICOM Toolkit API to include a C-MOVE method that will take care of everything including the incoming association. In today’s post I’m going to use the new MoveAndStore method to talk about the DICOM Query/Retrieve service. We’ll start at the end and then work our way backwards.

C-MOVE is a DICOM command that means this: The calling AE (we) ask the called AE (the PACS) to send all the DICOM Instances that match the identifier to the target AE. 

If you're looking for a High-Performance DICOM Server with Query/Retrieve SCP check HRZ's DICOM Server solution.

Here’s how you ask a PACS to send you the DICOM images with RZDCX (version 2.0.1.9).

        public void MoveAndStore()
        {
            // Create an object with the query matching criteria (Identifier)
            DCXOBJ query = new DCXOBJ();
            DCXELM e = new DCXELM();
            e.Init((int)DICOM_TAGS_ENUM.patientName);
            e.Value = DOE^JOHN";
            query.insertElement(e);
            e.Init((int)DICOM_TAGS_ENUM.patientID);
            e.Value = @"123456789";
query.insertElement(e);
            // Create an accepter to handle the incomming association
DCXACC accepter = new DCXACC();
            accepter.StoreDirectory = @".\MoveAndStore";
Directory.CreateDirectory(accepter.StoreDirectory);
            // Create a requester and run the query
DCXREQ requester = new DCXREQ();
            requester.MoveAndStore(
                MyAETitle, // The AE title that issue the C-MOVE
                IS_AE,     // The PACS AE title
                IS_Host,   // The PACS IP address
                IS_port,   // The PACS listener port
                MyAETitle, // The AE title to send the
                query,     // The matching criteria
                104,       // The port to receive the results
                accepter); // The accepter to handle the results
        }

Behind this rather short function hides a lot of DICOM networking and when it returns we should have all the matching objects stored in the directory “.\MoveAndStore”. Readers with some practical DICOM experience probably expect me to say that it can also fail. In that case MoveAndStore throws an exception with the error code and description. Sometimes you would have to set the detailed logging on and start reading logs like we did in chapter 5 of this tutorial on DICOM networking and in some later post we will look together at a DICOM log of a Q/R transaction.

The following diagram, taken from part 2 of the DICOM standard, is commonly seen in DICOM Conformance Statements as the Data Flow diagram of the Q/R Service. These diagrams and their notation are defined by the standard in part 2 that specify the DICOM Conformance Statement – a standard document that every application vendor should provide and that describes how they implemented the standard in their product. At some point we will get to how to read and write these documents.




The vertical dashed line represents the DICOM Protocol Interface between the two applications (it is usually a single dashed line but in this example it got a bit messed up). The arrows accros the interface represents DICOM associations. The arrow points from the application that initiates the association (the requester) to the application that responds to it (the responder or accepter). The upper part of the diagram shows the control chanel where the C-MOVE request is sent and statuses are reported back by the PACS. The lower part of the diagram shows the data chanel where the DICOM instances are sent to the client.

DICOM Modality Worklist

Modality worklist (MWL) is one of DICOM’s workflow services that really make a difference. It’s the difference between grocery store workflow with notes on little pieces of paper and a true modern accountable workflow (If you are looking for a dedicated DICOM MWL solution for implementation, click here). 

Technically speaking, DICOM Modality Worklist is a task manager just like a piece of paper with short text and a check box or the tasks application on your iPhone (or Android). But for the imaging center or RAD department the advantages are enormous. The most obvious benefit is that there’s no need to reconcile all kind of misspelled names in the PACS because the patient name is no longer keyed in on the modality workstation but received electronically via the MWL query. The fact that the requested procedure is also received electronically reduces the chance for doing the wrong procedure to the wrong patient. Combined with Modality Performed Procedure step (MPPS), that allows the modality to report the task status, take ownership over the task and checkmark it as done when completed, the up side is obvious. No wonder then, that many HMO’s require Modality Worklist as a mandatory feature for every imaging device they purchase. 

The most basic abstraction of a task is a short description of what should be done and a checkbox. That’s all it takes. The MWL data model is a bit more complicated and has two levels.
The top, parent, level is called “Requested Procedure” (RP) and holds the information about the patient (name, id), the study (accession number, study instance UID) and the procedure. The procedure can be described as text using attribute (0032,1060) – “Requested Procedure Description” or in a more sophisticated manner using the (0032,1064) – “Requested Procedure Code Sequence” where static tables of codes and meanings can be used to configure and maintain procedures in the RIS or HIS.
The child level is called “Scheduled Procedure Step” (SPS) and holds attributes relevant to the modality and the actual procedure to be made. A single requested procedure may hold more than one SPS if the request is for a multi-modality study, for example a chest X-Ray and a CT or whatever combination, or if for example two protocols should be applied (e.g. Chest and Abdomen). As a modality, we will use the data in the RP to identify the patient and eliminate re-typing of the name and ID and the SPS to determine what exactly to do.