/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* Licensed under the Oculus SDK License Agreement (the "License");
* you may not use the Oculus SDK except in compliance with the License,
* which is provided at the time of installation or download, or which
* otherwise accompanies this software in either electronic or hard copy form.
*
* You may obtain a copy of the License at
*
* https://developer.oculus.com/licenses/oculussdk/
*
* Unless required by applicable law or agreed to in writing, the Oculus SDK
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using UnityEngine;
namespace Oculus.Interaction
{
///
/// Class implementing the Random Sample Consensus (RANSAC) algorithm for a generic data type.
/// RANSAC is an iterative non-deterministic algorithm used for fitting estimation in the presence of a large number of outliers in the data.
///
/// The type of the model being fitted to the data.
public class RandomSampleConsensus
{
///
/// Delegate for generating a model from a pair of samples,
/// represented by their indices.
///
/// The first sample index.
/// The second sample index.
/// A model generated from the provided indices.
public delegate TModel GenerateModel(int index1, int index2);
///
/// Delegate for evaluating the agreement score of a model with respect to the entire dataset.
///
/// The model to be evaluated.
/// The set of all generated models for comparison.
/// The score representing the model's agreement with the data.
public delegate float EvaluateModelScore(TModel model, TModel[,] modelSet);
private readonly TModel[,] _modelSet;
private readonly int _exclusionZone;
private readonly int _maxDataPoints;
///
/// Initializes a new instance of the RandomSampleConsensus class.
///
/// The total number of sample points.
/// The threshold to avoid selecting samples too close to each other.
public RandomSampleConsensus(int maxDataPoints = 10, int exclusionZone = 2)
{
_maxDataPoints = maxDataPoints;
_exclusionZone = exclusionZone;
_modelSet = new TModel[maxDataPoints, maxDataPoints];
}
///
/// Calculates the best fitting model based on the RANSAC algorithm.
///
/// The function to generate models from data points.
/// The function to evaluate the agreement score of a model.
/// The model that best fits the data according to the RANSAC algorithm.
public TModel FindOptimalModel(GenerateModel modelGenerator, EvaluateModelScore modelScorer)
{
return FindOptimalModel(modelGenerator, modelScorer, _maxDataPoints);
}
///
/// Calculates the best fitting model based on the RANSAC algorithm.
///
/// The function to generate models from data points.
/// The function to evaluate the agreement score of a model.
/// The number of data points to evaluate.
/// The model that best fits the data according to the RANSAC algorithm.
public TModel FindOptimalModel(GenerateModel modelGenerator,
EvaluateModelScore modelScorer, int dataPointsCount)
{
for (int i = 0; i < dataPointsCount; ++i)
{
for (int j = i + 1; j < dataPointsCount; ++j)
{
_modelSet[i, j] = modelGenerator(
(i + _exclusionZone) % dataPointsCount,
(j + _exclusionZone) % dataPointsCount);
}
}
TModel bestModel = default;
float bestScore = float.PositiveInfinity;
for (int i = 0; i < dataPointsCount; ++i)
{
int y = Random.Range(0, dataPointsCount - 1);
int x = Random.Range(y + 1, dataPointsCount);
TModel model = _modelSet[y, x];
float score = modelScorer(model, _modelSet);
if (score < bestScore)
{
bestModel = model;
bestScore = score;
}
}
return bestModel;
}
}
}