Command line worker program for HumanoidMocap. It parses arguments to run various tasks like downloading models, converting video, preparing depth data, estimating lens FOV, running GPU and ONNX benchmarks, and performing body/hand capture and refinement by reading JSON job requests and invoking worker components.
using HumanoidMocap.Worker;
using HumanoidMocap.Core.Inference;
using HumanoidMocap.EditorTools.Inference;
using System.Text.Json;
using TorchSharp;
Console.OutputEncoding=new System.Text.UTF8Encoding(false);
Console.InputEncoding=System.Text.Encoding.UTF8;
using var cancellation=new CancellationTokenSource();
Console.CancelKeyPress+=(_,e)=>{e.Cancel=true;cancellation.Cancel();};
if(Console.IsInputRedirected)
{
_=Task.Run(async()=>
{
try{while(await Console.In.ReadLineAsync() is { } line)if(line=="cancel")cancellation.Cancel();}
catch(ObjectDisposedException){}
});
}
// MediaPipe hand models run on ONNX Runtime in the worker; the editor keeps the managed interpreter.
LiteOnnx.Install(Console.WriteLine);
// One thread per physical core on typical SMT processors, leaving the rest to the
// editor. Four threads took 2.4 times longer than eight for WiLoR on a Ryzen 7 7800X3D.
static int InferenceThreads()=>int.TryParse(Environment.GetEnvironmentVariable("HUMANOID_MOCAP_THREADS"),out var threads)&&threads is >=1 and <=64
?threads:Math.Clamp(Environment.ProcessorCount/2,2,12);
try
{
if(args.Length==2&&args[0]=="download-body-models")
await BodyModelDownloads.Ensure(Path.GetFullPath(args[1]),cancellation.Token);
else if(args.Length==3&&args[0]=="convert-video")
{VideoConvert.Run(args[1],args[2],cancellation.Token,Console.WriteLine);Console.WriteLine("HM_RESULT "+args[2]);}
else if(args.Length==3&&args[0]=="depth-prepare")
{
var prepared=DepthPrepare.Run(args[1],args[2],cancellation.Token,Console.WriteLine);
var resultPath=Path.Combine(args[2],"depth-prepare.json");File.WriteAllText(resultPath,System.Text.Json.JsonSerializer.Serialize(prepared));
Console.WriteLine("HM_RESULT "+resultPath);
}
else if(args.Length==3&&args[0]=="lens")
{
var metadata=HumanoidMocap.EditorTools.Mp4Metadata.Read(args[2]);var clock=System.Diagnostics.Stopwatch.StartNew();
var fov=MogeLens.EstimateHorizontalFov(args[1],args[2],metadata.Times,metadata.Width,metadata.Height,5,cancellation.Token,Console.WriteLine);
Console.WriteLine(fov is float f?$"FOV {f:F2} deg, focal {metadata.Width/2/Math.Tan(f*Math.PI/360):F1} px, {clock.Elapsed.TotalSeconds:F1}s":"no estimate");
}
else if(args.Length==2&&args[0]=="hands-bench")LiteOnnx.Bench(args[1],Console.WriteLine);
else if(args.Length==1&&args[0]=="adapters")foreach(var a in GpuBackbone.ListAdapters())Console.WriteLine(a);
else if(args.Length==2&&args[0]=="gpu-bench")GpuBackbone.Bench(args[1],Console.WriteLine);
else if(args.Length==2&&args[0]=="batch-bench")GpuBackbone.BatchBench(args[1],Console.WriteLine);
else if(args.Length==3&&args[0]=="download-hand-models")
await HandModelDownloads.Ensure(Path.GetFullPath(args[1]),args[2],cancellation.Token);
else if(args.Length==2&&args[0]=="import-hot3d")
{
var request=JsonSerializer.Deserialize<Hot3dImportRequest>(File.ReadAllText(args[1]))??throw new ArgumentException("Invalid HOT3D import request.");
Console.WriteLine("HM_RESULT "+Hot3dClipImport.Run(request,cancellation.Token,Console.WriteLine));
}
else if(args.Length==2&&args[0]=="landmark-capture")
{
var request=JsonSerializer.Deserialize<LandmarkCaptureRequest>(File.ReadAllText(args[1]))??throw new ArgumentException("Invalid landmark job request.");
Console.WriteLine("HM_RESULT "+LandmarkCapture.Run(request,cancellation.Token,Console.WriteLine));
}
else if(args.Length==2&&args[0]=="body-capture")
{
torch.set_num_threads(InferenceThreads());
OpenCvSharp.Cv2.SetNumThreads(InferenceThreads());
var request=JsonSerializer.Deserialize<BodyCaptureRequest>(File.ReadAllText(args[1]))??throw new ArgumentException("Invalid body job request.");
{var gpuCache=Path.Combine(request.Models,"gpu");if(Directory.Exists(gpuCache))GpuBackbone.RemoveStale(gpuCache);}
Console.WriteLine("HM_RESULT "+BodyCapture.Run(request,cancellation.Token,Console.WriteLine));
}
else if(args.Length==4&&args[0]=="foot-clean")
{
var document=HumanoidMocap.Core.Motion.MotionDocument.Parse(File.ReadAllBytes(args[1]));var clock=System.Diagnostics.Stopwatch.StartNew();
var note=FootContactCleanup.Apply(document,args[3],cancellation.Token,Console.WriteLine);
if(note is not null)document.Diagnostics.Add(note);File.WriteAllText(args[2],document.ToJson());
Console.WriteLine($"HM_RESULT {(note??"nothing to clean")} ({clock.Elapsed.TotalSeconds:F1} s)");
}
else if(args.Length==2&&args[0]=="body-refine")
{
var request=JsonSerializer.Deserialize<BodyRefinementRequest>(File.ReadAllText(args[1]))??throw new ArgumentException("Invalid body refinement request.");
Console.WriteLine("HM_RESULT "+BodyRefinement.Run(request,cancellation.Token,Console.WriteLine));
}
else if(args.Length==2&&args[0]=="hand-capture")
{
torch.set_num_threads(InferenceThreads());
var request=JsonSerializer.Deserialize<HandCaptureRequest>(File.ReadAllText(args[1]))??throw new ArgumentException("Invalid hand job request.");
Console.WriteLine("HM_RESULT "+HandCapture.Run(request,cancellation.Token,Console.WriteLine));
}
else throw new ArgumentException("Usage: download-body-models <model-folder> | download-hand-models <model-folder> <mediapipe|mobilehand|wildhands|wilor> | body-capture <job.json> | body-refine <job.json> | hand-capture <job.json> | landmark-capture <job.json> | import-hot3d <job.json>");
}
catch(OperationCanceledException){Console.Error.WriteLine("Cancelled. Cached reconstruction can be resumed.");Environment.ExitCode=2;}
catch(Exception e){Console.Error.WriteLine(Environment.GetEnvironmentVariable("HUMANOID_MOCAP_TRACE")=="1"?e.ToString():e.Message);Environment.ExitCode=1;}