Pixel Representation

Hi,

I am reading in a dicom image via GDCM as unsigned short. The pixel representation of the imported dicom image is 0 (unsigned short). I confirmed that the component type and internal component types are USHORT. I am then writing out the image again to dicom using the gdcm writer. The outputted file has Pixel Representation of 1 (2’s complement). I didn’t check explicitly, but I assuming that the pixel data is in 2’s complement.

I would like to ensure that the outputted data is consistent with the inputted data (pixel representation =0, unsigned short), but I cannot find anyway to change the way the data is outputted, nor can I figure out why the data is written out as a 2’s complement in the first place, especially when I checked that the internal and regular component types are both USHORT.

Any help would be appreciated.

Thanks,

i found the issue. I had instantiated the reader type as signed short not unsigned. this forces the output to pixel representation =1 (2’s complement), even thought the component type and internal component types were unsigned short. When defining the reader class as an unsigned short and outputting, then the pixel representation and data is outputted as 0 (unsigned short). However, i am still confused about how the rescale slope and intercept are handled. I believe they are applied during reading, which should force the data to negative values (if intercept is -1000, which it normally is). Then upon exporting it is reversed putting the data back to unsigned short. I am not 100% sure how to handle this situation. Should i instantiate the reader as signed or unsigned? If it is unsigned, i am guessing that the rescale slope and intercept would not force the data properly to negative values. But in this case the export would have the proper pixel representation of 0 (unsigned again) but potentially be wrong when the rescale slope and intercept is reversed prior to export?

For reading, signed short seems like the right choice. Writing DICOMs has many pitfalls. Maybe @mihail.isakov, @mathieu.malaterre, or @lassoan have some suggestions?

In 3D Slicer we read into float pixel type to avoid data loss due to rescaling; and we support writing signed/unsigned char, short, int images (DICOM has float voxel type for a while, but it is not very commonly implemented in applications, so we haven’t added support for writing float type).

I would not worry too much about changing the output voxel type to signed/unsigned. The written DICOM file will be quite different from the input DICOM file anyway (different UIDs, creator software information, maybe image type and other things as well).

1 Like

i am still confused about how the rescale slope and intercept are handled. I believe they are applied during reading

Yes, they are.

You can query output pixel type, component type, etc. eg like this (for any file):

#include <itkImageFileReader.h>
#include <itkImageIOBase.h>
#include <itkImageIOFactory.h>
#include <iostream>
#include <string>

int main(int argc, char ** argv)
{
  if (argc < 2)
  {
    std::cout << "File name is required" << std::endl;
    return 0;
  }
  itk::ImageIOBase::Pointer imageIO;
  itk::IOPixelType          pixel_type{itk::IOPixelType::UNKNOWNPIXELTYPE};
  itk::IOComponentType      component_type{itk::IOComponentType::UNKNOWNCOMPONENTTYPE};
  unsigned int              component_number{};
  unsigned int              dim{};
  try
  {
    imageIO = itk::ImageIOFactory::CreateImageIO(argv[1], itk::ImageIOFactory::ReadMode);
  }
  catch (const itk::ExceptionObject & ex)
  {
    std::cout << ex.GetDescription() << std::endl;
    return 0;
  }
  if (imageIO.IsNull())
  {
    std::cout << "Can not create ImageIO for " << argv[1] << std::endl;
    return 0;
  }
  imageIO->SetFileName(std::string(argv[1]));
  try
  {
    imageIO->ReadImageInformation();
  }
  catch (const itk::ExceptionObject & ex)
  {
    std::cout << ex.GetDescription() << std::endl;
    return 0;
  }
  //
  //
  //
  pixel_type = imageIO->GetPixelType();
  component_type = imageIO->GetComponentType();
  component_number = imageIO->GetNumberOfComponents();
  dim = imageIO->GetNumberOfDimensions();
  //
  //
  //
  std::cout << "IO: " << imageIO->GetNameOfClass() << "\nImage dimensions: " << dim << "\nPixel type: ";
  switch (pixel_type)
  {
  case itk::IOPixelType::SCALAR:
    std::cout << "scalar";
    break;
  case itk::IOPixelType::RGB:
    std::cout << "RGB";
    break;
  case itk::IOPixelType::RGBA:
    std::cout << "RGBA";
    break;
  case itk::IOPixelType::OFFSET:
    std::cout << "offset";
    break;
  case itk::IOPixelType::VECTOR:
    std::cout << "vector";
    break;
  case itk::IOPixelType::POINT:
    std::cout << "point";
    break;
  case itk::IOPixelType::COVARIANTVECTOR:
    std::cout << "covariant vector";
    break;
  case itk::IOPixelType::SYMMETRICSECONDRANKTENSOR:
    std::cout << "symmetric secondrank tensor";
    break;
  case itk::IOPixelType::DIFFUSIONTENSOR3D:
    std::cout << "diffusion tensor 3d";
    break;
  case itk::IOPixelType::COMPLEX:
    std::cout << "complex";
    break;
  case itk::IOPixelType::ARRAY:
    std::cout << "array";
    break;
  case itk::IOPixelType::FIXEDARRAY:
    std::cout << "fixed array";
    break;
  case itk::IOPixelType::MATRIX:
    std::cout << "matrix";
    break;
  case itk::IOPixelType::VARIABLELENGTHVECTOR:
    std::cout << "variable length vector";
    break;
  case itk::IOPixelType::VARIABLESIZEMATRIX:
    std::cout << "variable size matrix";
    break;
  default:
    std::cout << "unknown pixel type";
    break;
  }
  std::cout << "\nComponent type: ";
  switch (component_type)
  {
  case itk::IOComponentType::UCHAR:
    std::cout << "unsigned char";
    break;
  case itk::IOComponentType::CHAR:
    std::cout << "char";
    break;
  case itk::IOComponentType::USHORT:
    std::cout << "unsigned short";
    break;
  case itk::IOComponentType::SHORT:
    std::cout << "short";
    break;
  case itk::IOComponentType::UINT:
    std::cout << "unsigned int";
    break;
  case itk::IOComponentType::INT:
    std::cout << "int";
    break;
  case itk::IOComponentType::FLOAT:
    std::cout << "float";
    break;
  case itk::IOComponentType::DOUBLE:
    std::cout << "double";
    break;
  case itk::IOComponentType::LDOUBLE:
    std::cout << "long double";
    break;
  case itk::IOComponentType::ULONG:
    std::cout << "unsigned long";
    break;
  case itk::IOComponentType::LONG:
    std::cout << "long";
    break;
  case itk::IOComponentType::ULONGLONG:
    std::cout << "unsigned long long";
    break;
  case itk::IOComponentType::LONGLONG:
    std::cout << "long long";
    break;
  default:
    std::cout << "unknown component";
    break;
  }
  std::cout << " (" << component_number << ")" << std::endl;
  return 0;
}
1 Like