2

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?

Mitchell van Zuylen
  • 3,905
  • 4
  • 27
  • 64
  • I realized I do not know if this is unique to `.hdr` images, but that's the type of image I'm trying to do this for. – Mitchell van Zuylen Jun 01 '22 at 02:01
  • Apparently there is a unit8 conversion somewhere... It seems wand-numpy introp is still a little immature. Consider doing the conversion to bytes manually, not using unit8 as intermediate data type. See https://stackoverflow.com/questions/47599012/how-to-convert-a-wand-image-object-to-numpy-array-without-opencv – LudvigH Jun 01 '22 at 08:50
  • Also, check that you have new versions of ImageMagick and Wand! – LudvigH Jun 01 '22 at 08:51
  • Thanks for the insights! Converting directly to bytes worked, but only if I specified the image format. However, I could not directly use `.reshape(image.size)`. I'll post an answer. Do you perhaps have any insight into why that happens? – Mitchell van Zuylen Jun 01 '22 at 22:15

1 Answers1

1

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')

Mitchell van Zuylen
  • 3,905
  • 4
  • 27
  • 64