pyvips
pyvips copied to clipboard
Numpy array of vips images doesn't work in later numpy versions
import pyvips, numpy as np
i = pyvips.Image.new_from_memory(np.zeros((3, 3), dtype=np.uint8), format='uchar', width=3, height=3, bands=1)
a = np.array([i], dtype=object)
In older numpy versions, up to at least 1.19.3, this works fine. In 1.20.3, I get
Traceback (most recent call last):
File "test.py", line 3, in <module>
a = np.array([i], dtype=object)
ValueError: invalid __array_struct__
As a workaround, I am able to do
a = np.zeros(dtype=object, shape=1)
a[0] = i
Hi @hroskes, thank you for reporting this.
Your sample code is working for me on ubuntu 21.10 (numpy 1.19.5). I'll see if I can get 1.20 installed.
Yes, I see a failure too for numpy 1.21.4.
I'm actually surprised this ever worked, so I think I'd tag this as not-a-bug. The numpy / pyvips interface that pyvips supports is documented here:
https://libvips.github.io/pyvips/intro.html#numpy-and-pil
So I would write:
#!/usr/bin/python3
import numpy as np
import pyvips
array = np.zeros((3, 3, 1), dtype=np.uint8)
# to pyvips
height, width, bands = array.shape
linear = array.reshape(width * height * bands)
image = pyvips.Image.new_from_memory(linear.data, width, height, bands, "uchar")
# back to an array
array = np.ndarray(buffer=image.write_to_memory(),
dtype=np.uint8,
shape=[image.height, image.width, image.bands])
This will share memory areas between pyvips and numpy, so there are no extra copy operations. pyvips has some extra logic to keep references to shared memory areas of this type, so it should also be safe.
Thanks, actually what I was trying to do was make a 1D array of dtype object containing vips images rather than an array containing their data. The reason was I wanted to do linear algebra with the nice numpy shorthand, something like this:
images = np.array([pyvips.Image.new_from_memory(np.zeros((3, 3), dtype=np.uint8), format='uchar', width=3, height=3, bands=1) for i in range(5)], dtype=object)
mixingmatrix = np.array([[1, 0, 0, 0.5, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0.5]])
mixed = mixingmatrix @ images
And then mixed is also a vips array of three images (which I'd like to combine into one image with bands, if there's a way to do that...)
Ah gotcha. Yes, I'd make a vips_to_numpy() conversion function and pass everything through that.
To combine after your @, go back to vips images again and use bandjoin:
rgb = mixed[0].bandjoin(mixed[1:])
There are some notes about bandjoin near the end of this section in the intro:
https://libvips.github.io/pyvips/intro.html#calling-libvips-operations
But the docs seem to be missing, I don't know why. I'll have a look.