16-bit and 12-bit questions

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Posts: 5
Joined: Fri, 2004-11-19, 20:06

16-bit and 12-bit questions

#1 Post by mask » Fri, 2004-11-19, 20:15

I have some questions about decoding 12- and 16-bit grayscale images.

I have a 16-bit CT scan:

Code: Select all

0028,0100,Bits Allocated=16
0028,0101,Bits Stored=16
0028,0102,High Bit=15
0028,0103,Pixel Representation=1
0028,1050,Window Center=50
0028,1051,Window Width=75
0028,1052,Rescale Intercept=0 
0028,1053,Rescale Slope=1
If I look at the pixel values with a hex editor, I can see lots of pixels with the values FC4C.
Interpreted as 16bit signed values, this is -958. (Pretty close to -1000, which is air).

I decode this file with the following:

Code: Select all

DcmFileFormat *dfile = new DcmFileFormat();
dfile->read(*m_pInputStream, EXS_Unknown, EGL_withoutGL, DCM_MaxReadLength);
E_TransferSyntax xfer = dfile->getDataset()->getOriginalXfer();
unsigned long opt_compatibilityMode = CIF_MayDetachPixelData | CIF_TakeOverExternalDataset;
m_di = new DicomImage(dfile, xfer, opt_compatibilityMode, 0 /*first frame*/, 0 /* all frames */);
now I retrieve the decoded values with:

Code: Select all

m_di->getOutputData(&myBuffer[0], myBufferSize, 16, 0);
If I analyse this data, I see that FC4C becomes 7C42, which is probably due to signed/unsigned conversion by adding 2^15.

Now comes the strange part. I try the same with a 12-bit scan:

Code: Select all

0028,0100,Bits Allocated=16
0028,0101,Bits Stored=12
0028,0102,High Bit=11
0028,0103,Pixel Representation=0
0028,0200,Image Location [ret]=32736
0028,1050,Window Center=000035 -0170
0028,1051,Window Width=000050 02700
0028,1052,Rescale Intercept=-01.024000E+03
0028,1053,Rescale Slope=001.000000E+00
In this file, the "air" pixels are encoded as 001D. This makes sense if I apply the rescale intercept of -1024.
After decoding the file with the above code, 001D becomes 01D0. It seems as if the sample values were multiplied by 16 during the 12-bit to 16-bit conversion :cry:. If I change the getOutputData call to:

Code: Select all

m_di->getOutputData(&myBuffer[0], myBufferSize, 0, 0);
the 12-bit values get padded and not multiplied. This is better, but not perfect. The application would like to treat all images as 16-bit greyscale, and the above call will not work with 8-bit scans. Also, for some reason,
2^15 was not added as with the first example.

So, my main question is: is there any way to decode various files in a uniform way? What I would really like is to get signed 16-bit values where the rescale intercept & slope have already been applied.

Thanks in advance,

Marco Eichelberg
Posts: 1293
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany

#2 Post by Marco Eichelberg » Tue, 2004-11-23, 10:11

The functions in dcmimage are intended for image presentation, not for post-processing. Since there is no such thing as signed pixel values (all graphics boards, operating systems, GUI libraries expect unsigned values), the library always produces unsigned output of the bit depth requested by the user (default is the internal bit depth of the source image), aligned to the next structure size (8/16/32 bit alignment). When you ask for a 12-bit image, the library will scale your image so that 0xFFF is white and 0x000 is black, no matter what the input range was.

Your problem is something that is completely CT specific - you want to extract Hounsfield units, which in DICOM means an image where the Modality LUT transformation has been applied, but all other transformation steps are left out. This makes certainly sense for CT applications (and is a much requested feature) but currently not possible with the dcmimage library.

In the next release, i.e. DCMTK 3.5.4, we plan to add a method

Code: Select all

const DiPixel *DicomImage::getInterData() const
which will give you access to the internal representation of the image (which is signed for the CT case) after application of the Modality LUT.
You can patch this into dcmimage.h if you like - the code just looks like this:

Code: Select all

/** get intermediate pixel data representation (read-only).
 *  This function allows to access the pixel data after they have been extracted
 *  from the DICOM data element and the modality transformation has been applied
 *  (if present and not disabled).  Please note that for monochrome images the
 *  internal representation might be signed whereas color images are automatically
 *  converted to unsigned RGB format.  Pixels are aligned to 8, 16 or 32 bits.
 ** @return pointer to intermediate pixel data representation
inline const DiPixel *getInterData() const
    return (Image != NULL) ?
       Image->getInterData() : NULL;
The interface to class DiPixel is a bit tricky, especially for color images, since this class was only intended for internal use, but it will allow you to do what you want. You will still need to consider alignment issues, though.

Posts: 5
Joined: Fri, 2004-11-19, 20:06

#3 Post by mask » Tue, 2004-11-23, 18:38

Thanks for the great explaination.
I'll give the getInterData() method a try.

Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 1 guest