public List <int> GetHeadCandidates()
        {
            List <int> possibleHeadCandidates = SlideWindow(GlobVar.HaarOuterWindowWidth, GlobVar.HaarInnerWindowWidth,
                                                            Thresholds.HaarDifferenceBetweenInnerAndOuterRectangle);

            DisjointSet groupedHeadCandidates = GroupCandidates(possibleHeadCandidates);

            List <int> filteredHeadCandidates = FilterCandidates(groupedHeadCandidates);

            return(filteredHeadCandidates);
        }
        /// <summary>
        /// Filters the candidate points. Only the candidates that has a certain amount of possible candidates in the neighborhood area are kept.
        /// </summary>
        private static List <int> FilterCandidates(DisjointSet disjointSet)
        {
            var setRepresentatives = new List <int>();

            for (int i = 0; i < disjointSet.Count; i++)
            {
                int setRepresentative = disjointSet.Find(i);
                if (!setRepresentatives.Contains(setRepresentative) && disjointSet.SetSize(setRepresentative) > Thresholds.HaarCandidateMinCount)
                {
                    setRepresentatives.Add(setRepresentative);
                }
            }

            var highestCandidates = new List <int>();

            foreach (var representative in setRepresentatives)
            {
                highestCandidates.Add(disjointSet.HighestIndexInTree[representative]);
            }

            return(highestCandidates);
        }
        /// <summary>
        /// Groups the candidates if the distance between them in the XY-plane is below threshold.
        /// </summary>
        private static DisjointSet GroupCandidates(List <int> candidatePoints)
        {
            DisjointSet groupedSet = new DisjointSet(candidatePoints);

            for (int i = 0; i < candidatePoints.Count; i++)
            {
                for (int j = 0; j < candidatePoints.Count; j++)
                {
                    if (i == j)
                    {
                        continue;
                    }
                    ;
                    if (GlobUtils.GetEuclideanDistanceXYPlane(candidatePoints[i], candidatePoints[j]) < Thresholds.HaarMaxDistanceBetweenCandidates)
                    {
                        groupedSet.Union(i, j);
                    }
                }
            }

            return(groupedSet);
        }
        /// <summary>
        /// Filters the candidate points. Only the candidates that has a certain amount of possible candidates in the neighborhood area are kept.
        /// </summary>
        private static List<int> FilterCandidates(DisjointSet disjointSet)
        {
            var setRepresentatives = new List<int>();

            for (int i = 0; i < disjointSet.Count; i++)
            {
                int setRepresentative = disjointSet.Find(i);
                if (!setRepresentatives.Contains(setRepresentative) && disjointSet.SetSize(setRepresentative) > Thresholds.HaarCandidateMinCount)
                {
                    setRepresentatives.Add(setRepresentative);
                }
            }

            var highestCandidates = new List<int>();

            foreach (var representative in setRepresentatives)
            {
                highestCandidates.Add(disjointSet.HighestIndexInTree[representative]);
            }

            return highestCandidates;
        }
        /// <summary>
        /// Groups the candidates if the distance between them in the XY-plane is below threshold.
        /// </summary>
        private static DisjointSet GroupCandidates(List<int> candidatePoints)
        {
            DisjointSet groupedSet = new DisjointSet(candidatePoints);

            for (int i = 0; i < candidatePoints.Count; i++)
            {
                for (int j = 0; j < candidatePoints.Count; j++)
                {
                    if (i == j) { continue; };
                    if (GlobUtils.GetEuclideanDistanceXYPlane(candidatePoints[i], candidatePoints[j]) < Thresholds.HaarMaxDistanceBetweenCandidates)
                    {
                        groupedSet.Union(i, j);
                    }
                }
            }

            return groupedSet;
        }