* Add an exact method to compute an ellipse from three points and two tangents.
[master-thesis.git] / Parasitemia / Parasitemia / MatchingEllipses.fs
index 6e57199..3599bb5 100644 (file)
@@ -8,14 +8,6 @@ open System.Collections.Generic
 open Types
 open Utils
 
-
-// Do not take in account matching score below this when two ellipses are matched.
-[<Literal>]
-let matchingScoreThreshold1 = 0.6f
-
-[<Literal>]
-let scaleOverlapTest = 0.8f
-
 type private EllipseScoreFlaggedKd (matchingScore: float32, e: Ellipse) =
     let mutable matchingScore = matchingScore
 
@@ -23,7 +15,7 @@ type private EllipseScoreFlaggedKd (matchingScore: float32, e: Ellipse) =
 
     member this.MatchingScore = matchingScore
 
-    member this.AddMatchingScore(score: float32) =
+    member this.AddMatchingScore (score: float32) =
         matchingScore <- matchingScore + score
 
     member val Processed = false with get, set
@@ -33,12 +25,11 @@ type private EllipseScoreFlaggedKd (matchingScore: float32, e: Ellipse) =
         member this.X = this.Ellipse.Cx
         member this.Y = this.Ellipse.Cy
 
-
-type MatchingEllipses (radiusMin: float32) =
+type MatchingEllipses (radius: float32) =
     let ellipses = List<EllipseScoreFlaggedKd>()
 
     // All ellipses with a score below this are removed.
-    let matchingScoreThreshold2 = 20.f * radiusMin
+    let matchingScoreThreshold = 1.f
 
     member this.Add (e: Ellipse) =
         ellipses.Add(EllipseScoreFlaggedKd(0.f, e))
@@ -46,7 +37,6 @@ type MatchingEllipses (radiusMin: float32) =
     member this.Ellipses : Ellipse list =
         List.ofSeq ellipses |> List.map (fun e -> e.Ellipse)
 
-    // Process all ellipses and return ellipses ordered from the best score to the worst.
     member this.PrunedEllipses : Ellipse list =
         if ellipses.Count = 0
         then
@@ -56,7 +46,7 @@ type MatchingEllipses (radiusMin: float32) =
             let tree = KdTree.Tree.BuildTree (List.ofSeq ellipses)
 
             // 2) Compute the matching score of each ellipses.
-            let windowSize = radiusMin
+            let windowSize = radius / 2.f
             for e in ellipses do
                 e.Processed <- true
                 let areaE = e.Ellipse.Area
@@ -69,45 +59,33 @@ type MatchingEllipses (radiusMin: float32) =
                     then
                         let areaOther = other.Ellipse.Area
                         match EEOver.EEOverlapArea e.Ellipse other.Ellipse with
-                        | Some (commonArea, _, _) ->
-                            let matchingScore = 2.f * commonArea / (areaE + areaOther)
-                            if matchingScore >= matchingScoreThreshold1
+                        | Some (overlapArea, _, _) ->
+                            let matchingScore = (2.f * overlapArea / (areaE + areaOther)) ** 20.f
+                            if matchingScore <= 1.f // For approximation error.
                             then
-                                other.AddMatchingScore(matchingScore * e.Ellipse.Perimeter)
-                                e.AddMatchingScore(matchingScore * other.Ellipse.Perimeter)
+                                other.AddMatchingScore(matchingScore)
+                                e.AddMatchingScore(matchingScore)
                         | _ -> ()
 
-            // 3) Sort ellipses by their score.
-            ellipses.Sort(fun e1 e2 -> e2.MatchingScore.CompareTo(e1.MatchingScore))
-
-            // 4) Remove ellipses with a low score.
-            let i = ellipses.BinarySearch(EllipseScoreFlaggedKd(matchingScoreThreshold2, Ellipse(0.f, 0.f, 0.f, 0.f, 0.f)),
-                                          { new IComparer<EllipseScoreFlaggedKd> with
-                                                member this.Compare(e1, e2) = e2.MatchingScore.CompareTo(e1.MatchingScore) }) |> abs
-            let nbToRemove = ellipses.Count - i
-            if nbToRemove > 0
-            then
-                for j in i .. nbToRemove - 1 do
-                    ellipses.[j].Removed <- true
-                ellipses.RemoveRange(i, nbToRemove)
-
-            // 5) Remove ellipses whose center is into an ellipse with a better score
+            // 3) Remove ellipses whose center is near the center of another ellipse with a better score.
             for e in ellipses do
-                if not e.Removed
+                if e.MatchingScore < matchingScoreThreshold
                 then
+                    e.Removed <- true
+                else
                     let window = { KdTree.minX = e.Ellipse.Cx - e.Ellipse.A
                                    KdTree.maxX = e.Ellipse.Cx + e.Ellipse.A
                                    KdTree.minY = e.Ellipse.Cy - e.Ellipse.A
                                    KdTree.maxY = e.Ellipse.Cy + e.Ellipse.A }
                     for other in tree.Search window do
-                        if not other.Removed && other.MatchingScore < e.MatchingScore
+                        if not other.Removed && e.MatchingScore > other.MatchingScore &&
+                            distanceTwoPoints (PointD(e.Ellipse.Cx, e.Ellipse.Cy)) (PointD(other.Ellipse.Cx, other.Ellipse.Cy)) < 0.3f * e.Ellipse.B
                         then
-                            if e.Ellipse.Scale(scaleOverlapTest).Contains other.Ellipse.Cx other.Ellipse.Cy
-                            then
-                                other.Removed <- true
-            ellipses.RemoveAll(fun e -> e.Removed) |> ignore
-
-            List.ofSeq ellipses |> List.map (fun e -> e.Ellipse)
-
+                            other.Removed <- true
 
+            ellipses
+            |> List.ofSeq
+            |> List.filter (fun e -> not e.Removed)
+            |> List.sortWith (fun e1 e2 -> e2.MatchingScore.CompareTo(e1.MatchingScore))
+            |> List.map (fun e -> e.Ellipse)