217 lines
7.5 KiB
C#
217 lines
7.5 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using UnityEngine;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.Physics
|
|
{
|
|
/// <summary>
|
|
/// Vector Statistics used in gaze stabilization.
|
|
/// </summary>
|
|
public class VectorRollingStatistics
|
|
{
|
|
/// <summary>
|
|
/// Current standard deviation of the positions of the vectors.
|
|
/// </summary>
|
|
public float CurrentStandardDeviation { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Difference to standardDeviation when the latest sample was added.
|
|
/// </summary>
|
|
public float StandardDeviationDeltaAfterLatestSample { get; private set; }
|
|
|
|
/// <summary>
|
|
/// How many standard deviations the latest sample was away.
|
|
/// </summary>
|
|
public float StandardDeviationsAwayOfLatestSample { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The average position.
|
|
/// </summary>
|
|
public Vector3 Average { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The number of samples in the current set (may be 0 - maxSamples)
|
|
/// </summary>
|
|
public float ActualSampleCount { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Keeps track of the index into the sample list for the rolling average.
|
|
/// </summary>
|
|
private int currentSampleIndex;
|
|
|
|
/// <summary>
|
|
/// An array of samples for calculating standard deviation.
|
|
/// </summary>
|
|
private Vector3[] samples;
|
|
|
|
/// <summary>
|
|
/// The sum of all of the samples.
|
|
/// </summary>
|
|
private Vector3 cumulativeFrame;
|
|
|
|
/// <summary>
|
|
/// The sum of all of the samples squared.
|
|
/// </summary>
|
|
private Vector3 cumulativeFrameSquared;
|
|
|
|
/// <summary>
|
|
/// The total number of samples taken.
|
|
/// </summary>
|
|
private int cumulativeFrameSamples;
|
|
|
|
/// <summary>
|
|
/// The maximum number of samples to include in
|
|
/// the average and standard deviation calculations.
|
|
/// </summary>
|
|
private int maxSamples;
|
|
|
|
/// <summary>
|
|
/// Initialize the rolling stats.
|
|
/// </summary>
|
|
public void Init(int sampleCount)
|
|
{
|
|
maxSamples = sampleCount;
|
|
samples = new Vector3[sampleCount];
|
|
Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the stats to zero.
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
currentSampleIndex = 0;
|
|
ActualSampleCount = 0;
|
|
cumulativeFrame = Vector3.zero;
|
|
cumulativeFrameSquared = Vector3.zero;
|
|
cumulativeFrameSamples = 0;
|
|
CurrentStandardDeviation = 0.0f;
|
|
StandardDeviationDeltaAfterLatestSample = 0.0f;
|
|
StandardDeviationsAwayOfLatestSample = 0.0f;
|
|
Average = Vector3.zero;
|
|
|
|
if (samples != null)
|
|
{
|
|
for (int index = 0; index < samples.Length; index++)
|
|
{
|
|
samples[index] = Vector3.zero;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a new sample to the sample list and updates the stats.
|
|
/// </summary>
|
|
/// <param name="value">The new sample to add</param>
|
|
public void AddSample(Vector3 value)
|
|
{
|
|
if (maxSamples == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// remove the old sample from our accumulation
|
|
Vector3 oldSample = samples[currentSampleIndex];
|
|
|
|
// -- Below replaces operations:
|
|
// cumulativeFrame -= oldSample;
|
|
// cumulativeFrameSquared -= (oldSample.Mul(oldSample));
|
|
|
|
cumulativeFrame.x -= oldSample.x;
|
|
cumulativeFrame.y -= oldSample.y;
|
|
cumulativeFrame.z -= oldSample.z;
|
|
|
|
oldSample.x *= oldSample.x;
|
|
oldSample.y *= oldSample.y;
|
|
oldSample.z *= oldSample.z;
|
|
|
|
cumulativeFrameSquared.x -= oldSample.x;
|
|
cumulativeFrameSquared.y -= oldSample.y;
|
|
cumulativeFrameSquared.z -= oldSample.z;
|
|
// --
|
|
|
|
// Add the new sample to the accumulation
|
|
samples[currentSampleIndex] = value;
|
|
|
|
// -- Below replaces operations:
|
|
// cumulativeFrame += value;
|
|
// cumulativeFrameSquared += value.Mul(value);
|
|
cumulativeFrame.x += value.x;
|
|
cumulativeFrame.y += value.y;
|
|
cumulativeFrame.z += value.z;
|
|
|
|
Vector3 valueSquared = value;
|
|
valueSquared.x = value.x * value.x;
|
|
valueSquared.y = value.y * value.y;
|
|
valueSquared.z = value.z * value.z;
|
|
|
|
cumulativeFrameSquared.x += valueSquared.x;
|
|
cumulativeFrameSquared.y += valueSquared.y;
|
|
cumulativeFrameSquared.z += valueSquared.z;
|
|
// --
|
|
|
|
// Keep track of how many samples we have
|
|
cumulativeFrameSamples++;
|
|
ActualSampleCount = Mathf.Min(maxSamples, cumulativeFrameSamples);
|
|
|
|
// see how many standard deviations the current sample is from the previous average
|
|
// -- Below replaces operations:
|
|
// Vector3 deltaFromAverage = (Average - value);
|
|
Vector3 deltaFromAverage = Average;
|
|
deltaFromAverage.x -= value.x;
|
|
deltaFromAverage.y -= value.y;
|
|
deltaFromAverage.z -= value.z;
|
|
// --
|
|
|
|
float oldStandardDeviation = CurrentStandardDeviation;
|
|
// -- Below replaces operations:
|
|
// StandardDeviationsAwayOfLatestSample = oldStandardDeviation.Equals(0) ? 0 : (deltaFromAverage / oldStandardDeviation).magnitude;
|
|
if (oldStandardDeviation == 0)
|
|
{
|
|
StandardDeviationsAwayOfLatestSample = 0;
|
|
}
|
|
else
|
|
{
|
|
deltaFromAverage.x /= oldStandardDeviation;
|
|
deltaFromAverage.y /= oldStandardDeviation;
|
|
deltaFromAverage.z /= oldStandardDeviation;
|
|
StandardDeviationsAwayOfLatestSample = deltaFromAverage.magnitude;
|
|
}
|
|
// --
|
|
|
|
// And calculate new averages and standard deviations
|
|
// (note that calculating a standard deviation of a Vector3 might not
|
|
// be done properly, but the logic is working for the gaze stabilization scenario)
|
|
|
|
// -- Below replaces operations:
|
|
// Average = cumulativeFrame / ActualSampleCount;
|
|
// float newStandardDev = Mathf.Sqrt((cumulativeFrameSquared / ActualSampleCount - Average.Mul(Average)).magnitude);
|
|
Vector3 average = Average;
|
|
average.x = cumulativeFrame.x / ActualSampleCount;
|
|
average.y = cumulativeFrame.y / ActualSampleCount;
|
|
average.z = cumulativeFrame.z / ActualSampleCount;
|
|
|
|
Average = average;
|
|
|
|
Vector3 frmSqrDivSamples = cumulativeFrameSquared;
|
|
frmSqrDivSamples.x /= ActualSampleCount;
|
|
frmSqrDivSamples.y /= ActualSampleCount;
|
|
frmSqrDivSamples.z /= ActualSampleCount;
|
|
|
|
frmSqrDivSamples.x -= (average.x * average.x);
|
|
frmSqrDivSamples.y -= (average.y * average.y);
|
|
frmSqrDivSamples.z -= (average.z * average.z);
|
|
|
|
float newStandardDev = Mathf.Sqrt(frmSqrDivSamples.magnitude);
|
|
// --
|
|
|
|
StandardDeviationDeltaAfterLatestSample = oldStandardDeviation - newStandardDev;
|
|
CurrentStandardDeviation = newStandardDev;
|
|
|
|
// update the next list position
|
|
currentSampleIndex = (currentSampleIndex + 1) % maxSamples;
|
|
}
|
|
}
|
|
}
|