Search code examples
pythonc++dicomgdcm

How to use the DICOM LUT decoder with gdcm in Python - passing buffers


I need to use the GDCM for converting DICOM images to PNG-format. While this example works, it does not seem to take the LUT into account and thus I get a mixture of inverted/non-inverted images. While I'm familiar with both C++ and Python I can't quite grasp the black magic inside the wrapper. The documentation is purely written in C++ and I need some help in connecting the dots.

The main task

Convert the following section in the example:

def gdcm_to_numpy(image):
    ....
    gdcm_array = image.GetBuffer()
    result = numpy.frombuffer(gdcm_array, dtype=dtype)
    ....

to something like this:

def gdcm_to_numpy(image):
    ....
    gdcm_array = image.GetBuffer()
    lut = image.GetLUT()
    gdcm_decoded = lut.Decode(gdcm_array)
    result = numpy.frombuffer(gdcm_decoded, dtype=dtype)
    ....

Now this gives the error:

NotImplementedError: Wrong number or type of arguments for overloaded function 'LookupTable_Decode'.
  Possible C/C++ prototypes are:
    gdcm::LookupTable::Decode(std::istream &,std::ostream &) const
    gdcm::LookupTable::Decode(char *,size_t,char const *,size_t) const

From looking at the GetBuffer definition I guess the first parameter is the assigned variable bool GetBuffer(char *buffer) const;. I guess that the latter 4-argument version is the one I should aim for. Unfortunately I have no clue to what the size_t arguments should be. I've tried with

gdcm_in_size = sys.getsizeof(gdcm_array)
gdcm_out_size = sys.getsizeof(gdcm_array)*3
gdcm_decoded = lut.Decode(gdcm_out_size, gdcm_array, gdcm_in_size) 

also

gdcm_in_size = ctypes.sizeof(gdcm_array)
gdcm_out_size = ctypes.sizeof(gdcm_array)*3
gdcm_decoded = lut.Decode(gdcm_out_size, gdcm_array, gdcm_in_size) 

but with no success.

Update - test with the ImageApplyLookupTable according to @malat's suggestion

...
lutfilt = gdcm.ImageApplyLookupTable();
lutfilt.SetInput( image );
if (not lutfilt.Apply()):
    print("Failed to apply LUT")

gdcm_decoded = lutfilt.GetOutputAsPixmap()\
    .GetBuffer()

dtype = get_numpy_array_type(pf)
result = numpy.frombuffer(gdcm_decoded, dtype=dtype)
...

Unfortunately I get "Failed to apply LUT" printed and the images are still inverted. See the below image, ImageJ suggests that it has an inverting LUT.

Comarison of the two images


Solution

  • As a simple solution, I would apply the LUT first. In which case you'll need to use ImageApplyLookupTable. It internally calls the gdcm::LookupTable API. See for example.

    Of course the correct solution would be to pass the DICOM LUT and convert it to a PNG LUT.


    Update: now that you have posted the screenshot. I understand what is going on on your side. You are not trying to apply the DICOM Lookup Table, you are trying to change the rendering of two different Photometric Interpration DICOM DataSet, namely MONOCHROME1 vs MONOCHROME2.

    In this case you can change that using software implementation via the use of: gdcm.ImageChangePhotometricInterpretation. Technically this type of rendering is best done using your graphic card (but that is a different story).