1.2.7. DICOM Data

DICOM is a widely used format for storing medical image data (CT, MRI, etc), defined by the National Electrical Manufacturers Association (NEMA) (http://medical.nema.org). The SoVRDicomFileReader( C++ | Java | .NET ) class can load a volume from a single DICOM file or from a list of DICOM files (stack of images). Loading a volume from a single DICOM file is the same as loading any other format supported by VolumeViz. Loading a volume from a list of DICOM files can be done using a list file or by specifying a list of filenames. Using a list file is the same as loading a stack of images using the SoVRRasterStackReader( C++ | Java | .NET ) except the list file should not contain any header. Each line in the list file may be a full path containing directory names or a simple file name. Simple file names are assumed to be in the same directory as the list file. The application can also specify a list of file paths using the setFilenameList() method. This is useful, for example, if the application user is allowed to select a list of files in a file selection dialog.

Unlike a raster stack, the position of each slice in the volume is determined by the location value in its file header and not by the order of the file name in the list. Also unlike a raster stack consisting of (for example) JPEG or PNG images, a DICOM file in the list may contain more than one slice of the volume. The reader handles this automatically. The first file in the list is considered the “reference” for the volume and all slices must be compatible with this one. Specifically this means that subsequent files must have the same width, height, data type and so on. The volume is oriented in 3D space in a natural way for medical images. The slice width is along the X axis, the slice height is along the Y axis and slice location values increase along the Z axis.

Note when using a list file: If the file extension is not “.dc3”, “.dic” or “.dicom” VolumeViz will not automatically select the DICOM volume reader. You can either give the list file one of the DICOM file extensions or force VolumeViz to use the DICOM reader by creating an instance of SoVRDicomFileReader( C++ | Java | .NET ) and calling the setReader() method as described in the “Non-standard extension” sub-section of Section 1.2.3, “Loading from a file”.

The volume reader will automatically get the correct volume dimensions, data type, extent (voxel size/spacing) and number of significant bits from the DICOM file header. The reader will also apply the data adjustment (if any) specified by the RescaleSlope and RescaleIntercept tags in the DICOM file header, i.e.: actualValue = slope * storedValue + intercept. As part of this process the reader will automatically convert unsigned data to the corresponding signed data type if necessary (in other words if the rescale calculation produces negative values). The application can also explicitly specify the volume data type. This allows, for example, converting float data values to more compact integer values.

[Important]

The DICOM reader only uses the rescale slope and intercept values from the first file in the list. It does not currently handle the (less common) case where each file contains different rescale values.

The SoVRDicomData( C++ | Java | .NET ) class allows the application to access DICOM specific data in the file header(s). One way to do this is to query the volume reader from the SoVolumeData( C++ | Java | .NET ) then query the SoVRDicomData( C++ | Java | .NET ) object from the reader. However the application can also create an instance of SoVRDicomData( C++ | Java | .NET ) explicitly and then open DICOM files directly using the readDicomHeader() method. SoVRDicomData( C++ | Java | .NET ) provides methods such as getSliceSpacing() to query some of the commonly used values. However it also provides the getDicomInfo() method which allows the application to query any DICOM tag (if present in the file) by its hexadecimal group and tag number.


C++
SoVRDicomFileReader* pReader = (SoVRDicomFileReader*)pVolData->getReader();
const SoVRDicomData &dicomData = pReader->getDicomData();
float rescaleSlope = dicomData.getDefaultSlope();
SbString str = dicomData.getDicomInfo( 0x28, 0x1053 );
if (! str.isEmpty())
{
  rescaleSlope = str.toFloat();
}

.NET
SoVRDicomFileReader Reader = (SoVRDicomFileReader)VolData.GetReader();
SoVRDicomData dicomData = Reader.GetDicomData();
float rescaleSlope = dicomData.GetSlope();
string str = dicomData.GetDicomInfo(0x28, 0x1053);
if (str.Length > 0)
{
  rescaleSlope = float.Parse(str);
}

Java
SoVRDicomFileReader reader = (SoVRDicomFileReader)volData.getReader();
SoVRDicomData dicomData = reader.getDicomData();
float rescaleSlope = dicomData.getSlope();
String str = dicomData.getDicomInfo((short)0x28, (short)0x1053);
if (str.length() > 0)
{
  rescaleSlope = Float.valueOf(str);
} 

Displaying a DICOM as a RAW image on a screen is easy. Locating properly a DICOM in a 3D scene may be much more tricky. This part is not dedicated to OpenInventor but helps to understand how DICOM are located in spaces.

If you are a 3D computer graphics or if you are using OpenInventor, you may already be familiar with the concept of 3D vector space. You probably heard about World Space, Camera Space, Projection Space, Object Space, etc. We will now introduce some new spaces specific to DICOM world.

By default VolumeViz will locate DICOM images so that center of image will be in (0, 0, 0):


This may be useful in case of simple image viewer but may not be adapted for application using several 3D object and locate them in space. To properly locate image in image space or in patient space, you should call the MedicalHelper( C++ | Java | .NET )::dicomAdjustVolume(volumeData) method. This method exist in two versions:


C++
dicomAdjustVolume( SoVolumeData* volume, SbBool useImagePosition = TRUE );

This version works only with axis aligned volume. When specifying useImegPosition = FALSE, volume extent will be adjusted so that origin will be in center of top left pixel as defined in DICOM Image Space. When specifying useImagePosition = TRUE, image will be located so that center of top left pixel will be located in ImagePositionPatient (0020, 0032). This correspond to locating image in Patient Space, in case of axis aligned acquisition.


C++
dicomAdjustVolume( SoVolumeData* volume, SoMatrixTransform* imgToPatient );

This version is more generic and handle non axis aligned acquisitions. This method will adjust volumeData extent so that image will be properly located in image space, ie origin in center of top left pixel, and will compute the ImgToPatient matrix in an SoMatrixTransform( C++ | Java | .NET ). This node can be added in scenegraph just before SoVolumeData( C++ | Java | .NET ) to properly locate volume in patient space.

In following examples the small trihedron is located in scenegraph world origin

Example of image position without putting SoMatrixTransform( C++ | Java | .NET ):


And same example with SoMatrixTransform( C++ | Java | .NET )