< Summary - ReFlex - Library

Information
Class: ReFlex.Core.Interactivity.Components.InteractionObserverBase
Assembly: ReFlex.Core.Interactivity
File(s): D:\a\reflex\reflex\library\src\Core\Interactivity\Components\InteractionObserverBase.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 258
Coverable lines: 258
Total lines: 505
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 95
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

D:\a\reflex\reflex\library\src\Core\Interactivity\Components\InteractionObserverBase.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Threading.Tasks;
 5using ReFlex.Core.Common.Components;
 6using ReFlex.Core.Common.Interfaces;
 7using ReFlex.Core.Common.Util;
 8using ReFlex.Core.Interactivity.Interfaces;
 9using ReFlex.Core.Interactivity.Util;
 10using Math = System.Math;
 11
 12namespace ReFlex.Core.Interactivity.Components
 13{
 14    public abstract class InteractionObserverBase : IInteractionObserver, IPerformanceReporter
 15    {
 16        #region Fields
 17
 18        private readonly InteractionSmoothingBehaviour _smoothingBehaviour;
 019        private int _numSmoothingFrames = 5;
 020        private int _historySize = 25;
 021        private int _maxNumEmptyFramesBetween = 3;
 022        private float _touchMergeDistance2d = 64f;
 023        private float _depthScale = 100.0f;
 24        private FilterType _filterType;
 25        private float _maxConfidence;
 26        private int _extremumTypeCheckRadius;
 27        private int _extremumTypeCheckNumSamples;
 28
 029        private Tuple<int, int>[] _fixedSamples = Array.Empty<Tuple<int, int>>();
 030        private Tuple<int, int>[] _stochasticSamples = Array.Empty<Tuple<int, int>>();
 31
 32        #endregion
 33
 34        #region Properties
 35
 36        /// <summary>
 37        /// Gets or sets the distance.
 38        /// </summary>
 39        /// <value>
 40        /// The distance.
 41        /// </value>
 042        public float Distance { get; set; }
 43
 44        /// <summary>
 45        /// Gets or sets the minimum depth.
 46        /// </summary>
 47        /// <value>
 48        /// The maximum depth.
 49        /// </value>
 050        public float MinDistance { get; set; }
 51
 52        /// <summary>
 53        /// Gets or sets the maximum depth.
 54        /// </summary>
 55        /// <value>
 56        /// The maximum depth.
 57        /// </value>
 058        public float MaxDistance { get; set; }
 59
 60        /// <summary>
 61        /// defines how many points should be checked to determine the type of the extremum
 62        /// </summary>
 63        public int ExtremumTypeCheckNumSamples
 64        {
 065            get => _extremumTypeCheckNumSamples;
 66            set
 067            {
 068                if (_extremumTypeCheckNumSamples == value)
 069                    return;
 070                _extremumTypeCheckNumSamples = value;
 071                UpdateSamples();
 072            }
 73        }
 74
 75        /// <summary>
 76        /// The pixel radius in which the points are sampled for deriving the type of the local extremum
 77        /// </summary>
 78        public int ExtremumTypeCheckRadius
 79        {
 080            get => _extremumTypeCheckRadius;
 81            set
 082            {
 083                if (_extremumTypeCheckRadius == value)
 084                    return;
 085                _extremumTypeCheckRadius = value;
 086                UpdateSamples();
 087            }
 88        }
 89
 90        /// <summary>
 91        /// Which ratio of "correctly aligned" points should match to distinguish between Undefined or Minimum/Maximum ?
 92        /// </summary>
 093        public float ExtremumTypeCheckFittingPercentage { get; set; }
 94
 95        /// <summary>
 96        /// Defines how neighboring the points are sampled for determining the type of extremum.
 97        /// </summary>
 098        public ExtremumTypeCheckMethod ExtremumTypeCheckMethod { get; set; }
 99
 100        public int InteractionHistorySize
 101        {
 0102            get => _historySize;
 103            set
 0104            {
 0105                _historySize = value;
 0106                if (_smoothingBehaviour != null)
 0107                {
 0108                    _smoothingBehaviour.NumFramesHistory = value;
 109                    // update depending smoothing/filter values
 0110                    NumSmoothingFrames = _numSmoothingFrames;
 0111                    MaxNumEmptyFramesBetween = _maxNumEmptyFramesBetween;
 0112                }
 0113            }
 114        }
 115
 116        public int NumSmoothingFrames
 117        {
 0118            get => _numSmoothingFrames;
 119            set
 0120            {
 0121                _numSmoothingFrames = value;
 0122                if (_smoothingBehaviour != null)
 0123                    _smoothingBehaviour.NumFramesSmoothing = value < _smoothingBehaviour.NumFramesHistory
 0124                        ? _smoothingBehaviour.NumFramesHistory
 0125                        : value;
 0126            }
 127        }
 128
 129        // TODO: write tests for negative (!) values and values larger than history
 130        public int MaxNumEmptyFramesBetween
 131        {
 0132            get => _maxNumEmptyFramesBetween;
 133            set
 0134            {
 0135                _maxNumEmptyFramesBetween = value;
 0136                if (_smoothingBehaviour != null)
 0137                    _smoothingBehaviour.MaxNumEmptyFramesBetween = value < _smoothingBehaviour.NumFramesHistory
 0138                        ? _smoothingBehaviour.NumFramesHistory
 0139                        : value;
 0140            }
 141        }
 142
 143        public float TouchMergeDistance2D
 144        {
 0145            get => _touchMergeDistance2d;
 146            set
 0147            {
 0148                _touchMergeDistance2d = value;
 0149                if (_smoothingBehaviour != null)
 0150                    _smoothingBehaviour.TouchMergeDistance2D = value;
 0151            }
 152        }
 153
 154        public float DepthScale
 155        {
 0156            get => _depthScale;
 157            set
 0158            {
 0159                _depthScale = value;
 0160                if (_smoothingBehaviour != null)
 0161                    _smoothingBehaviour.DepthScale = value;
 0162            }
 163        }
 164
 165        public FilterType FilterType
 166        {
 0167            get => _filterType;
 168            set
 0169            {
 0170                _filterType = value;
 0171                if (_smoothingBehaviour != null)
 0172                    _smoothingBehaviour.UpdateFilterType(_filterType);
 0173            }
 174        }
 175
 176
 177        /// <summary>
 178        /// Gets or sets the minimum angle.
 179        /// </summary>
 180        /// <value>
 181        /// The minimum angle.
 182        /// </value>
 0183        public virtual float MinAngle { get; set; }
 184
 185        /// <summary>
 186        /// Gets or sets the minimum confidence.
 187        /// </summary>
 188        /// <value>
 189        /// The minimum confidence.
 190        /// </value>
 0191        public virtual float MinConfidence { get; set; }
 192
 193        /// <summary>
 194        /// Gets or sets the maximum confidence.
 195        /// </summary>
 196        /// <value>
 197        /// The maximum confidence.
 198        /// </value>
 199        public virtual float MaxConfidence
 200        {
 0201            get => _maxConfidence;
 202            set
 0203            {
 0204                _maxConfidence = value;
 0205                if (_smoothingBehaviour != null)
 0206                    _smoothingBehaviour.MaxConfidence = (int) MaxConfidence;
 0207            }
 208        }
 209
 0210        public bool MeasurePerformance { get; set; }
 211
 0212        protected int FrameId { get; private set; }
 213
 214        #region Abstract Members
 215
 216        public abstract ObserverType Type { get; }
 217        public abstract PointCloud3 PointCloud { get; set; }
 218        public abstract VectorField2 VectorField { get; set; }
 219
 220        public abstract event EventHandler<IList<Interaction>> NewInteractions;
 221
 222        public event EventHandler<PerformanceDataItem> PerformanceDataUpdated;
 223
 224        public event EventHandler<IList<InteractionFrame>> InteractionHistoryUpdated;
 225
 226        public abstract Task<ProcessingResult> Update();
 227
 228        #endregion
 229
 230        #region Constructor
 231
 0232        protected InteractionObserverBase()
 0233        {
 0234            _smoothingBehaviour = new InteractionSmoothingBehaviour(InteractionHistorySize);
 0235        }
 236
 237        #endregion
 238
 239        public void UpdateFrameId(int frameId)
 0240        {
 0241            FrameId = frameId;
 0242        }
 243
 244        #endregion
 245
 246        protected InteractionFrame ComputeSmoothingValue(IList<Interaction> rawInteractions)
 0247        {
 0248            var result = _smoothingBehaviour.Update(rawInteractions);
 0249            InteractionHistoryUpdated?.Invoke(this, _smoothingBehaviour.InteractionsFramesCache);
 250
 0251            return result;
 0252        }
 253
 254        protected virtual ExtremumDescription ComputeExtremumType(Point3[][] pointsArray, int xPos, int yPos)
 0255        {
 0256            if (ExtremumTypeCheckMethod == ExtremumTypeCheckMethod.Global)
 0257                return new ExtremumDescription
 0258                    { Type = ExtremumType.Maximum, NumFittingPoints = ExtremumTypeCheckNumSamples, PercentageFittingPoin
 259
 0260            var xMax = pointsArray.Length - 1;
 0261            var yMax = xMax > 0 ? pointsArray[0].Length - 1 : 0;
 262
 263            // refZ is the absolute distance from sensor
 0264            var refZ = Math.Abs(pointsArray[xPos][yPos].Z);
 265
 0266            var numCloser = 0;
 0267            var numFarther = 0;
 268
 269            Tuple<int, int>[] samples;
 0270            switch (ExtremumTypeCheckMethod)
 271            {
 272                case ExtremumTypeCheckMethod.StochasticStatic:
 0273                    samples = _stochasticSamples;
 0274                    break;
 275                case ExtremumTypeCheckMethod.StochasticDynamic:
 0276                    samples = GenerateSamples();
 0277                    break;
 278                case ExtremumTypeCheckMethod.Global:
 0279                    throw new ArgumentException("invalid Method 'Global' at this point.");
 280                case ExtremumTypeCheckMethod.FixedRadius:
 281                default:
 0282                    samples = _fixedSamples;
 0283                    break;
 284            }
 285
 0286            for (var i = 0; i < samples.Length; i++)
 0287            {
 0288                var sample = GetSample(pointsArray, xPos, yPos, samples, i, xMax, yMax);
 0289                if (Math.Abs(sample.Z) > refZ)
 0290                    numFarther++;
 291                else
 0292                    numCloser++;
 0293            }
 294
 295
 0296            var ratioMax = ExtremumTypeCheckNumSamples > 0 ? (float)numCloser / ExtremumTypeCheckNumSamples : 0;
 0297            var ratioMin = ExtremumTypeCheckNumSamples > 0 ? (float)numFarther / ExtremumTypeCheckNumSamples : 0;
 298
 0299            if (numCloser > numFarther && ratioMax > ExtremumTypeCheckFittingPercentage)
 0300            {
 0301                return new ExtremumDescription
 0302                {
 0303                    Type = ExtremumType.Maximum,
 0304                    NumFittingPoints = numCloser,
 0305                    PercentageFittingPoints = ratioMax
 0306                };
 307            }
 308
 0309            if (numFarther > numCloser && ratioMin > ExtremumTypeCheckFittingPercentage)
 0310            {
 0311                return new ExtremumDescription
 0312                {
 0313                    Type = ExtremumType.Minimum,
 0314                    NumFittingPoints = numFarther,
 0315                    PercentageFittingPoints = ratioMin
 0316                };
 317            }
 318
 0319            return new ExtremumDescription
 0320            {
 0321                Type = ExtremumType.Undefined,
 0322                NumFittingPoints = Math.Max(numCloser, numFarther),
 0323                PercentageFittingPoints = Math.Max(ratioMin, ratioMax)
 0324            };
 325
 0326        }
 327
 328        protected List<Interaction> ConvertDepthValue(List<Interaction> interactions)
 0329        {
 0330            var result = new List<Interaction>();
 331
 0332            foreach (var item in interactions)
 0333            {
 0334                if (item == null)
 0335                    continue;
 336
 0337                if (item.Position.Z < Distance)
 0338                {
 0339                    var depth = (Distance - item.Position.Z - MinDistance) / (MinDistance - MaxDistance);
 340
 0341                    if (depth > 0)
 0342                        continue;
 343
 0344                    if (depth < -1)
 0345                        depth = -1;
 346
 0347                    item.Position.Z = depth;
 0348                }
 349                else
 0350                {
 0351                    var depth = (item.Position.Z - MinDistance - Distance) / (MaxDistance - MinDistance);
 352
 0353                    if (depth < 0)
 0354                        continue;
 355
 0356                    if (depth > 1)
 0357                        depth = 1;
 358
 0359                    item.Position.Z = depth;
 0360                }
 361
 0362                result.Add(item);
 0363            }
 364
 0365            return result;
 0366        }
 367
 368        protected IEnumerable<Interaction> ApplyConfidenceFilter(IEnumerable<Interaction> interactions)
 0369        {
 0370            return interactions.ToArray().Where(item => item.Confidence > MinConfidence);
 0371        }
 372
 373        protected List<Interaction> ComputeExtremumType(List<Interaction> interactions, Point3[][] pointsArray)
 0374        {
 0375            interactions.ForEach(interaction =>
 0376                interaction.ExtremumDescription = ComputeExtremumType(pointsArray, (int)interaction.Position.X, (int)int
 377
 0378            return interactions;
 0379        }
 380
 381        /// <summary>
 382        /// generic approach: removes all maximums below the surface and all minimums above the surface. Removal is base
 383        /// Points witch <see cref="ExtremumType.Undefined"/> are also removed.
 384        /// </summary>
 385        /// <param name="interactions">the list of preprocessed interactions with valid ExtremumDescription</param>
 386        /// <returns></returns>
 387        protected List<Interaction> RemoveExtremumsBetweenTouches(List<Interaction> interactions)
 0388        {
 0389          return interactions.Where((touch) =>
 0390            // invalid or undefined ExtremumType
 0391            touch.ExtremumDescription != null && touch.ExtremumDescription.Type != ExtremumType.Undefined &&
 0392            // minimums below the surface
 0393            ((touch.Position.Z < 0 && touch.ExtremumDescription.Type == ExtremumType.Minimum) ||
 0394             // maximums above the surface
 0395             (touch.Position.Z > 0 && touch.ExtremumDescription.Type == ExtremumType.Maximum))
 0396
 0397          ).ToList();
 0398        }
 399
 400        protected void UpdatePerformanceMetrics(ProcessPerformance perfItem)
 0401        {
 0402            var pData = new PerformanceDataItem
 0403            {
 0404                FrameId = Convert.ToInt32(FrameId),
 0405                FrameStart = DateTime.Now.Ticks,
 0406                Process = perfItem,
 0407                Stage = PerformanceDataStage.ProcessingData
 0408            };
 409
 0410            PerformanceDataUpdated?.Invoke(this, pData);
 0411        }
 412
 413        protected void UpdateInteractionFrames(List<Interaction> cleanedUpInteractions, InteractionFrame currentFrame)
 0414        {
 0415          currentFrame.Interactions.ForEach((interaction) =>
 0416          {
 0417            if (cleanedUpInteractions.FirstOrDefault((i) => i.TouchId == interaction.TouchId) == null)
 0418            {
 0419              if (interaction.Confidence > 0)
 0420                interaction.Confidence--;
 0421            }
 0422          });
 423
 0424          currentFrame.Interactions.RemoveAll((interaction) => interaction.Confidence <= 0);
 425
 0426          _smoothingBehaviour.UpdateCachedFrame(currentFrame);
 0427          InteractionHistoryUpdated?.Invoke(this, _smoothingBehaviour.InteractionsFramesCache);
 0428        }
 429
 430        /// <summary>
 431        /// Calculates the average distance for the currently stored PointCloud.
 432        /// </summary>
 433        /// <returns></returns>
 434        public float CalculateAverageDistance()
 0435        {
 0436            var sum = 0f;
 0437            var points = PointCloud?.AsArray();
 438
 0439            if (points == null)
 0440                return sum;
 441
 0442            var numValidSamples = 0;
 443
 0444            for (var i = 0; i < points.Length; ++i)
 0445            {
 0446                if (!points[i].IsValid || points[i].IsFiltered)
 0447                    continue;
 448
 0449                numValidSamples++;
 0450                sum += points[i].Z;
 0451            }
 452
 0453            return (float) decimal.Round((decimal) (sum / numValidSamples), 2);
 0454        }
 455
 456        private static Point3 GetSample(Point3[][] pointsArray, int xPos, int yPos, Tuple<int, int>[] samples, int i, in
 0457        {
 0458            var xIdx = xPos + samples[i].Item1;
 0459            xIdx = Math.Min(Math.Max(xIdx, 0), xMax);
 460
 0461            var yIdx = yPos + samples[i].Item2;
 0462            yIdx = Math.Min(Math.Max(yIdx, 0), yMax);
 0463            var sample = pointsArray[xIdx][yIdx];
 464
 0465            return sample;
 0466        }
 467
 468        private Tuple<int, int>[] GenerateSamples()
 0469        {
 0470            var result = new List<Tuple<int, int>>();
 471
 0472            var rnd = new Random();
 473
 0474            var min = (int)Math.Floor(0.5f * ExtremumTypeCheckRadius);
 0475            var max = ExtremumTypeCheckRadius;
 476
 0477            for (var i = 0; i < ExtremumTypeCheckNumSamples; i++)
 0478            {
 0479                var xStochastic = rnd.Next(min, max);
 0480                var yStochastic = rnd.Next(min, max);
 481
 0482                result.Add(new Tuple<int, int>(xStochastic, yStochastic));
 0483            }
 484
 0485            return result.ToArray();
 0486        }
 487
 488        private void UpdateSamples()
 0489        {
 0490            var samplesFixed = new List<Tuple<int, int>>();
 491
 0492            for (var i = 0; i < ExtremumTypeCheckNumSamples; i++)
 0493            {
 0494                var p = (float)i / ExtremumTypeCheckRadius;
 0495                var xFixed = (int) Math.Floor(ExtremumTypeCheckRadius * Math.Cos(p * 2 * Math.PI));
 0496                var yFixed = (int) Math.Floor(ExtremumTypeCheckRadius * Math.Sin(p * 2 * Math.PI));
 497
 0498                samplesFixed.Add(new Tuple<int, int>(xFixed, yFixed));
 0499            }
 0500            _fixedSamples = samplesFixed.ToArray();
 501
 0502            _stochasticSamples = GenerateSamples();
 0503        }
 504    }
 505}

Methods/Properties

.ctor()
get_Distance()
get_MinDistance()
get_MaxDistance()
get_ExtremumTypeCheckNumSamples()
set_ExtremumTypeCheckNumSamples(System.Int32)
get_ExtremumTypeCheckRadius()
set_ExtremumTypeCheckRadius(System.Int32)
get_ExtremumTypeCheckFittingPercentage()
get_ExtremumTypeCheckMethod()
get_InteractionHistorySize()
set_InteractionHistorySize(System.Int32)
get_NumSmoothingFrames()
set_NumSmoothingFrames(System.Int32)
get_MaxNumEmptyFramesBetween()
set_MaxNumEmptyFramesBetween(System.Int32)
get_TouchMergeDistance2D()
set_TouchMergeDistance2D(System.Single)
get_DepthScale()
set_DepthScale(System.Single)
get_FilterType()
set_FilterType(ReFlex.Core.Interactivity.Util.FilterType)
get_MinAngle()
get_MinConfidence()
get_MaxConfidence()
set_MaxConfidence(System.Single)
get_MeasurePerformance()
get_FrameId()
UpdateFrameId(System.Int32)
ComputeSmoothingValue(System.Collections.Generic.IList`1<ReFlex.Core.Common.Components.Interaction>)
ComputeExtremumType(ReFlex.Core.Common.Components.Point3[][],System.Int32,System.Int32)
ConvertDepthValue(System.Collections.Generic.List`1<ReFlex.Core.Common.Components.Interaction>)
ApplyConfidenceFilter(System.Collections.Generic.IEnumerable`1<ReFlex.Core.Common.Components.Interaction>)
ComputeExtremumType(System.Collections.Generic.List`1<ReFlex.Core.Common.Components.Interaction>,ReFlex.Core.Common.Components.Point3[][])
RemoveExtremumsBetweenTouches(System.Collections.Generic.List`1<ReFlex.Core.Common.Components.Interaction>)
UpdatePerformanceMetrics(ReFlex.Core.Common.Util.ProcessPerformance)
UpdateInteractionFrames(System.Collections.Generic.List`1<ReFlex.Core.Common.Components.Interaction>,ReFlex.Core.Common.Components.InteractionFrame)
CalculateAverageDistance()
GetSample(ReFlex.Core.Common.Components.Point3[][],System.Int32,System.Int32,System.Tuple`2<System.Int32,System.Int32>[],System.Int32,System.Int32,System.Int32)
GenerateSamples()
UpdateSamples()