The main process is now complete.
[master-thesis.git] / Parasitemia / Parasitemia / MatchingEllipses.fs
1 module MatchingEllipses
2
3 open System
4 open System.Linq
5 open System.Collections
6 open System.Collections.Generic
7
8 open Types
9 open Utils
10
11
12 let matchingScoreThreshold1 = 0.6
13 let matchingScoreThreshold2 = 1.0
14
15 type private EllipseScoreFlaggedKd (matchingScore: float, e: Ellipse) =
16 let mutable matchingScore = matchingScore
17
18 member this.Ellipse = e
19
20 member this.MatchingScore = matchingScore
21 member this.AddMatchingScore(score: float) =
22 matchingScore <- matchingScore + score
23
24 member val Processed = false with get, set
25 member val Removed = false with get, set
26
27 interface KdTree.I2DCoords with
28 member this.X = this.Ellipse.Cx
29 member this.Y = this.Ellipse.Cy
30
31
32 type MatchingEllipses (radiusMin: float) =
33 let ellipses = List<EllipseScoreFlaggedKd>()
34
35 member this.Add (e: Ellipse) =
36 ellipses.Add(EllipseScoreFlaggedKd(0.0, e))
37
38 member this.Ellipses : Ellipse list =
39 if ellipses.Count = 0
40 then
41 []
42 else
43 // 1) Create a kd-tree from the ellipses list.
44 let tree = KdTree.Tree.BuildTree (List.ofSeq ellipses)
45
46 // 2) Compute the matching score of each ellipses.
47 let windowSize = radiusMin
48 for e in ellipses do
49 e.Processed <- true
50 let areaE = e.Ellipse.Area
51 let window = { KdTree.minX = e.Ellipse.Cx - windowSize / 2.0
52 KdTree.maxX = e.Ellipse.Cx + windowSize / 2.0
53 KdTree.minY = e.Ellipse.Cy - windowSize / 2.0
54 KdTree.maxY = e.Ellipse.Cy + windowSize / 2.0 }
55 for other in tree.Search window do
56 if not other.Processed
57 then
58 let areaOther = other.Ellipse.Area
59 match EEOver.EEOverlapArea e.Ellipse other.Ellipse with
60 | Some (commonArea, _, _) ->
61 let matchingScore = 2.0 * commonArea / (areaE + areaOther)
62 if matchingScore >= matchingScoreThreshold1
63 then
64 other.AddMatchingScore(matchingScore)
65 e.AddMatchingScore(matchingScore)
66 | _ -> ()
67
68 // 3) Sort ellipses by their score.
69 ellipses.Sort(fun e1 e2 -> e2.MatchingScore.CompareTo(e1.MatchingScore))
70
71 // 4) Remove ellipses wich have a low score.
72 let i = ellipses.BinarySearch(EllipseScoreFlaggedKd(matchingScoreThreshold2, Ellipse(0.0, 0.0, 0.0, 0.0, 0.0)),
73 { new IComparer<EllipseScoreFlaggedKd> with
74 member this.Compare(e1, e2) = e2.MatchingScore.CompareTo(e1.MatchingScore) }) |> abs
75 let nbToRemove = ellipses.Count - i
76 if nbToRemove > 0
77 then
78 for j in i .. nbToRemove - 1 do
79 ellipses.[j].Removed <- true
80 ellipses.RemoveRange(i, nbToRemove)
81
82 // 5) Remove ellipses whose center is into an ellipse with a better score
83 for e in ellipses do
84 if not e.Removed
85 then
86 let window = { KdTree.minX = e.Ellipse.Cx - e.Ellipse.A
87 KdTree.maxX = e.Ellipse.Cx + e.Ellipse.A
88 KdTree.minY = e.Ellipse.Cy - e.Ellipse.A
89 KdTree.maxY = e.Ellipse.Cy + e.Ellipse.A }
90 for other in tree.Search window do
91 if not other.Removed && other.MatchingScore < e.MatchingScore
92 then
93 if e.Ellipse.Contains other.Ellipse.Cx other.Ellipse.Cy
94 then
95 other.Removed <- true
96 ellipses.RemoveAll(fun e -> e.Removed) |> ignore
97
98 List.ofSeq ellipses |> List.map (fun e -> e.Ellipse)
99
100
101