vtk-dicom
0.8.17
|
Managing DICOM meta data.
The meta data can be loaded from a DICOM file in two ways: either via the vtkDICOMReader class (which also reads the image data), or via the vtkDICOMParser class (which reads only the meta data). These classes store the meta data in a vtkDICOMMetaData object, which provides storage for all of the files in the DICOM series. The Get() method for this class, which returns the value of a DICOM attribute, accepts a file index as a parameter. This allows one to choose the file in the series for which to get the attribute.
Attributes are returned as a vtkDICOMValue object, which has methods such as AsDouble(), AsInt() or AsString() that allow conversion of the value to various C++ types. If the DICOM file does not have the requested attribute, then the returned value will be empty and "value.IsValid()" will return "false". Calling AsString() on an invalid value will return the empty string, and likewise calling AsInt() or AsDouble() on an invalid value will return 0.
As discussed in the Overview, a "file index" can be used when retrieving an attribute from vtkDICOMMetaData. It is important to note that this is a file index and not a slice index. The slices are sorted by spatial location, which might be different from the file order. Furthermore, a single DICOM file might actually contain multiple slices, with each slice stored in a different frame within the file.
The reader provides an array, the FileIndexArray, that can be used to convert a "slice index" to a file index. It also provides a FrameIndexArray that can be used to convert a slice index to a frame number. Together, these can be used regardless of whether the there was one slice per file, or one slice per frame in a multi-frame file: the vtkDICOMMetaData object provides a Get() method that takes both a file index and a frame index, along with the tag of the attribute to be inspected.
As a caveat, for multi-frame files, the example given above assumes that the meta data contains a per-frame ImagePositionPatient attribute. This is the case for enhanced multi-frame CT and MRI files, but not for multi-frame nuclear medicine files. Whenever retrieving meta data from a DICOM image, it is wise to consult the DICOM standard to see how the attributes are defined for the various modality-specific IODs (information object descriptions).
In the example in the previous section, the fileMap->GetComponent() method was called with two arguments, but the second argument was set to zero. If the vtkDICOMReader assigned a vector dimension to the data, then the the vtkImageData will have multiple scalar values in each voxel. For instance, the first component in each voxel may have come from a file that provided the real component of a complex-valued image, while the second component from a file that provided the imaginary component. In this case, one would do the following to retrieve the meta data from the "imaginary" file:
If the data has a time dimension and the reader's TimeAsVectorOn() method was called, then the components of each voxel can correspond both to a specific time slot, and to a specific vector component. To make the situation even more complicated, each pixel in the DICOM files might be an RGB pixel and therefore have three components as given by the SamplesPerPixel attribute in the meta data.
The number of components in the FileIndexArray and FrameIndexArray is equal to the vector dimension, and if TimeAsVectorOn() was called, then the vector dimension will include the time dimension. The FileIndexArray and FrameIndexArray do not have components that correspond to the individual R,G,B components in RGB images, since the R, G, and B components will always have the same meta data because they always come from the same file and frame.
The following strategy is recommended for accessing per-component meta data in multi-dimensional images:
DICOM meta data can be nested. For example, nesting is used to store per-frame meta data for enhanced multi-frame DICOM files. In order to make it easy to access nested attributes, the vtkDICOMTagPath class describes the full path to a nested attribute.
This is rather verbose, so a more convenient method for accessing per-frame data is provided for enhanced multi-frame files. You can give the frame index after the file index, in which case the Get() method will perform a search for the attribute without requiring a full path.
The vtkDICOMMetaDataAdapter class can also be used to access enhanced multi-frame files as if each frame was a separate file.
The vtkDICOMMetaData object also provides iterator-style access to the data elements. This is useful, for instance, when you want to iterate through all of the elements in the meta data in sequential order. It is also useful if you want to check which attributes vary between files in the series.
You might be surprised by the PerInstance check, but it is necessary due to the fact that vtkDICOMMetaData holds the meta data for an entire series of DICOM files. Most attributes are the same across the series, but a few attributes vary from one file to the next. These per-file attributes are identified when the file is read by vtkDICOMReader.
When iterating through the data elements in the meta data, as described in the previous section, it can be useful to get information about the meaning of the data elements that are encountered. Complete information can only be provided by the DICOM standards documents themselves, but the vtkDICOMDictionary can at least provide a summary of what kind of data to expect for a given attribute.
The vtkDICOMDictionary class provides information for attributes that are described in the DICOM standard, as well as information for private attributes defined by medical device manufacturers. Every DICOM file is likely to have a mix of standard attributes and private attributes. Fortunately, it is easy to tell the difference between the two: private attributes always use a tag with with an odd group number, while the DICOM standard only uses even group numbers. The lookup of private tags requires the name of the private dictionary.
Because private tags are not registered with any central authority, there is no guarantee that they are unique. Instead, each private group within a DICOM file contains 240 blocks (each with 256 elements) that can be be individually reserved for elements belonging to a specific private dictionary. The details of how this is done are described in Part 5, Section 7.8 of the DICOM standard.
The result of this is that private tags are of the form (gggg,xxee) where "xx" is a hexadecimal value between 10
and ff
that identifies the block that was used to store the attribute. The tricky thing is that this value can vary from one DICOM file to the next, though it is usually consistent within a single series. Some people are surprised by this, because the first block (i.e. 10
) is the only block that is used in most files.
To ensure that you are looking for private attributes in the correct location (i.e. within the correct block), you must resolve each private tag before using it.