Search code examples
pythonnumpywand

Convert python wand hdr image to numpy array and back


Python wand supports converting images directly to a Numpy arrays, such as can be seen in related questions.

However, when doing this for .hdr (high dynamic range) images, this appears to compress the image to 0/255. As a result, converting from a Python Wand image to a np array and back drastically reduces file size/quality.

# Without converting to a numpy array 
img = Image('image.hdr') # Open with Python Wand Image
img.save(filename='test.hdr') # Save with Python wand

Running this opens the image and saves it again, which creates a file with a size of 41.512kb. However, if we convert it to numpy before saving it again..

# With converting to a numpy array 
img = Image(filename=os.path.join(path, 'N_SYNS_89.hdr')) # Open with Python Wand Image
arr = np.asarray(img, dtype='float32') # convert to np array
img = Image.from_array(arr)            # convert back to Python Wand Image
img.save(filename='test.hdr') # Save with Python wand

This results in a file with a size of 5.186kb.

Indeed, if I look at arr.min() and arr.max() I see that the min and max values for the numpy array are 0 and 255. If I open the .hdr image with cv2 however as an numpy array, the range is much higher.

img = cv2.imread('image.hdr'), -1)
img.min() # returns 0
img.max() # returns 868352.0 

Is there a way to convert back and forth between numpy arrays and Wand images without this loss?


Solution

  • As per the comment of @LudvigH, the following worked as in this answer.

    img = Image(filename='image.hdr')) 
    img.format = 'rgb' 
    img.alpha_channel = False # was not required for me, including it for completion
    img_array = np.asarray(bytearray(img.make_blob()), dtype='float32')
    

    Now we much reshape the returned img_array. In my case I could not run the following

    img_array.reshape(img.shape)  
    

    Instead, for my img.size was a (x,y) tuple that should have been an (x,y,z) tuple.

    n_channels = img_array.size / img.size[0] / img.size[1]
    img_array = img_array.reshape(img.size[0],img.size[1],int(n_channels))
    

    After manually calculating z as above, it worked fine. Perhaps this is also what caused the original fault in converting using arr = np.asarray(img, dtype='float32')