Menu

#530 Possible memory de/allocation errors with SWIG + Python

3.1
wont-fix
None
5
2022-01-26
2022-01-26
XXX
No

I've been finding some odd behaviour with gdcmImage and gdcmImageChangeTransferSyntax when using Python 3.6 to 3.10 on Ubuntu 20.04 64-bit, Windows 10 64-bit and MacOS 64-bit Intel. I've tried with my own compilation and using wheels installed from pip install python-gdcm.

The script below demonstrates using this dataset, but it happens with everything I've tested.

import gdcm
from pydicom import dcmread


def convert(pixel_data):
    image = gdcm.Image()
    image.SetNumberOfDimensions(2)
    image.SetDimensions((800, 600, 1))

    pi = gdcm.PhotometricInterpretation.GetPIType(
        "PALETTE COLOR"
    )
    image.SetPhotometricInterpretation(
        gdcm.PhotometricInterpretation(pi)
    )
    image.SetTransferSyntax(
        gdcm.TransferSyntax(
            gdcm.TransferSyntax.ExplicitVRLittleEndian
        )
    )
    pixel_format = gdcm.PixelFormat(1, 8, 8, 7, 0)
    image.SetPixelFormat(pixel_format)

    elem = gdcm.DataElement(
        gdcm.Tag(0x7FE0, 0x0010),
        gdcm.VL(len(pixel_data)),
        gdcm.VR(gdcm.VR.OB),
    )
    elem.SetByteStringValue(pixel_data)
    image.SetDataElement(elem)

    # Convert to RLE
    converter = gdcm.ImageChangeTransferSyntax()
    converter.SetTransferSyntax(
        gdcm.TransferSyntax(
            gdcm.TransferSyntax.GetTSType("1.2.840.10008.1.2.5")
        )
    )
    converter.SetInput(image)
    result = converter.Change()
    if not result:
        raise RuntimeError(
            "An error occurred: ImageChangeTransferSyntax.Change() "
            "returned a failure result"
        )

    # Re-using image as the variable causes crash on Ubuntu
    # and weirdness on Windows
    # image = converter.GetOutput()
    output = converter.GetOutput()
    elem = output.GetDataElement()
    seq = elem.GetSequenceOfFragments()
    fragment = seq.GetFragment(0)
    value = fragment.GetByteValue()
    buffer = value.GetBuffer()
    print("Re-encoding to bytes")
    as_bytes = buffer.encode("utf-8", "surrogateescape")

    print("Cleaning bad objects")
    # Freeing up the objects here causes crash on Windows
    image = None
    converter = None

    print("Returning bytes")
    # If image and converter aren't freed then crash 
    # here on Windows instead
    return as_bytes


if __name__ == "__main__":
    # 1.2.840.10008.1.2.1 (Explicit VR Little Endian)
    ds = dcmread("OBXXXX1A.dcm")

    pixel_data = ds.PixelData  # bytes
    print("Converting to RLE")
    convert(pixel_data)
    print("Conversion complete")

The above works on Ubuntu, however on macOS and Windows it will crash during the "Cleaning bad objects" step.

Also, on Ubuntu I can reliably get a segfault when re-using image as the variable name (at the position indicated by "Re-using image as the variable causes crash"), and on Windows it gives a weird gdcmImage instance.

Windows will sometimes give "Windows fatal exception: code 0xc0000374" or a Python heap error as well.

Thanks

Discussion

  • Mathieu Malaterre

    • status: open --> wont-fix
    • assigned_to: Mathieu Malaterre
     
  • Mathieu Malaterre

    Dean, the above code is anti-pattern. While I agree, the python wrapping mechanism is clearly brain-dead, you cannot allocate a gdcm.Image directly in python code. You need to have a holder for it (ref mechanism). Use the Input/Ouput of a gdcm.ImageReader/gdcm.ImageWriter. See gdcm/Examples/Python section.

     
  • Mathieu Malaterre

    in your code

    converter = gdcm.ImageChangeTransferSyntax()
    # converter.SetInput(image)
    image = converter.GetInput()
    

    that should work

     
  • XXX

    XXX - 2022-01-26

    OK, thanks, I'll give that a try

     
  • XXX

    XXX - 2022-01-26

    Yep, that worked, thanks Mathieu!

     

Log in to post a comment.