99e3cb597e0807cd9d574b5acb5003f718387203
1
module MatchingEllipses
5 open System.Collections
6 open System.Collections.Generic
12 // Do not take in account matching score below this when two ellipses are matched.
13 let matchingScoreThreshold1 = 0.6
15 // All ellipsee with a score below this is removed.
16 let matchingScoreThreshold2 = 1. / 100.
18 type private EllipseScoreFlaggedKd (matchingScore
: float, e
: Ellipse) =
19 let mutable matchingScore = matchingScore
20 let perimeter = e
.Perimeter
22 member this
.Ellipse = e
24 member this
.MatchingScore = matchingScore
26 // The score is proportional to the perimeter because large ellipse will receive more votes.
27 member this
.AddMatchingScore(score: float) =
28 matchingScore <- matchingScore + score / perimeter
30 member val Processed = false with get
, set
31 member val Removed = false with get
, set
33 interface KdTree.I2DCoords with
34 member this
.X = this
.Ellipse.Cx
35 member this
.Y = this
.Ellipse.Cy
38 type MatchingEllipses (radiusMin
: float) =
39 let ellipses = List<EllipseScoreFlaggedKd>()
41 member this
.Add (e
: Ellipse) =
42 ellipses.Add(EllipseScoreFlaggedKd(0.0, e
))
44 member this
.Ellipses : Ellipse list =
45 List.ofSeq
ellipses |> List.map
(fun e
-> e
.Ellipse)
47 // Process all ellipses and return ellipses ordered from the best score to the worst.
48 member this
.PrunedEllipses : Ellipse list =
53 // 1) Create a kd-tree from the ellipses list.
54 let tree = KdTree.Tree.BuildTree (List.ofSeq
ellipses)
56 // 2) Compute the matching score of each ellipses.
57 let windowSize = radiusMin
60 let areaE = e
.Ellipse.Area
61 let window = { KdTree.minX
= e
.Ellipse.Cx - windowSize / 2.0
62 KdTree.maxX
= e
.Ellipse.Cx + windowSize / 2.0
63 KdTree.minY
= e
.Ellipse.Cy - windowSize / 2.0
64 KdTree.maxY
= e
.Ellipse.Cy + windowSize / 2.0 }
65 for other
in tree.Search window do
66 if not
other.Processed
68 let areaOther = other.Ellipse.Area
69 match EEOver.EEOverlapArea e
.Ellipse other.Ellipse with
70 | Some (commonArea
, _
, _
) ->
71 let matchingScore = 2.0 * commonArea
/ (areaE + areaOther)
72 if matchingScore >= matchingScoreThreshold1
74 other.AddMatchingScore(matchingScore)
75 e
.AddMatchingScore(matchingScore)
78 // 3) Sort ellipses by their score.
79 ellipses.Sort(fun e1 e2
-> e2
.MatchingScore.CompareTo(e1
.MatchingScore))
81 // 4) Remove ellipses wich have a low score.
82 let i = ellipses.BinarySearch(EllipseScoreFlaggedKd(matchingScoreThreshold2, Ellipse(0.0, 0.0, 0.0, 0.0, 0.0)),
83 { new IComparer<EllipseScoreFlaggedKd> with
84 member this
.Compare(e1
, e2
) = e2
.MatchingScore.CompareTo(e1
.MatchingScore) }) |> abs
85 let nbToRemove = ellipses.Count - i
88 for j
in i .. nbToRemove - 1 do
89 ellipses.[j
].Removed <- true
90 ellipses.RemoveRange(i, nbToRemove)
92 // 5) Remove ellipses whose center is into an ellipse with a better score
96 let window = { KdTree.minX
= e.Ellipse.Cx - e.Ellipse.A
97 KdTree.maxX
= e.Ellipse.Cx + e.Ellipse.A
98 KdTree.minY
= e.Ellipse.Cy - e.Ellipse.A
99 KdTree.maxY
= e.Ellipse.Cy + e.Ellipse.A }
100 for other in tree.Search window do
101 if not
other.Removed && other.MatchingScore < e.MatchingScore
103 if e.Ellipse.Scale(0.8).Contains other.Ellipse.Cx other.Ellipse.Cy
105 other.Removed <- true
106 ellipses.RemoveAll(fun e -> e.Removed) |> ignore
108 List.ofSeq
ellipses |> List.map
(fun e -> e.Ellipse)