1
module ParasitemiaCore.MatchingEllipses
5 open System.Collections
6 open System.Collections.Generic
11 type private EllipseScoreFlaggedKd (matchingScore
: float32
, e
: Ellipse) =
12 let mutable matchingScore = matchingScore
14 member this
.Ellipse = e
16 member this
.MatchingScore = matchingScore
18 member this
.AddMatchingScore (score
: float32
) =
19 matchingScore <- matchingScore + score
21 member val Processed = false with get
, set
22 member val Removed = false with get
, set
24 interface KdTree.I2DCoords with
25 member this
.X = this
.Ellipse.Cx
26 member this
.Y = this
.Ellipse.Cy
28 type MatchingEllipses (radius
: float32
) =
29 let ellipses = List<EllipseScoreFlaggedKd>()
31 // All ellipses with a score below this are removed.
32 let matchingScoreThreshold = 0.8f
34 member this
.Add (e
: Ellipse) =
35 ellipses.Add(EllipseScoreFlaggedKd(0.f
, e
))
37 member this
.Ellipses : Ellipse list =
38 List.ofSeq
ellipses |> List.map
(fun e
-> e
.Ellipse)
40 member this
.PrunedEllipses : Ellipse list =
45 // 1) Create a kd-tree from the ellipses list.
46 let tree = KdTree.Tree.BuildTree (List.ofSeq
ellipses)
48 // 2) Compute the matching score of each ellipses.
49 let windowSize = radius
/ 2.f
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
60 let areaOther = other.Ellipse.Area
61 match EEOver.EEOverlapArea e
.Ellipse other.Ellipse with
62 | Some (overlapArea
, _
, _
) ->
63 let matchingScore = (2.f
* overlapArea
/ (areaE + areaOther)) ** 30.f
64 if matchingScore <= 1.f
// For approximation error.
66 other.AddMatchingScore(matchingScore)
67 e
.AddMatchingScore(matchingScore)
70 // 3) Remove ellipses whose center is near the center of another ellipse with a better score.
72 if e
.MatchingScore < matchingScoreThreshold
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
83 // Case where ellipses are too close.
84 if distanceTwoPoints
(PointD(e
.Ellipse.Cx, e
.Ellipse.Cy)) (PointD(other.Ellipse.Cx, other.Ellipse.Cy)) < 0.3f * e
.Ellipse.B
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
->
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)