[Python MetaHuman Animator] Capture source depth sequence creation problem

Hello,

I am trying to automate MetaHuman Animator capture source ingestion process with a simple Python script.

In this example capture source asset is already created. Capture source type is set to “Stereo HMC Archives” and storage path is set to the root folder of the performance folders containing top and bottom image sequences, calib.json, take.json and thumbnail.json.

Running footage ingest from the Capture Manager works fine and creates depth sequence and assets.

Problem is with the Python script below.

get_takes fails if Capture Manager is not open and the capture source asset is not selected.

“LogMetaHumanCaptureSource: Error: Failed to get take. Not all requested takes are available to process”

However If I manually open Capture Manager and select capture source asset, the script partially works, it creates only assets without a depth sequence.

My question is there a way around two mentioned problems, to initialize the Capture Manager with Python and output depth sequence.

import unreal
captureSource = unreal.load_asset(‘/Game/MH_CS’)
captureSource.set_target_path(‘/Game/MH_CS_Ingested/’, ‘/Game/MH_CS_Ingested/’)
takesList = [0]
MHA_GetTakesCallback = unreal.MetaHumanCaptureSource_GetTakesCallbackPerTake()
captureSource.get_takes(takesList, MHA_GetTakesCallback)
captureSource.startup(mode=unreal.TakeIngestMode.ASYNC)

Thank you.

There are two things happening here. The first one is that you are calling startup after get_takes and you didn’t register a callback even though you are using ASYNC mode.

There are two ways of using python automation in this case, you can SYNC when calling startup, which will cause the function to wait until all takes are ready to be ingested or you can register a callback. Here is a small example of the two approaches

import unreal

captureSource = unreal.load_asset(‘/Game/MH_CS’)
captureSource.set_target_path(‘/Game/MH_CS_Ingested/’, ‘/Game/MH_CS_Ingested/’)
takesList = [0]

def process_takes(takes):
    for take in takes:
        take_index = take.get_editor_property("take_index")
        take_info = capture_source.get_take_info(take_index=take_index)

         # then you can use take_info to create a CaptureData

# Register a callback that will be called when takes are ready
captureSource.on_get_takes_finished_dynamic_delegate.add_callable(process_takes)

# Call startup to parse the folder you have set in the Capture Source
captureSource.startup(mode=unreal.TakeIngestMode.SYNC)

# This will call the function process_takes
captureSource.get_takes(takesList)

The difference from using ASYNC is that the call to startup and get_takes will not block execution but when using python scripts using SYNC is more convenient

Hi Thales,

Thank you for the help. I managed to ingest the capture source with a callback method.

I have few questions/issues

  1. captureSource.get_takes() only works if the Tools-Capture Manager UI window is open and capture source node is selected.

If the Capture Manager UI window is open, but the capture source node is not selected, the following error is displayed after calling get_takes()
LogMetaHumanCaptureSource: Error: Failed to get take. Not all requested takes are available to process

Is there a way to open Capture Manager UI and select capture source node with Python or some other way to initialize capture manager?

  1. Question regarding captureSource.set_target_path.

First path argument “target_ingest_directory” appears to be local to the project. Setting it to /Game/MH_Ingested/ creates uassets in …\Unreal Projects\ProjectName\Content\MH_Ingested

Second path argument “target_folder_asset_path” appears to be local to the drive where UE is installed.
Setting it to /Game/MH_Ingested/ creates folder structure with top, bottom and depth image sequence in the root of the drive where UE is installed, for example D:\Game\MH_Ingested\

Question is how to have all assets in the same folder, similar to processing manually from Capture Manager?

  1. What is the function of captureSource.startup() if all capture source ingest assets get created by calling get_takes()?

Also SYNC take ingest mode is not available for me, only ASYNC and BLOCKING. I am currently using UE v.5.2.1 with MetaHuman plugin v.2.0.2
LogPython: Error: AttributeError: type object ‘TakeIngestMode’ has no attribute ‘SYNC’

Thanks.

  1. captureSource.get_takes() only works if the Tools-Capture Manager UI window is open and capture source node is selected.

This should not be the case. The Capture Manager is basically a UI built on top the Capture Source API so after you call startup, get_takes should be working, if its not its probably a bug

  1. Question regarding captureSource.set_target_path.

The first argument target_ingest_directory can be an absolute path in your computer. This wil be the path where the capture source will place the footage/depth frames. This is to give you the flexibility to select exactly where you want to store your data. When using the Capture Manager window we set this to be a path local to the project.

target_folder_asset_path is the path inside the project where you want unreal assets to be created, this will include Image Sequences, Audio Clips, Lens FIles, Camera Calibration asset etcs. This should be local to your project in the format /Game/TargetFolderAssetPath

  1. What is the function of captureSource.startup() if all capture source ingest assets get created by calling get_takes()?

startup parses the folder that the capture source is pointing looking for takes that can be imported. If you look at the Capture Manager window, startup is called when you click on a capture source in the list to populate the UI with the takes that are available. THe information on each take can be obained with a call to get_take_info, which returns a struct with all the information available for a particular take. get_takes will do the actual footage ingest.

Also SYNC take ingest mode is not available for me, only ASYNC and BLOCKING. I am currently using UE v.5.2.1 with MetaHuman plugin v.2.0.2
LogPython: Error: AttributeError: type object ‘TakeIngestMode’ has no attribute ‘SYNC’

You are right, it is either ASYNC or BLOCKING. I had the class open while writing the answer but I still typed the wrong thing

Hi Thales,

I found that running captureSource.get_takes() immediately after captureSource.startup() doesn’t work since startup needs some time to parse takes.
I solved this by creating a separate thread which waits for captureSource.can_ingest_takes() to become True and then runs captureSource.get_takes(). Is there a better way to do this?

I also discovered that the difference between running get_takes with Capture Manager window open and startup() is that with startup() created uassets stay unsaved, so additional line unreal.EditorLoadingAndSavingUtils.save_dirty_packages in callback function is required to save them.

Second difference in running get_takes with the Capture Manager window open is that FootageCaptureData uasset gets created with image sequences, depth sequences and camera calibration uassets assigned.
I understand that I can create FootageCaptureData with script and populate it with uassets so it’s not an issue.

Have you tried like in my example where you call startup passing BLOCKING captureSource.startup(mode=unreal.TakeIngestMode.BLOCKING)? If you do this, startup is meant to only return after all takes were parsed

I understand that I can create FootageCaptureData with script and populate it with uassets so it’s not an issue.

This was by design. The CaptureSource and CaptureData do not talk to each other directly and you are free to create the Capture Data like you see fit.

I tried that many times. I always get an error message:

LogMetaHumanCaptureSource: Error: Failed to get take. Not all requested takes are available to process

That is the reason why I was initially confused about the function of the startup since it was not doing anything for me.

Example of my code:

import unreal

captureSource = unreal.load_asset('/Game/MH_CS')
takesList = [0]

def process_takes(takes):
	print('Takes ingested')

captureSource.startup(mode=unreal.TakeIngestMode.ASYNC)
captureSource.set_target_path('c:/Unreal Projects/ProjectName/Content/MH_CS_Ingested/', '/Game/MH_CS_Ingested/')
captureSource.on_get_takes_finished_dynamic_delegate.add_callable(process_takes)
captureSource.get_takes(takesList, unreal.MetaHumanCaptureSource_GetTakesCallbackPerTake())

Sorry, I typed the wrong thing again. I meant unreal.TakeIngestMode.BLOCKING

I am getting the same error with BLOCKING mode as well.

hi! Thales,
I’m using UE 5.3.2. In this version the unreal.py seems to remove all the properties like startup and get_takes under the captureSource class.
Instead, these property are moved under class MetaHumanCaptureSourceSync.
Besides, the TakeIngestMode class is also removed
Thus, I found it hard to automate the capture manager operation with the new api. Could u give a simple example so that I can know how to startup the ingest operation with python scripyt?
thx~

I have tried to connect to the remote device with the help of MetaHumanCaptureSourceSync module.

Some useful example is available inside MetaHuman plugin folder of Content/Python.
I haven’t found any useful examples to connect to remote device but with the help of Python Stub
I am able to connect to remote device and retrieve the takes and ingesting example is available inside Plugin

def setupCaptureSource():
    # Creating core logic for remote device for MetaHuman CaptureSource 

    capture_source = unreal.MetaHumanCaptureSourceSync() # Does not found any way to retreive data from MetaHumanCaptureSource 
    capture_source.rename( capture_source_name )

    # create Device Address for capture_source 
    device_address = unreal.DeviceAddress()
    device_address.set_editor_property( "ip_address" , REMOTE_IP )

    capture_source.set_editor_property( "device_ip_address" , device_address ) # adding device_Address 
    capture_source.set_editor_property( "device_control_port" , REMOTE_PORT )  # PORT

    # capture_type to connection for remote device
    capture_source.set_editor_property( "capture_source_type" , unreal.MetaHumanCaptureSourceType.LIVE_LINK_FACE_CONNECTION )

    # setup for ingest path
    dir_path = unreal.DirectoryPath()
    dir_path.set_editor_property("path", footage_path)
    capture_source.set_editor_property('storage_path', dir_path)

    capture_source.set_target_path(capture_source_target_ingest_directory, footage_path)

    return capture_source


capture_source = setupCaptureSource()
if capture_source.can_startup():   # Checking whether we can connect to device or not
    print( "DEVICE is ready to go" )
    capture_source.startup()  # Start Connection before retrieving

    takes = capture_source.refresh()  # Find Takes

else:
    print( "Something wrong, Please look again" )


Hopefully this script could help you out