4

I am trying to read the pixel values of an image contained in a DICOM file in my simple c++ application using the Grassroots DICOM (GDCM) library. When reading the file metadata I get the following information about the picture:

Bits allocated: 16
Bits Stored: 16
High Bit: 15
Unsigned or signed: 1
Samples pr pixel: 1
Dimensions: 2
Dimension values: 256x256
Pixel Representation: 1
SamplesPerPixel: 1
ScalarType: INT16
PhotometricInterpretation: MONOCHROME2
Pixel buffer length: 131072

Given that the image has a resolution of 256x256 and is of MONOCHROME2 type, I expected the pixel buffer length to be 256x256=65536 elements but it is in fact 131072 elements long.

If I use MATLAB instead to import the pixel data i get exactly 65536 values in the range of 0 - 850 where 0 is black and 850 is white.

When i look at the pixel buffer i get from the GDCM readout in my c++ application the pixelbuffer is 131072 elements where every even indexed element is in the range -128 to +127 and every odd indexed element is in the range 0-3. like this:

Exerpt:    

PixelBuffer[120] = -35
PixelBuffer[121] = 0
PixelBuffer[122] = 51
PixelBuffer[123] = 2
PixelBuffer[124] = 71
PixelBuffer[125] = 2
PixelBuffer[126] = 9
PixelBuffer[127] = 2
PixelBuffer[128] = -80
PixelBuffer[129] = 2
PixelBuffer[130] = 87
PixelBuffer[131] = 3
PixelBuffer[132] = 121
PixelBuffer[133] = 3
PixelBuffer[134] = -27
PixelBuffer[135] = 2
PixelBuffer[136] = 27
PixelBuffer[137] = 2
PixelBuffer[138] = -111
PixelBuffer[139] = 1
PixelBuffer[140] = 75
PixelBuffer[141] = 1
PixelBuffer[142] = 103 

What does this arrangement of values mean? Is this some kind of typical pixel representation for monochrome images? I have been "googeling image pixel structure" and similar but cant find what I am looking for. Is there some resource available that can help me understand this arrangement of values and how they correlate to each pixel?

malat
  • 12,152
  • 13
  • 89
  • 158
MrCravon
  • 53
  • 4
  • 1
    `INT16` means each pixel is an integer consisting of 2 bytes. – Christopher Oicles Sep 23 '16 at 03:54
  • Thanks, that helped a lot. What is a good way to combine them to one INT16. I thought about using something like c = (b << 8) + a; where "a" is LSB and "b" is MSB but that doesn't work when "a" is signed. Also, how is "black" considered 0 when I have negative values? I am guessing a negative value cant be considered "more black" than "black"? – MrCravon Sep 23 '16 at 05:29
  • You have the right idea, but you should treat all bytes and double-byte values as unsigned. I'm not sure what your code looks like, but you might start by reading bytes as `unsigned char` instead of `char`, or you could cast to unsigned early, or cast to unsigned late and just zero out any leftover sign-extended bits like: `unsigned short c = (static_cast(b) << 8) + (static_cast(a) & 0xff);`. That is kind of ugly, but this technique is likely to work in most cases. – Christopher Oicles Sep 23 '16 at 05:43
  • Thanks a lot, that worked great! Now i get exactly the same values that MATLAB provides. – MrCravon Sep 23 '16 at 06:14
  • 1
    Or you can reinterpret the char pointer to the data memory as `int16_t *pixels = reinterpret_cast(char_pointer);` (or use `uint16_t` for unsigned data, but if I understand that header info, you have signed data). Then `int16_t pixel = pixels[256*y + x];`. This works only when both host platform and the data have the same endianness, otherwise you must swap bytes to get the correct value, or do yours `((a<<8) | b)`, and fetch `a` `b` in correct order. – Ped7g Sep 23 '16 at 15:07
  • About negative black... Well, 16b signed type has range -32768..32767. You may want to normalize the values to some expected range? Like searching trough the data for min/max, and then use linear projection to 0..255? Depends what you want, anything is possible. – Ped7g Sep 23 '16 at 15:09

1 Answers1

1

I use this code to read 16 bit MONOCHROME2 Dicom file:

byte[] signedData = new byte[2];
        List<int> tempInt = new List<int>();
        List<ushort> returnValue = new List<ushort>();

        for (i = 0; i < PixelBuffer.Length; ++i)
        {
            i1 = i * 2;
            signedData[0] = PixelBuffer[i1];
            signedData[1] = PixelBuffer[i1 + 1];
            short sVal = System.BitConverter.ToInt16(signedData, 0);

            int pixVal = (int)(sVal * rescaleSlope + rescaleIntercept);

            tempInt.Add(pixVal);
        }

        int minPixVal = tempInt.Min();
        SignedImage = false;
        if (minPixVal < 0) SignedImage = true;

        foreach (int pixel in tempInt)
        {
            ushort val;
            if (SignedImage)
                val = (ushort)(pixel - short.MinValue);
            else
            {
                if (pixel > ushort.MaxValue) val = ushort.MaxValue;
                else val = (ushort)(pixel);
            }

            returnValue.Add(val);
        }
gege
  • 25
  • 8