Editor/Inference/SamDecoderFeedback.cs
using System;
using System.Threading;
namespace HumanoidMocapX.Inference;
/// <summary>Connects real body/hand predictions to the decoder's five intermediate
/// updates and final prediction. Reuse sequentially for one crop at a time.</summary>
internal sealed class SamDecoderFeedback
{
readonly SamDecoderShape shape;
readonly SamPoseHead pose;
readonly SamPredictionHead camera;
readonly SamKeypointFeedback keypoints;
readonly float[] cameraParameters = new float[3], translation = new float[3];
readonly float[] cropped = new float[SamKeypoints.Count * 2], depths = new float[SamKeypoints.Count];
ReadOnlyMemory<float> image;
SamProjection projection;
internal SamPoseHead Pose => pose;
internal ReadOnlySpan<float> CameraParameters => cameraParameters;
internal ReadOnlySpan<float> CameraTranslation => translation;
internal SamDecoderFeedback(SamDecoderShape shape, SamPoseHead pose, SamPredictionHead camera, SamKeypointFeedback keypoints)
{
ArgumentNullException.ThrowIfNull(pose); ArgumentNullException.ThrowIfNull(camera); ArgumentNullException.ThrowIfNull(keypoints);
if (camera.Parameters != 3 || shape.Tokens < 145) throw new ArgumentException("SAM feedback requires a camera head and complete keypoint tokens.");
this.shape = shape; this.pose = pose; this.camera = camera; this.keypoints = keypoints;
}
/// <summary>Binds this crop's projection and conditioned image. The image must
/// remain unchanged until decoding finishes. No image copy is made.</summary>
internal void Prepare(SamProjection projection, ReadOnlyMemory<float> image)
{
if (image.Length != shape.ImageElements) throw new ArgumentException("SAM feedback image dimensions do not match the decoder.");
SamDecoder.Finite(image.Span); this.image = image; this.projection = projection;
}
internal void Update(int layer, ReadOnlySpan<float> normalized, Span<float> tokens, Span<float> positions, CancellationToken cancellation)
{
if (layer < 0 || layer >= SamDecoderShape.Layers - 1) throw new ArgumentOutOfRangeException(nameof(layer));
Finish(normalized, cancellation);
keypoints.Run(image.Span, cropped, depths, pose.Keypoints, tokens, positions, cancellation);
}
/// <summary>Evaluates the final normalized tokens without applying another
/// feedback update. Missing crop preparation and incompatible tensors fail.</summary>
internal void Finish(ReadOnlySpan<float> normalized, CancellationToken cancellation = default)
{
cancellation.ThrowIfCancellationRequested();
if (image.Length != shape.ImageElements || normalized.Length != shape.TokenElements)
throw new ArgumentException("Prepare the SAM feedback crop and supply complete normalized decoder tokens.");
var token = normalized.Slice(0, shape.Width); pose.Run(token, cancellation); camera.Run(token, cameraParameters, cancellation);
projection.Run(pose.Keypoints, cameraParameters, cropped, depths, translation);
}
}