天天看點

DICOM 壓縮格式讀取Howto: Accessing Compressed Data

Howto: Accessing Compressed Data

If compressed DICOM images are loaded, DCMTK most of the time does the job of decompressing the image data itself, e.g. when the user feeds it into the 

DicomImage

 class for visualization or directly calls 

chooseRepresentation()

 to change the transfer syntax. However, sometimes it may be useful to access the original, compressed pixel data of a single- or multi-frame image. This may be the case if the compressed data should be decompressed by an external library (e.g. because DCMTK does not support this kind of compression codec like for MPEG2) or if the data should be decompressed at all since it should be inserted directly into to another file.

For uncompressed pixel data usually this is done very easily by just finding the Pixel Data element in the DICOM dataset by calling DcmItem's 

findAndGetElement()

 routine and then call 

getUint8Array()

 (for example) on the resulting element to access the uncompressed raw pixel data vaules. For compressed data, however, there are some intermediate steps necessary in order to parse the underlying DICOM structures.

The reason is that for compressed data, there is a pseudo-sequence embedded into the Pixel Data element that has to be parsed before actually accessing the compressed data (and individual frames if applicable). The first pseudo-item in that sequence is always the offset table indicating at which byte position each frame actually starts within the following raw data of the Pixel Data element. However, you cannot rely on that table since it may be empty (always empty for MPEG2, for example). Within the next items so-called fragments stored. Usually, one fragment refers to exactly one frame of the image, but that is not a requirement at all. For the rest of the example, at least, this is assumed since this covers 90% of all cases.

In order to access the pixel sequence and the items it is containing, you have to deal with the Pixel Sequence within the Pixel Data element, and that is done using the 

DcmPixelSequence

 class in DCMTK. Here is a full (compiling) example how to accomplish that:

#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
#include "dcmtk/dcmdata/dctk.h" 
#include "dcmtk/dcmdata/dcpxitem.h" 

int main(int argc, char *argv[])
{
  OFCondition result = EC_Normal;
  /* Load file and get pixel data element */
  DcmFileFormat dfile;
  result = dfile.loadFile("example.dcm");
  if (result.bad())
    return 1;
  DcmDataset *data = dfile.getDataset();
  if (data == NULL)
    return 1;
  DcmElement* element = NULL;
  result = data->findAndGetElement(DCM_PixelData, element);
  if (result.bad() || element == NULL)
    return 1;
  DcmPixelData *dpix = NULL;
  dpix = OFstatic_cast(DcmPixelData*, element);
  /* Since we have compressed data, we must utilize DcmPixelSequence
     in order to access it in raw format, e. g. for decompressing it
     with an external library.
   */
  DcmPixelSequence *dseq = NULL;
  E_TransferSyntax xferSyntax = EXS_Unknown;
  const DcmRepresentationParameter *rep = NULL;
  // Find the key that is needed to access the right representation of the data within DCMTK
  dpix->getOriginalRepresentationKey(xferSyntax, rep);
  // Access original data representation and get result within pixel sequence
  result = dpix->getEncapsulatedRepresentation(xferSyntax, rep, dseq);
  if ( result == EC_Normal )
  {
    DcmPixelItem* pixitem = NULL;
    // Access first frame (skipping offset table)
    dseq->getItem(pixitem, 1);
    if (pixitem == NULL)
      return 1;
    Uint8* pixData = NULL;
    // Get the length of this pixel item (i.e. fragment, i.e. most of the time, the lenght of the frame)
    Uint32 length = pixitem->getLength();
    if (length == 0)
      return 1;
    // Finally, get the compressed data for this pixel item
    result = pixitem->getUint8Array(pixData);
    // Do something useful with pixData...
  }
  if (result.bad())
    return 1;
  else
    return 0;
}