-
-
Save tbttfox/0fc8dbe284f8dcd143cb507658a6fd8d to your computer and use it in GitHub Desktop.
import numpy as np | |
import imath | |
from ctypes import ( | |
c_bool, c_byte, c_ubyte, c_short, c_ushort, | |
c_int, c_uint, c_float, c_double | |
) | |
# imathArrayType: (numpyDType, ctype, dim, dataShape) | |
_CONVERT_DICT = { | |
# vertices | |
imath.V2fArray: (c_float, 1, (2,)), | |
imath.V2dArray: (c_double, 1, (2,)), | |
imath.V2sArray: (c_short, 1, (2,)), | |
imath.V2iArray: (c_int, 1, (2,)), | |
imath.V3fArray: (c_float, 1, (3,)), | |
imath.V3dArray: (c_double, 1, (3,)), | |
imath.V3sArray: (c_short, 1, (3,)), | |
imath.V3iArray: (c_int, 1, (3,)), | |
imath.V4fArray: (c_float, 1, (4,)), | |
imath.V4dArray: (c_double, 1, (4,)), | |
imath.V4sArray: (c_short, 1, (4,)), | |
imath.V4iArray: (c_int, 1, (4,)), | |
# boxes | |
imath.Box2fArray: (c_float, 1, (2, 2)), | |
imath.Box2dArray: (c_double, 1, (2, 2)), | |
imath.Box2sArray: (c_short, 1, (2, 2)), | |
imath.Box2iArray: (c_int, 1, (2, 2)), | |
imath.Box3fArray: (c_float, 1, (2, 3)), | |
imath.Box3dArray: (c_double, 1, (2, 3)), | |
imath.Box3sArray: (c_short, 1, (2, 3)), | |
imath.Box3iArray: (c_int, 1, (2, 3)), | |
# colors | |
imath.C3cArray: (c_byte, 1, (3,)), | |
imath.C3fArray: (c_float, 1, (3,)), | |
imath.C4cArray: (c_byte, 1, (4,)), | |
imath.C4fArray: (c_float, 1, (4,)), | |
# images | |
imath.Color4cArray2D: (c_byte, 2, (4,)), | |
imath.Color4fArray2D: (c_float, 2, (4,)), | |
# matrices | |
imath.M33fArray: (c_float, 1, (3, 3)), | |
imath.M33dArray: (c_double, 1, (3, 3)), | |
imath.M44fArray: (c_float, 1, (4, 4)), | |
imath.M44dArray: (c_double, 1, (4, 4)), | |
# rotations | |
imath.QuatfArray: (c_float, 1, (4,)), | |
imath.QuatdArray: (c_double, 1, (4,)), | |
imath.EulerfArray: (c_float, 1, (3,)), | |
imath.EulerdArray: (c_double, 1, (3,)), | |
# raw numerical | |
imath.FloatArray: (c_float, 1, ()), | |
imath.FloatArray2D: (c_float, 2, ()), | |
imath.DoubleArray: (c_double, 1, ()), | |
imath.DoubleArray2D: (c_double, 2, ()), | |
imath.IntArray: (c_int, 1, ()), | |
imath.IntArray2D: (c_int, 2, ()), | |
imath.BoolArray: (c_bool, 1, ()), | |
imath.ShortArray: (c_short, 1, ()), | |
imath.SignedCharArray: (c_byte, 1, ()), | |
imath.UnsignedCharArray: (c_ubyte, 1, ()), | |
imath.UnsignedIntArray: (c_uint, 1, ()), | |
imath.UnsignedShortArray: (c_ushort, 1, ()), | |
# string pointers. | |
# Not really sure what to do with these off the top of my head | |
#imath.StringArray: (c_char_p, 1, ()) | |
#imath.WstringArray: (c_wchar_p, 1, ()) | |
# No idea what this is | |
#imath.VIntArray | |
} | |
def arrayToNumpy(imathArray): | |
""" Wrap the given imath array as a numpy array | |
The returned numpy array will share memory with the imath array. | |
""" | |
# Get the conversion data | |
tpe = type(imathArray) | |
if tpe not in _CONVERT_DICT: | |
raise TypeError("Unrecognized type: {0}".format(tpe)) | |
ctype, dim, dataShape = _CONVERT_DICT[tpe] | |
# Build the shape of the numpy array | |
if dim == 1: | |
shape = (len(imathArray),) + dataShape | |
elif dim == 2: | |
shape = imathArray.size() + dataShape | |
# Create the ctypes data pointer | |
cdata = ctype | |
for component in reversed(shape): | |
# this multiplication effectively prepends | |
# a new dimension to the ctype data shape | |
# hence the reversed loop | |
cdata = cdata * component | |
# This ctypes array will get garbage collected, but that's OK | |
# because we have the numpy array already pointing at the | |
# internal imath array data under the hood | |
cta = cdata.from_address(imathArray.address()) | |
# Return the numpy array | |
return np.ctypeslib.as_array(cta) | |
Sorry, no. We've got a custom compile at work that exposes the .address()
method.
This gist was intended to be linked in a PR over in openExr. They rejected it before I could add the link. But it seems they like the idea.
AcademySoftwareFoundation/openexr#373
More unfortunately, I don't have the time or inclination to make a PR to do it "right" like they want (I've already got the local "bad idea" custom compile)
Right, thanks for the response though. My next approach is then to just manually implement the missing bindings in imathnumpy.cpp
.
I think an even better way may be to add a method in PyImathFixedArray.h
that just returns a bytestring containing the data.
Numpy can read the bytestring, but it doesn't require linking to numpy (which is a major pain when doing this for use in Maya)
And then you can do something similar to what I have here. Except that the complexity I overlook in my PR goes away because you'll have to make an explicit copy of the data when building the string.
Right! For me the dependency on numpy is not such a bad thing, so I will probably stick to my solution for now. Thanks for the hint though, I might use it in the future. Maybe it will also help some poor souls finding this in the future :D
So I figured out how to do this without the special compile. See here:
https://gist.github.com/tbttfox/4610d77d45e301089db6c16e0b9e5654
Hey, just stumbled on this, because I got fed up with
imathnumpy
only having 3 coverters. Does this actually work? I tried to use this with python 2.7 and got something like'V3fArray' object has no attribute 'address'
.