Skip to content

Instantly share code, notes, and snippets.

@tbttfox
Created April 2, 2019 22:32
Show Gist options
  • Save tbttfox/0fc8dbe284f8dcd143cb507658a6fd8d to your computer and use it in GitHub Desktop.
Save tbttfox/0fc8dbe284f8dcd143cb507658a6fd8d to your computer and use it in GitHub Desktop.
address based complete imathnumpy
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)
@tbttfox
Copy link
Author

tbttfox commented Sep 2, 2020

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)

@digitalillusions
Copy link

Right, thanks for the response though. My next approach is then to just manually implement the missing bindings in imathnumpy.cpp.

@tbttfox
Copy link
Author

tbttfox commented Sep 3, 2020

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.

@digitalillusions
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment