Cleaning.
[master-thesis.git] / Parasitemia / ParasitemiaCore / MatchingEllipses.fs
1 module ParasitemiaCore.MatchingEllipses
2
3 open System
4 open System.Drawing
5 open System.Linq
6 open System.Collections
7 open System.Collections.Generic
8
9 open Types
10 open Utils
11
12 type private EllipseScoreFlaggedKd (matchingScore: float32, e: Ellipse) =
13 let mutable matchingScore = matchingScore
14
15 member this.Ellipse = e
16
17 member this.MatchingScore = matchingScore
18
19 member this.AddMatchingScore (score: float32) =
20 matchingScore <- matchingScore + score
21
22 member val Processed = false with get, set
23 member val Removed = false with get, set
24
25 interface KdTree.I2DCoords with
26 member this.X = this.Ellipse.Cx
27 member this.Y = this.Ellipse.Cy
28
29 type MatchingEllipses (radius: float32) =
30 let ellipses = List<EllipseScoreFlaggedKd>()
31
32 // All ellipses with a score below this are removed.
33 let matchingScoreThreshold = 0.4f
34
35 member this.Add (e: Ellipse) =
36 ellipses.Add(EllipseScoreFlaggedKd(0.f, e))
37
38 member this.Ellipses : Ellipse list =
39 List.ofSeq ellipses |> List.map (fun e -> e.Ellipse)
40
41 member this.PrunedEllipses : Ellipse list =
42 if ellipses.Count = 0
43 then
44 []
45 else
46 // 1) Create a kd-tree from the ellipses list.
47 let tree = KdTree.Tree.BuildTree (List.ofSeq ellipses)
48
49 // 2) Compute the matching score of each ellipses.
50 let windowSize = radius / 3.f
51 for e in ellipses do
52 e.Processed <- true
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
60 then
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)
69 | _ -> ()
70
71 // 3) Remove ellipses whose center is near the center of another ellipse with a better score.
72 for e in ellipses do
73 if e.MatchingScore < matchingScoreThreshold
74 then
75 e.Removed <- true
76 else
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
83 then
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
86 then
87 other.Removed <- true
88 else
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 ->
92 other.Removed <- true
93 | _ ->
94 ()
95 ellipses
96 |> List.ofSeq
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)
100