0

I would like to know how to read a HDR image (.hdr) by obtaining pixel values in the RGBE format quickly and efficiently in Python.

These are somethings I tried:

    import imageio
    img = imageio.imread(hdr_path, format="HDR-FI")

alternatively:

    import cv2
    img = cv2.imread(hdr_path, flags=cv2.IMREAD_ANYDEPTH)

This read the image, but gave the values in a RGB format.

How do you obtain the 4rth channel, the "E" channel for every pixel, without altered RGB values? I would prefer a solution involving only imageio, as i am restricted to use only that module.

Coder Alpha
  • 157
  • 1
  • 10
  • Two questions: (a) Do you have an example image you can share so I can test a full example? (b) What is your `img[0].dtype`? – FirefoxMetzger May 24 '21 at 19:20
  • a) HDR images can be downloaded via many websites... (this is a website: https://all-free-download.com/free-photos/room-hdr.html); b) img[0].dtype = float32@FirefoxMetzger – Coder Alpha May 29 '21 at 07:11
  • Thanks for the clarification. (a) I asked for the image, because sometimes the problem only exists with that specific image, e.g., because of the image's metadata. (b) Since your image is already a 32-bit floating-point, it should already be in RGBE format. What made you conclude that it isn't? Edit: Well technically, it is an expanded version of RGBE, but that is an implementation detail. – FirefoxMetzger May 29 '21 at 12:10
  • When i use imageio or cv2 to read ANY hdr image, it gives me a numpy.ndarray of dimensions image_width x image_height x 3, and the inner-most lists all have the length of 3, which hold the respective RGB values of the given pixel, and each channel has values from 0 and above, because RGB values in the range [0,1] in the pixel have somehow been "exponentialized" using the E channel. If i parse the numpy array manually and split the channels into an E channel, it takes too much time... So i need a direct way to get the E channel and RGB values in the range [0,1] quickly. @FirefoxMetzger – Coder Alpha May 30 '21 at 06:14

1 Answers1

1

If you prefer the RGBE representation over the float representation you can convert between the two

def float_to_rgbe(image, *, channel_axis=-1):

    # ensure channel-last
    image = np.moveaxis(image, channel_axis, -1)

    max_float = np.max(image, axis=-1)
    
    scale, exponent = np.frexp(max_float)
    scale *= 256.0/max_float

    image_rgbe = np.empty((*image.shape[:-1], 4)
    image_rgbe[..., :3] = image * scale
    image_rgbe[..., -1] = exponent + 128

    image_rgbe[scale < 1e-32, :] = 0
    
    # restore original axis order
    image_rgbe = np.moveaxis(image_rgbe, -1, channel_axis)

    return image_rgbe

(Note: this is based on the RGBE reference implementation (found here) and can be further optimized if it actually is the bottleneck.)

In your comment, you mention "If i parse the numpy array manually and split the channels into an E channel, it takes too much time...", but it is hard to tell why that is the case without seeing the code. The above is O(height*width), which seems reasonable for a pixel-level image processing method.

FirefoxMetzger
  • 2,880
  • 1
  • 18
  • 32