pythonnumpydata-conversionopenexr

How to convert float16 to uint8 in Python for EXR files


I'm using OpenEXR to read EXR files in Python. I have R, G and B channels with Half data (float16). Using Numpy, I tried, unsuccessfully to convert the data from float16 to uint8 (0-255 colors).

        rCh = getChanEXR(imageFile, 'R','HALF')
        rCh = np.array(rCh).astype('uint8')

So, I put the R channel pixel values to a variable rCh. Then I convert the array.array to an np.array so that I can use the astype method to convert it to uint8. I am new to this, so I clearly do not have it right as all values become 0. Originally, the values are like this: 0.0, 2.9567511226945634e-14, 1.2295237050707897e-10 etc.

In addition to the float16 values, I also have some regular float values that need to be normalized. I think I need to normalize the float16 values before they can be set in a range from 0-255.

Any ideas? Thank you.

Adding the code for the def mentioned in here getChanEXR (just a custom def based on code from the python OpenEXR docs for getting channel data.

def getChanEXR(curEXRStr, curChannel, dataType):
    #import OpenEXR, Imath, array
    pt = 'none'
    if dataType == 'HALF':
        pt = Imath.PixelType(Imath.PixelType.HALF)
    if dataType == 'FLOAT':
        pt = Imath.PixelType(Imath.PixelType.FLOAT)
    if dataType == 'UINT':
        pt = Imath.PixelType(Imath.PixelType.UINT)
    chanstr = OpenEXR.InputFile(curEXRStr).channel(curChannel, pt)
    chan = array.array('f', chanstr)
    return chan

Solution

  • I haven't got much experience with array.array but I believe you can convert it to a numpy float array so it's a bit easier to work with:

    rCh = np.asarray(rCh, dtype=np.float)
    

    If your data is normalized in [0,1] multiply it by 255 before the conversion:

    rCh = np.asarray(rCh * 255, dtype=np.uint8)
    

    I believe it's truncating away the fractional part though. Manually rounding it should be safer? (not so sure, see discussion in the comments, I believe the correct approach will be dithering here, but I guess the matter deserves better research regarding your specific use case)

    rCh = np.asarray(np.around(rCh * 255), dtype=np.uint8)
    

    If it's not normalized you could just do

    rCh -= rCh.min()
    rCh /= rCh.max()
    

    And then convert it to 8bits

    rCh = np.asarray(rCh * 255, dtype=np.uint8)