Editor/Inference/PersonSpan.cs
using System;
using System.Threading;
using System.Threading.Tasks;
namespace HumanoidMocapX.Inference;
/// <summary>The part of a capture timeline in which the tracked person is visible: its first
/// frame, how many follow, and one XYXY box per visible frame.</summary>
internal sealed record PersonSpan(float[] Boxes, int First, int Count)
{
const double TimelineFps = 30;
/// <summary>Seconds from the timeline's start to the first visible frame.</summary>
internal double StartSeconds => First / TimelineFps;
/// <summary>The visible frames of <paramref name="frames"/>; the same instance when the
/// person is visible throughout.</summary>
internal ICaptureFrames Visible(ICaptureFrames frames) =>
First == 0 && Count == frames.Count ? frames : new Range(frames, First, Count);
/// <summary>A diagnostic naming the captured part of the video, whose timeline starts at
/// <paramref name="start"/> seconds and has <paramref name="frames"/> frames; null when the
/// person is visible throughout.</summary>
internal string Note(double start, int frames) => First == 0 && Count == frames ? null
: FormattableString.Invariant($"The person is in view from {start + StartSeconds:0.00} s to {start + (First + Count) / TimelineFps:0.00} s of the video, so only that part was captured.");
sealed class Range(ICaptureFrames frames, int first, int count) : ICaptureFrames
{
public int Width => frames.Width;
public int Height => frames.Height;
public int Count => count;
public ValueTask ReadAsync(int frame, Memory<byte> rgba, CancellationToken cancellation)
{
if ((uint)frame >= (uint)count) throw new ArgumentOutOfRangeException(nameof(frame));
return frames.ReadAsync(first + frame, rgba, cancellation);
}
}
}