/* * 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; } } }