public double RMSE(PoseEstimate other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            double result = this.MSE(other);

            result = Math.Sqrt(result);
            return(result);
        }
        public double MSE(PoseEstimate other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            double result = Math.Pow(this.Dx - other.Dx, 2);

            result += Math.Pow(this.Dy - other.Dy, 2);
            result += Math.Pow(this.Ds - other.Ds, 2);
            result += Math.Pow(this.Do - other.Do, 2);

            result /= 4.0;

            return(result);
        }
        private List <MDMatch> clusterBasedOnPoseEstimation(List <MDMatch> matches, KeyPoints queryKeyPoints, PreProcessedImage trainingData)
        {
            // If no training data is provided then we cannot compute pose (LeafAnalysisV1), hence just return back the original set
            if (trainingData == null ||
                matches == null ||
                !matches.Any())
            {
                return(matches);
            }

            Dictionary <MDMatch, List <MDMatch> > clusters = new Dictionary <MDMatch, List <MDMatch> >(matches.Count);
            List <PoseEstimate> poseEstimates = new List <PoseEstimate>(matches.Count);

            foreach (MDMatch match in matches)
            {
                MKeyPoint queryKeyPoint    = queryKeyPoints.Points[match.QueryIdx];
                MKeyPoint trainingKeyPoint = trainingData.KeyPoints.Points[match.TrainIdx];

                PoseEstimate estimate = new PoseEstimate();
                estimate.Match = match;
                estimate.Dx    = trainingKeyPoint.Point.X - queryKeyPoint.Point.X;
                estimate.Dy    = trainingKeyPoint.Point.Y - queryKeyPoint.Point.Y;
                estimate.Ds    = trainingKeyPoint.Octave / queryKeyPoint.Octave;
                estimate.Do    = trainingKeyPoint.Angle - queryKeyPoint.Angle;

                poseEstimates.Add(estimate);
                // Initialize clusters for each individual match
                // Next we will add other matches which belong to this cluster
                clusters.Add(match, new List <MDMatch>(new MDMatch[] { match }));
            }

            const double errorThreshold = 5;

            // Compute cluster membership
            foreach (PoseEstimate estimate in poseEstimates)
            {
                foreach (PoseEstimate otherEstimate in poseEstimates)
                {
                    // Ignore self
                    if (estimate == otherEstimate)
                    {
                        continue;
                    }

                    double error = estimate.RMSE(otherEstimate);
                    //Console.WriteLine("Error: " + trainingData.Category + ": " + error);
                    if (error < errorThreshold)
                    {
                        clusters[estimate.Match].Add(otherEstimate.Match);
                    }
                }
            }

            // Finally pick the largest cluster
            List <MDMatch> result        = null;
            int            sizeOfCluster = -1;;

            foreach (KeyValuePair <MDMatch, List <MDMatch> > cluster in clusters)
            {
                if (cluster.Value.Count == sizeOfCluster)
                {
                    // Tie breaker: choose the cluster with smaller overall distances
                    if (result.Sum(item => item.Distance) > cluster.Value.Sum(item => item.Distance))
                    {
                        result = cluster.Value;
                    }
                }
                else if (cluster.Value.Count > sizeOfCluster)
                {
                    sizeOfCluster = cluster.Value.Count;
                    result        = cluster.Value;
                }
            }

            return(result);
        }