Get Alembic information with Python

SOP, DOP, Wrangle seems to be covered with material: dancer_tone5: So I would like to write Python material. The version is as of Houdini 15.5. Since Houdini is a node-based system, in other words, it allows visual programming, I think there are fewer opportunities for scripting with Python than with other 3D CG software. So where do you use Python! : punch_tone1: I'm talking about it, but after all pipeline development is the main thing. For example, if Houdini's standard dispatcher HQueue is used as standard, the rendering job will be passed to another client after the simulation of all frames is completed, but with Python, ** 1 frame simulation When you're done, you can say ** throw the render job to another client. You can also specify a Python script file with the -P option of the Mantra command, so you can render by overwriting the ** IFD rendering properties ** (for example, subpixels) that you have already exported, so subpixels. You can save the trouble of changing and outputting the IFD again. : wink: You can also use Python to read the ** object transform ** information in the Alembic file when you read it. HQueue itself is free, but it checks for licenses of Houdini Indie or higher (I don't get a license), and IFD export is only possible with the commercial version of Houdini / Engine, so it is useful information for both Apprentice users and Indie users ** Python I would like to introduce how to get information on Alembic **. : relaxed:

Houdini's Python provides a module called ** _alembic_hom_extensions **. Normally, when controlling Houdini with Python, you start with the ** hou ** module, but this ** _alembic_hom_extensions ** is independent of the ** hou ** module. In other words, no license is required if you just use this module. But it doesn't make much sense because most of the time you pass Alembic information to Houdini for processing: scream_cat: By using this module, you can mainly get the object hierarchy, object transform, and visibility of the specified Almembic file. You can also get the value of the object's user properties (in Alembic from Houdini, that's the resolution added to the camera object) and the time range of the object animation. The functions that this module has are: Read through: zzz: Let's move on to the next explanation.


python


alembicArbGeometry(abcPath, objectPath, name, sampleTime) → (value, isConstant, scope)

Returns a tuple of None or (value, isConstant, scope). The contents of this tuple are the value of the attribute, a Boolean value indicating whether the attribute is constant on the time axis, and a scope ('varying','vertex','facevarying','uniform','constant','unknown'. ')is. </ sup>

python


alembicClearArchiveCache()

Clears the internal cache of Alembic files. </ sup>

python


alembicGetArchiveMaxCacheSize()

Returns the Alembic file cache size. </ sup>

python


alembicGetCameraDict(abcPath, objectPath, sampleTime)

Returns a dictionary of camera parameters for the specified object. </ sup>

python


alembicGetCameraResolution(abcPath, objectPath, sampleTime)

Returns a tuple containing None or two floats. The first value is the Houdini Camera's X resolution. The second value is the Houdini Camera's Y resolution. Some cameras (eg Maya Camera) have no resolution, so None is returned in this case. </ sup>

python


alembicGetObjectPathListForMenu(abcPath)

Returns a tuple of strings in the format required for menu callbacks. </ sup>

python


alembicGetSceneHierarchy(abcPath, objectPath) → (object_name, object_type, (children))

Returns 3 tuples. Each tuple has the following structure: (object_name, object_type, (children)) (children) is a tuple containing child nodes. object_type contains one of the following (may include other types): --cxform constant transform node --xform animation transform node --camera camera node --Polymesh Polygon Mesh Shape Node --subdmesh Subdivision Surface Mesh Shape Node --faceset Face Set Shape Node -curves Curves shape nodes --points Points Shape Node --nupatch NuPatch shape node --unknown unknown node

python


alembicHasUserProperties(abcPath, objectPath)

Returns None if the object has no user properties. If so, returns whether the user property is constant over time. </ sup>

python


alembicSetArchiveMaxCacheSize(size)

Sets the maximum number of Alembic files that will be cached at one time. </ sup>

python


alembicTimeRange(abcPath, [objectPath=None]) → (start_time, end_time)

Returns a tuple of None or (start_time, end_time). This tuple contains the global start / end times of the archive based on the FPS information in the Alembic archive. If you specify objectPath, the start / end time of that object is calculated. Returns None if the archive is constant. </ sup>

python


alembicUserProperty(abcPath, objectPath, name, sampleTime) → (value, isConstant)

Returns a tuple of None or (value, isConstant). This tuple contains the value of the attribute and a Boolean value that indicates whether the attribute is constant over time. </ sup>

python


alembicUserPropertyMetadata(abcPath, objectPath, sampleTime)

Returns None or JSON dictionary. This JSON dictionary contains a map of user property names-> user property metadata. </ sup>

python


alembicUserPropertyDictionary(abcPath, objectPath, sampleTime)

Returns None or JSON dictionary. This JSON dictionary contains a map of user property names-> user property values. </ sup>

python


alembicUserPropertyValuesAndMetadata(abcPath, objectPath, sampleTime)

Returns None or tuple. This tuple contains two JSON dictionaries. The first dictionary contains a map of user properties and values. The second dictionary contains a map of user properties and the metadata used to interpret the first dictionary. </ sup>

python


alembicVisibility(abcPath, objectPath, sampleTime, [check_ancestor=False]) → (value, isConstant)

Returns a tuple of None or (value, isConstant). This tuple contains the visibility of the object and a Boolean value that indicates whether the visibility is constant over time. The visibility return value of 0 is hidden, 1 is visible, and -1 is the defer (depending on the visibility of the parent). </ sup>

python


getLocalXform(abcPath, objectPath, sampleTime) → (xform, isConstant, inherit)

Returns a tuple of (xform, isConstant, inherits). This tuple is a local transform, a Boolean value that indicates whether the transform is constant over time, and a Boolean value that indicates whether the node inherits (or is connected to) the parent transform. Includes. </ sup>

python


getWorldXform(abcPath, objectPath, sampleTime) → (xform, isConstant, inherit)

Returns a tuple of (xform, isConstant, inherits). This tuple is a world transform, a Boolean value that indicates whether the transform is constant over time, and a Boolean value that indicates whether the node inherits (or is connected to) the parent transform. Includes. </ sup>


I don't really use all of the above functions, so I'd like to focus only on the ones that are of interest.

python


alembicGetSceneHierarchy(abcPath, objectPath)

python


alembicTimeRange(abcPath, [objectPath=None])

python


getWorldXform(abcPath, objectPath, sampleTime)

It has decreased considerably. Let's see what we can do with these three functions. Scene.gif NetworkEditor.png I created cam1, Toy, Subnet (Pig, ShaderBall in this). Other than ShaderBall, I have set object level animation. Output this whole scene to Alembic and load it into a new scene. NetworkEditorAlembic.png It captures data in the same object hierarchy as Houdini and also animates it. But when I select the cam1 object to animate, the Animation Editor doesn't show any animation curves. If you are familiar with CHOP, you can use Object CHOP to display the animation curves sampled in Motion FX View, right? I think you can think. If the original Alembic animation data is baked and linearly interpolated, that's fine, but if it's not, I'd like to get the floating point frame values as well. So you want to get the animation curve, right? That's where the ** _alembic_hom_extensions ** module comes in. As a rough flow,

--Get the object hierarchy in Alembic and determine which object has animation information --Examine the animation range of the object --Get object transform information

You can develop various pipelines with.

How to get Alembic object hierarchy and animation type

The code is below.

python


import _alembic_hom_extensions as abc

abcPath = "C:/data/alembicFile.abc"

def expandChild(root,child,objectHierarchy,objectType):
    objectHierarchy.append(root+child[0])
    objectType.append(child[1])
    if len(child[2])==0:
        return
    else:
        return expandChild(root+child[0]+"/",child[2][0],objectHierarchy,objectType)

objectHierarchy=[]
objectType=[]    
childNodes = abc.alembicGetSceneHierarchy(abcPath, "/")[2]
for eachChildNode in childNodes:
    expandChild("/",eachChildNode,objectHierarchy,objectType)
print objectHierarchy
print objectType

python


#Output result
['/Toy', '/Toy/testgeometry_rubbertoy1', '/cam1', '/cam1/cameraProperties', '/Subnet', '/Subnet/Pig', '/Subnet/Pig/testgeometry_pighead1', '/Subnet/Pig/testgeometry_pighead1/PigFace']
['xform', 'polymesh', 'xform', 'camera', 'cxform', 'xform', 'polymesh', 'faceset']

You can see that xform has animation information for that object and cxform has no animation information.

How to get the animation range

Now let's examine the animation range of cam1.

python


import _alembic_hom_extensions as abc

abcPath = "C:/data/alembicFile.abc"
objectPath = "/cam1"
print abc.alembicTimeRange(abcPath, objectPath)

python


#Output result
(0.041666666666666664, 2.0)

The unit of the animation range output by this function is seconds. This camera has an animation range of 1 to 48 frames and captures with FPS = 24. The result of (1.0 / 24.0, 48.0 / 24.0) is displayed.

How to get camera movement, rotation, and scale values in the current frame

Now let's examine the transform of the object.

python


import _alembic_hom_extensions as abc

abcPath = "C:/data/alembicFile.abc"

objectPath = "/cam1"

sampleTime = hou.frame()/hou.fps()

#The first element of the tuple returned by getWorldXform is 16 float tuples
xform = abc.getWorldXform(abcPath, objectPath, sampleTime)[0]
#Hou to convert to matrix format.Use Matrix4
xformMatrix = hou.Matrix4(xform)
#Extract move, rotate, scale
explodedDictionary = xformMatrix.explode()
print "translate:",explodedDictionary["translate"]
print "rotate",explodedDictionary["rotate"]
print "scale",explodedDictionary["scale"]

python


#Output result
translate: [5.75595, 3.34021, 10.2852]
rotate [-11.6801, 28.4249, -1.25354e-06]
scale [1, 1, 1]
```

 You can use it in the single script above, but ** Houdini expressions can use Python as well as HScript **.
 So, next, let's add a user parameter (called Spare parameter in Houdini) to the camera node imported by Alembic as follows.
 Here, I added two Float Vector3 and prepared the Translate parameter and Rotate parameter.
 Then change the expression type to Python.
 ![UserParms.png](https://qiita-image-store.s3.amazonaws.com/0/154191/3c170758-182d-f349-6414-f89ccfb60957.png)
 In the Alembic Xform node, the parameters of File Name, Object Path, Frame, and Frame are already prepared, so I would like to use the values of these parameters to get information on camera movement and rotation.
 You can enter multiple lines of Python code using the Edit Expression dialog displayed by pressing Alt + E on the parameters, but it is troublesome to enter frequently used code one by one.
 It is convenient to register frequently used code as a custom function.
 One way to register a custom function is to place a Python file in a specific directory and put it in the path, but that would result in an environment-dependent scene file.
 I want to avoid it!
 So let's define a custom function in the scene file.

#### How to register a custom function in a scene file
 Custom functions can be registered in the Python Source Editor in the Windows menu.
 ![PythonSourceEditor.png](https://qiita-image-store.s3.amazonaws.com/0/154191/6022a0b5-fa4d-97ad-e550-6a4fe3141500.png)
 Then enter the code as shown below.
 ![PythonSourceEditorCode.png](https://qiita-image-store.s3.amazonaws.com/0/154191/6285343b-318f-656a-d176-36201529f123.png)
 This code is a function that works with parameters on an Alembic Xform node and returns the transform information for that node.


#### **`python`**
```python

#mode is"translate"Then location information,"rotate"Then rotation information,"scale"Returns scale information
#If the index is 0, it is the X component, if it is 1, it is the Y component, and if it is 2, it is the Z component.
def getAlembicTransform(mode="translate",index=0):
    import _alembic_hom_extensions as abc
    currentNode = hou.pwd()
    abcPath = currentNode.parm('fileName').eval()
    objectPath = currentNode.parm('objectPath').eval()
    sampleTime = currentNode.parm('frame').eval()/currentNode.parm('fps').eval()
    xform = abc.getWorldXform(abcPath, objectPath, sampleTime)[0]
    xformMatrix = hou.Matrix4(xform)
    explodedDictionary = xformMatrix.explode()

    return explodedDictionary[mode][index]
```
 After registering the code in the Python Source Editor window,
 You will be able to call the "getAlembicTransform" custom function as follows:
 ![ParameterExpression.png](https://qiita-image-store.s3.amazonaws.com/0/154191/380f944f-fa7b-6f00-98e3-c6ffd7d9344a.png)
 Left-click on the parameter name to see if the value is correct.
 ![ParameterExpressionEval.png](https://qiita-image-store.s3.amazonaws.com/0/154191/c8e5cc41-04e2-4412-55ac-3d802a64680b.png)

 Now you can see the animation curve of the Alembic object in the Animation Editor.
 ![AnimationCurve.png](https://qiita-image-store.s3.amazonaws.com/0/154191/e4697ace-c750-d3b1-7f2a-64ff75d139f7.png)


 You have now included the Alembic transform information in the parameter value.

 That's it. : hugging:


Recommended Posts

Get Alembic information with Python
Get property information by scraping with python
Get date with python
[Python] Get Python package information with PyPI API
Get CPU information of Raspberry Pi with Python
Python script to get note information with REAPER
Get country code with python
Get Twitter timeline with python
Get Youtube data with python
Get information with zabbix api
Get thread ID with python
Get started with Python! ~ ② Grammar ~
Get home directory with python
Get keyboard events with python
[Python] Get user information and article information with Qiita API
Get video file information with ffmpeg-python
Get started with Python! ~ ① Environment construction ~
Get reviews with python googlemap api
Get the weather with Python requests
Get web screen capture with python
Get the weather with Python requests 2
[Python] Get economic data with DataReader
How to get started with Python
[python] Read information with Redmine API
[Small story] Get timestamp with Python
Get Qiita trends with Python scraping
[Python x Zapier] Get alert information and notify with Slack
Try Juniper JUNOS PyEz (python library) Memo 2 ~ Get information with PyEz ~
Get additional data in LDAP with python
Get html from element with Python selenium
[Note] Get data from PostgreSQL with Python
Statistics with python
Python with Go
[Python] Get the variable name with str
Twilio with Python
Map rent information on a map with python
Integrate with Python
Get BITCOIN LTP information with Raspberry PI
Play with 2016-Python
AES256 with python
Tested with Python
python starts with ()
with syntax (Python)
Get Started with TopCoder in Python (2020 Edition)
Bingo with python
Zundokokiyoshi with python
Let's get along with Python # 0 (Environment construction)
Get coincheck virtual currency information with API ♪
Excel with Python
Microcomputer with Python
[Blender x Python] Let's get started with Blender Python !!
PhytoMine-I tried to get the genetic information of plants with Python
Cast with python
[Yahoo! Weather Replacement Version] How to get weather information with LINE Notify + Python
I tried to get the movie information of TMDb API with Python
Collecting information from Twitter with Python (Environment construction)
Python hand play (let's get started with AtCoder?)
Get the latest AMI information with the AWS CLI
Get a ticket for a theme park with python
I tried to get CloudWatch data with Python
Get git branch name and tag name with python