Finding ellipses and parasites.
[master-thesis.git] / Parasitemia / Parasitemia / MatchingEllipses.fs
index e4eae8d..afc4ab7 100644 (file)
@@ -2,55 +2,95 @@
 
 open System
 open System.Linq
+open System.Collections
 open System.Collections.Generic
 
 open Types
 open Utils
 
 
-type EllipseScore = { mutable matchingScore: float; e: Ellipse }
+let matchingScoreThreshold1 = 0.6
+let matchingScoreThreshold2 = 1.0
 
-let matchingScoreThreshold1 = 0.5
-let matchingScoreThreshold2 = 2.0
+type EllipseScore (matchingScore: float, e: Ellipse) =
+    let mutable matchingScore = matchingScore
 
-let ellipseArea e = e.a * e.b * Math.PI
+    member this.MatchingScore = matchingScore
+    member this.Ellipse = e
+    member val Processed = false with get, set
+    member val Removed = false with get, set
 
-type MatchingEllipses () =
+    member this.AddMatchingScore(score: float) =
+        matchingScore <- matchingScore + score
+    
+    interface KdTree.I2DCoords with    
+        member this.X = e.Cx
+        member this.Y = e.Cy
+        
+type MatchingEllipses (radiusMin: float) =
     let ellipses = List<EllipseScore>()
         
     member this.Add (e: Ellipse) = 
-        
-        // dprintfn "new ellipse: %A, nb: %A" e ellipses.Count
-
-        let areaE = ellipseArea e
-
-        let mutable matchingScoreE = 0.0
+        ellipses.Add(EllipseScore(0.0, e))
 
-        for other in ellipses do
-            let areaOther = ellipseArea other.e
-            let commonArea = EEOver.EEOverlapArea e other.e
-            let matchingScore = (2.0 * commonArea / (areaE + areaOther)) ** 2.0
-            if matchingScore >= matchingScoreThreshold1
+    member this.Ellipses : Ellipse list =
+        // 1) Create a kd-tree from the ellipses list.
+        let tree = KdTree.Tree.buildTree (List.ofSeq ellipses)
+
+        // 2) Compute the matching score of each ellipses.       
+        let windowSize = radiusMin
+        for e in ellipses do
+            e.Processed <- true
+            let areaE = e.Ellipse.Area
+            let window = { KdTree.minX = e.Ellipse.Cx - windowSize / 2.0
+                           KdTree.maxX = e.Ellipse.Cx + windowSize / 2.0 
+                           KdTree.minY = e.Ellipse.Cy - windowSize / 2.0
+                           KdTree.maxY = e.Ellipse.Cy + windowSize / 2.0 }
+            for other in KdTree.Tree.search tree window do
+                if not other.Processed
+                then
+                    let areaOther = other.Ellipse.Area
+                    let commonArea = EEOver.EEOverlapArea e.Ellipse other.Ellipse
+                    let matchingScore = 2.0 * commonArea / (areaE + areaOther)
+                    if matchingScore >= matchingScoreThreshold1
+                    then
+                        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 wich have a low score. 
+        let i = ellipses.BinarySearch(EllipseScore(matchingScoreThreshold2, Ellipse(0.0, 0.0, 0.0, 0.0, 0.0)),
+                                      { new IComparer<EllipseScore> 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
+        for e in ellipses do
+            if not e.Removed
             then
-                other.matchingScore <- other.matchingScore + matchingScore
-                matchingScoreE <- matchingScoreE + matchingScore
-            printfn "Score"
-
-        ellipses.Add({ matchingScore = matchingScoreE; e = e })
+                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 KdTree.Tree.search tree window do
+                    if not other.Removed && other.MatchingScore < e.MatchingScore
+                    then            
+                        if e.Ellipse.Contains other.Ellipse.Cx other.Ellipse.Cy
+                        then
+                            other.Removed <- true
+        ellipses.RemoveAll(fun e -> e.Removed) |> ignore
 
-    member this.Ellipses : Ellipse list =
         dprintfn "Number of ellipse: %A" ellipses.Count
 
-        (*let sortedEllipses = 
-            List.filter (fun e -> e.matchingScore >= matchingScoreThreshold2) (List.ofSeq ellipses)
-            |> List.sortWith (fun e1 e2 -> e2.matchingScore.CompareTo(e1.matchingScore))*)
-
-        List.filter (fun e -> e.matchingScore >= matchingScoreThreshold2) (List.ofSeq ellipses)
-        |> List.sortWith (fun e1 e2 -> e2.matchingScore.CompareTo(e1.matchingScore))
-        |> List.map (fun { e = e } -> e)
+        List.ofSeq ellipses |> List.map (fun e -> e.Ellipse)
 
-        // ellipses.Where(fun e -> e.matchingScore >= matchingScoreThreshold2)        
-        // ([], fun acc { matchingScore = score; e = e } -> if score >= matchingScoreThreshold2 then e :: acc else acc)