1
module ParasitemiaCore.MatchingEllipses
6 open System.Collections
7 open System.Collections.Generic
12 type private EllipseScoreFlaggedKd (matchingScore
: float32
, e
: Ellipse) =
13 let mutable matchingScore = matchingScore
15 member this
.Ellipse = e
17 member this
.MatchingScore = matchingScore
19 member this
.AddMatchingScore (score
: float32
) =
20 matchingScore <- matchingScore + score
22 member val Processed = false with get
, set
23 member val Removed = false with get
, set
25 interface KdTree.I2DCoords with
26 member this
.X = this
.Ellipse.Cx
27 member this
.Y = this
.Ellipse.Cy
29 type MatchingEllipses (radius
: float32
) =
30 let ellipses = List<EllipseScoreFlaggedKd>()
32 // All ellipses with a score below this are removed.
33 let matchingScoreThreshold = 0.4f
35 member this
.Add (e
: Ellipse) =
36 ellipses.Add(EllipseScoreFlaggedKd(0.f
, e
))
38 member this
.Ellipses : Ellipse list =
39 List.ofSeq
ellipses |> List.map
(fun e
-> e
.Ellipse)
41 member this
.PrunedEllipses : Ellipse list =
46 // 1) Create a kd-tree from the ellipses list.
47 let tree = KdTree.Tree.BuildTree (List.ofSeq
ellipses)
49 // 2) Compute the matching score of each ellipses.
50 let windowSize = radius
/ 3.f
53 let areaE = e
.Ellipse.Area
54 let window = { KdTree.minX
= e
.Ellipse.Cx - windowSize / 2.f
55 KdTree.maxX
= e
.Ellipse.Cx + windowSize / 2.f
56 KdTree.minY
= e
.Ellipse.Cy - windowSize / 2.f
57 KdTree.maxY
= e
.Ellipse.Cy + windowSize / 2.f
}
58 for other
in tree.Search window do
59 if not
other.Processed
61 let areaOther = other.Ellipse.Area
62 match EEOver.EEOverlapArea e
.Ellipse other.Ellipse with
63 | Some (overlapArea
, _
, _
)
64 // Because of approximation error, see https://github.com/chraibi/EEOver/issues/4
65 when overlapArea
- areaE < 1.f
&& overlapArea
- areaOther < 1.f
->
66 let matchingScore = (2.f
* overlapArea
/ (areaE + areaOther)) ** 30.f
67 other.AddMatchingScore(matchingScore)
68 e
.AddMatchingScore(matchingScore)
71 // 3) Remove ellipses whose center is near the center of another ellipse with a better score.
73 if e
.MatchingScore < matchingScoreThreshold
77 let window = { KdTree.minX
= e
.Ellipse.Cx - e
.Ellipse.A
78 KdTree.maxX
= e
.Ellipse.Cx + e
.Ellipse.A
79 KdTree.minY
= e
.Ellipse.Cy - e
.Ellipse.A
80 KdTree.maxY
= e
.Ellipse.Cy + e
.Ellipse.A }
81 for other in tree.Search window do
82 if not
other.Removed && e
.MatchingScore > other.MatchingScore
84 // Case where ellipses are too close.
85 if distanceTwoPoints
(PointF(e
.Ellipse.Cx, e
.Ellipse.Cy)) (PointF(other.Ellipse.Cx, other.Ellipse.Cy)) < 0.3f * e
.Ellipse.B
89 // Case where ellipses are overlapped.
90 match EEOver.EEOverlapArea e
.Ellipse other.Ellipse with
91 | Some (overlapArea
, _, _) when e
.Ellipse.Area < 1.1f * overlapArea
|| other.Ellipse.Area < 1.1f * overlapArea
->
97 |> List.filter
(fun e
-> not
e.Removed)
98 |> List.sortWith
(fun e1 e2
-> e2
.MatchingScore.CompareTo(e1
.MatchingScore))
99 |> List.map
(fun e -> e.Ellipse)