ec10d934ca1319745a29a376bf81e0e4d3b5c879
[master-thesis.git] / Parasitemia / ParasitemiaCore / MatchingEllipses.fs
1 module ParasitemiaCore.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 type private EllipseScoreFlaggedKd (matchingScore: float32, e: Ellipse) =
12 let mutable matchingScore = matchingScore
13
14 member this.Ellipse = e
15
16 member this.MatchingScore = matchingScore
17
18 member this.AddMatchingScore (score: float32) =
19 matchingScore <- matchingScore + score
20
21 member val Processed = false with get, set
22 member val Removed = false with get, set
23
24 interface KdTree.I2DCoords with
25 member this.X = this.Ellipse.Cx
26 member this.Y = this.Ellipse.Cy
27
28 type MatchingEllipses (radius: float32) =
29 let ellipses = List<EllipseScoreFlaggedKd>()
30
31 // All ellipses with a score below this are removed.
32 let matchingScoreThreshold = 0.4f // 0.5f
33
34 member this.Add (e: Ellipse) =
35 ellipses.Add(EllipseScoreFlaggedKd(0.f, e))
36
37 member this.Ellipses : Ellipse list =
38 List.ofSeq ellipses |> List.map (fun e -> e.Ellipse)
39
40 member this.PrunedEllipses : Ellipse list =
41 if ellipses.Count = 0
42 then
43 []
44 else
45 // 1) Create a kd-tree from the ellipses list.
46 let tree = KdTree.Tree.BuildTree (List.ofSeq ellipses)
47
48 // 2) Compute the matching score of each ellipses.
49 let windowSize = radius / 2.f
50 for e in ellipses do
51 e.Processed <- true
52 let areaE = e.Ellipse.Area
53 let window = { KdTree.minX = e.Ellipse.Cx - windowSize / 2.f
54 KdTree.maxX = e.Ellipse.Cx + windowSize / 2.f
55 KdTree.minY = e.Ellipse.Cy - windowSize / 2.f
56 KdTree.maxY = e.Ellipse.Cy + windowSize / 2.f }
57 for other in tree.Search window do
58 if not other.Processed
59 then
60 let areaOther = other.Ellipse.Area
61 match EEOver.EEOverlapArea e.Ellipse other.Ellipse with
62 | Some (overlapArea, _, _)
63 // Because of approximation error, see https://github.com/chraibi/EEOver/issues/4
64 when overlapArea - areaE < 1.f && overlapArea - areaOther < 1.f ->
65 let matchingScore = (2.f * overlapArea / (areaE + areaOther)) ** 30.f
66 other.AddMatchingScore(matchingScore)
67 e.AddMatchingScore(matchingScore)
68 | _ -> ()
69
70 // 3) Remove ellipses whose center is near the center of another ellipse with a better score.
71 for e in ellipses do
72 if e.MatchingScore < matchingScoreThreshold
73 then
74 e.Removed <- true
75 else
76 let window = { KdTree.minX = e.Ellipse.Cx - e.Ellipse.A
77 KdTree.maxX = e.Ellipse.Cx + e.Ellipse.A
78 KdTree.minY = e.Ellipse.Cy - e.Ellipse.A
79 KdTree.maxY = e.Ellipse.Cy + e.Ellipse.A }
80 for other in tree.Search window do
81 if not other.Removed && e.MatchingScore > other.MatchingScore
82 then
83 // Case where ellipses are too close.
84 if distanceTwoPoints (PointF(e.Ellipse.Cx, e.Ellipse.Cy)) (PointF(other.Ellipse.Cx, other.Ellipse.Cy)) < 0.3f * e.Ellipse.B
85 then
86 other.Removed <- true
87 else
88 // Case where ellipses are overlapped.
89 match EEOver.EEOverlapArea e.Ellipse other.Ellipse with
90 | Some (overlapArea, _, _) when e.Ellipse.Area < 1.1f * overlapArea || other.Ellipse.Area < 1.1f * overlapArea ->
91 other.Removed <- true
92 | _ ->
93 ()
94 ellipses
95 |> List.ofSeq
96 |> List.filter (fun e -> not e.Removed)
97 |> List.sortWith (fun e1 e2 -> e2.MatchingScore.CompareTo(e1.MatchingScore))
98 |> List.map (fun e -> e.Ellipse)
99