Change the parasite detection method.
[master-thesis.git] / Parasitemia / Parasitemia / Ellipse.fs
index 62c0936..33333bf 100644 (file)
@@ -7,28 +7,29 @@ open Emgu.CV
 open Emgu.CV.Structure
 
 open Utils
+open Config
 open MatchingEllipses
 
 
 type private SearchExtremum = Minimum | Maximum
-    
+
 let private goldenSectionSearch (f: float -> float) (nbIter: int) (xmin: float) (xmax: float) (searchExtremum: SearchExtremum) : (float * float) =
     let gr = 1.0 / 1.6180339887498948482
     let mutable a = xmin
     let mutable b = xmax
     let mutable c = b - gr * (b - a)
     let mutable d = a + gr * (b - a)
-    
+
     for i in 1 .. nbIter do
         let mutable fc = f c
         let mutable fd = f d
-        
+
         if searchExtremum = Maximum
         then
             let tmp = fc
             fc <- fd
             fd <- tmp
-        
+
         if fc < fd
         then
             b <- d
@@ -38,105 +39,105 @@ let private goldenSectionSearch (f: float -> float) (nbIter: int) (xmin: float)
             a <- c
             c <- d
             d <- a + gr * (b - a)
-    
+
     let x = (b + a) / 2.0
     x, f x
 
 // Ellipse.A is always equal or greater than Ellipse.B.
 // Ellipse.Alpha is between 0 and Pi.
 let ellipse (p1x: float) (p1y: float) (m1: float) (p2x: float) (p2y: float) (m2: float) (p3x: float) (p3y: float) : Types.Ellipse option =
-    let accuracy_extremum_search_1 = 4
-    let accuracy_extremum_search_2 = 3
+    let accuracy_extremum_search_1 = 8 // 3
+    let accuracy_extremum_search_2 = 8 // 4
 
     // p3 as the referencial.
     let p1x = p1x - p3x
     let p1y = p1y - p3y
-    
+
     let p2x = p2x - p3x
     let p2y = p2y - p3y
-        
+
     // Convert to polar coordinates.
     let alpha1 = atan m1
     let alpha2 = atan m2
-    
+
     let r1 = sqrt (p1x ** 2.0 + p1y ** 2.0)
     let theta1 = atan2 p1y p1x
 
     let r2 = sqrt (p2x**2.0 + p2y**2.0)
     let theta2 = atan2 p2y p2x
 
-    let valid = 
-        4.0 * sin (alpha1 - theta1) * (-r1 * sin (alpha1 - theta1) + r2 * sin (alpha1 - theta2)) * 
+    let valid =
+        4.0 * sin (alpha1 - theta1) * (-r1 * sin (alpha1 - theta1) + r2 * sin (alpha1 - theta2)) *
         sin (alpha2 - theta2) * (-r1 * sin (alpha2 - theta1) + r2 * sin (alpha2 - theta2)) +
         r1 * r2 * sin (alpha1 - alpha2) ** 2.0 * sin (theta1 - theta2) ** 2.0 < 0.0
-    
+
     if valid
     then
-        let r theta = 
+        let r theta =
             (r1 * r2 * (r1 * (cos (alpha2 + theta - theta1 - theta2) - cos (alpha2 - theta) * cos (theta1 - theta2)) * sin (alpha1 - theta1) + r2 * (-cos (alpha1 + theta - theta1 - theta2) + cos (alpha1 - theta) * cos (theta1 - theta2)) * sin (alpha2 - theta2)) * sin (theta1 - theta2)) /
             (sin (alpha1 - theta1) * sin (alpha2 - theta2) * (r1 * sin (theta - theta1) - r2 * sin (theta - theta2)) ** 2.0 - r1 * r2 * sin (alpha1 - theta) * sin (alpha2 - theta) * sin (theta1 - theta2) ** 2.0)
-                
+
         let rabs = r >> abs
-        
+
         // We search for an interval [theta_a, theta_b] and assume the function is unimodal in this interval.
         let thetaTan, _ = goldenSectionSearch rabs accuracy_extremum_search_1 0.0 Math.PI Maximum
         let rTan = r thetaTan
-                        
+
         let PTanx = rTan * cos thetaTan
         let PTany = rTan * sin thetaTan
-        
+
         let d1a = tan alpha1
         let d1b = -d1a * p1x + p1y
-        
+
         let d2a = tan alpha2
         let d2b = -d2a * p2x + p2y
-        
+
         let d3a = -1.0 / tan thetaTan
         let d3b = -d3a * PTanx + PTany
-        
+
         let Ux = -(d1b - d2b) / (d1a - d2a)
         let Uy = -(d2a * d1b - d1a * d2b) / (d1a - d2a)
-        
+
         let Vx = -(d1b - d3b) / (d1a - d3a)
         let Vy = -(d3a * d1b - d1a * d3b) / (d1a - d3a)
-        
+
         let Wx = p1x + (p2x - p1x) / 2.0
         let Wy = p1y + (p2y - p1y) / 2.0
-        
+
         let Zx = p1x + (PTanx - p1x) / 2.0
         let Zy = p1y + (PTany - p1y) / 2.0
-        
+
         let va = -(-Vy + Zy) / (Vx - Zx)
         let vb = -(Zx * Vy - Vx * Zy) / (Vx - Zx)
-        
+
         let ua = -(-Uy + Wy) / (Ux - Wx)
         let ub = -(Wx * Uy - Ux * Wy) / (Ux - Wx)
-        
+
         let cx = -(vb - ub) / (va - ua)
         let cy = -(ua * vb - va * ub) / (va - ua)
-        
+
         let rc = sqrt (cx**2.0 + cy**2.0)
         let psi = atan2 cy cx
-        
-        let rellipse theta = 
+
+        let rellipse theta =
             sqrt (
                 rc ** 2.0 + (r1 ** 2.0 * r2 ** 2.0 * (r1 * (cos (alpha2 + theta - theta1 - theta2) - cos (alpha2 - theta) * cos (theta1 - theta2)) * sin (alpha1 - theta1) + r2 * (-cos (alpha1 + theta - theta1 - theta2) + cos (alpha1 - theta) * cos (theta1 - theta2)) * sin (alpha2 - theta2)) ** 2.0 * sin (theta1 - theta2) ** 2.0) /
                 (sin (alpha1 - theta1) * sin (alpha2 - theta2) * (r1 * sin (theta - theta1) - r2 * sin (theta - theta2)) ** 2.0 - r1 * r2 * sin (alpha1 - theta) * sin (alpha2 - theta) * sin (theta1 - theta2) ** 2.0) ** 2.0 -
                 (2.0 * r1 * r2 * rc * cos (theta - psi) * (r1 * (cos (alpha2 + theta - theta1 - theta2) - cos (alpha2 - theta) * cos (theta1 - theta2)) * sin (alpha1 - theta1) + r2 * (-cos (alpha1 + theta - theta1 - theta2) + cos (alpha1 - theta) * cos (theta1 - theta2)) * sin (alpha2 - theta2)) * sin (theta1 - theta2)) /
                 (sin (alpha1 - theta1) * sin (alpha2 - theta2) * (r1 * sin (theta - theta1) - r2 * sin (theta - theta2)) ** 2.0 - r1 * r2 * sin (alpha1 - theta) * sin (alpha2 - theta) * sin (theta1 - theta2) ** 2.0))
-                        
-        // We search for an interval [theta_a, theta_b] and assume the function is unimodal in this interval.             
+
+        // We search for an interval [theta_a, theta_b] and assume the function is unimodal in this interval.
         let r1eTheta, r1e = goldenSectionSearch rellipse accuracy_extremum_search_2 0.0 (Math.PI / 2.0) Maximum // Pi/2 and not pi because the period is Pi.
         let r2eTheta, r2e = goldenSectionSearch rellipse accuracy_extremum_search_2 0.0 (Math.PI / 2.0) Minimum
-                
+
         let rr1e = r r1eTheta
         let r1ex = rr1e * cos r1eTheta
         let r1ey = rr1e * sin r1eTheta
         let mutable alpha = atan ((r1ey - cy) / (r1ex - cx))
         if alpha < 0.0
         then
-           alpha <- alpha + Math.PI 
-        
+           alpha <- alpha + Math.PI
+
         // Ride off the p3 referential.
         let cx = cx + p3x
         let cy = cy + p3y
@@ -153,9 +154,9 @@ let private vectorRotation (p1x: float) (p1y: float) (v1x: float) (v1y: float) (
         if v1x > 0.0
         then
             rotation <- -1.0
-    elif p1y < py        
+    elif p1y < py
     then
-        if v1x < 0.0 
+        if v1x < 0.0
         then
             rotation <- -1.0
     elif p1x > px
@@ -163,9 +164,9 @@ let private vectorRotation (p1x: float) (p1y: float) (v1x: float) (v1y: float) (
         if v1y < 0.0
         then
             rotation <- -1.0
-    elif p1x < px              
+    elif p1x < px
     then
-        if v1y > 0.0 
+        if v1y > 0.0
         then
             rotation <- -1.0
     rotation
@@ -174,31 +175,45 @@ let private vectorRotation (p1x: float) (p1y: float) (v1x: float) (v1y: float) (
 let private areVectorsValid (p1x: float) (p1y: float) (p2x: float) (p2y: float) (v1x: float) (v1y: float) (v2x: float) (v2y: float) : (float * float) option =
     let m1 = -v1x / v1y
     let m2 = -v2x / v2y
-    
+
     let b1 = -m1 * p1x + p1y
     let b2 = -m2 * p2x + p2y
-    let px = -((b1 - b2)/(m1 - m2))
-    let py = -((m2 * b1 - m1 * b2)/(m1 - m2))
-    
+    let px = -((b1 - b2) / (m1 - m2))
+    let py = -((m2 * b1 - m1 * b2) / (m1 - m2))
+
     let rot1 = vectorRotation p1x p1y v1x v1y px py
     let rot2 = vectorRotation p2x p2y v2x v2y px py
-    
-    if rot1 = rot2 || rot1 * atan2 (p1y - py) (p1x - px) + rot2 * atan2 (p2y - py) (p2x - px) <= 0.0
+
+    if rot1 = rot2
     then
         None
     else
+        let alpha1 = atan2 (p1y - py) (p1x - px)
+        let alpha2 = atan2 (p2y - py) (p2x - px)
+
+        let alpha1' = if alpha1 < 0.0 then 2.0 * Math.PI + alpha1 else alpha1
+        let alpha2' = if alpha2 < 0.0 then 2.0 * Math.PI + alpha2 else alpha2
+
+        let diff = rot1 * alpha1' + rot2 * alpha2'
+
+        if diff > Math.PI || (diff < 0.0 && diff > -Math.PI)
+        then
+            None
+        else
         Some (m1, m2)
 
+
 let find (edges: Matrix<byte>)
-         (xDir: Image<Gray, float>) 
-         (yDir: Image<Gray, float>) 
-         (radiusRange: float * float) 
-         (windowSize: int) 
-         (factorNbPick: float) : Types.Ellipse list =
+         (xDir: Image<Gray, float>)
+         (yDir: Image<Gray, float>)
+         (config: Config) : MatchingEllipses =
+
+    let r1, r2 = config.Parameters.scale * config.RBCMin, config.Parameters.scale * config.RBCMax // FIXME: scale factor should be applied in Config!?
+    let windowSize = roundInt (config.Parameters.factorWindowSize * r2)
+    let factorNbPick = config.Parameters.factorNbPick
 
     let increment = windowSize / 4
 
-    let r1, r2 = radiusRange
     let radiusTolerance = (r2 - r1) * 0.2
 
     let minimumDistance = (r2 / 1.5) ** 2.0
@@ -206,7 +221,7 @@ let find (edges: Matrix<byte>)
 
     let h = edges.Height
     let w = edges.Width
-    
+
     let mutable last_i, last_j = Int32.MaxValue, Int32.MaxValue
 
     let currentElements = List<(int * int)>()
@@ -216,12 +231,12 @@ let find (edges: Matrix<byte>)
     let yDirData = yDir.Data
 
     let rng = Random(42)
-    
+
     let ellipses = MatchingEllipses(r1)
-        
-    for window_i in -windowSize + increment .. increment .. h - increment do                                                  
-        for window_j in -windowSize + increment .. increment .. w - increment do            
-                
+
+    for window_i in -windowSize + increment .. increment .. h - increment do
+        for window_j in -windowSize + increment .. increment .. w - increment do
+
             let window_i_begin = if window_i < 0 then 0 else window_i
             let window_i_end = if window_i + windowSize - 1 >= h then h - 1 else window_i + windowSize - 1
             let window_j_begin = if window_j < 0 then 0 else window_j
@@ -258,13 +273,15 @@ let find (edges: Matrix<byte>)
                             match areVectorsValid p1xf p1yf p2xf p2yf -xDirData.[p1y, p1x, 0] -yDirData.[p1y, p1x, 0] -xDirData.[p2y, p2x, 0] -yDirData.[p2y, p2x, 0] with
                             | Some (m1, m2) ->
                                 match ellipse p1xf p1yf m1 p2xf p2yf m2 p3xf p3yf with
-                                | Some e when e.Cx > 0.0 && e.Cx < (float w) - 1.0 && e.Cy > 0.0 && e.Cy < (float h) - 1.0 && 
+                                | Some e when e.Cx > 0.0 && e.Cx < (float w) - 1.0 && e.Cy > 0.0 && e.Cy < (float h) - 1.0 &&
                                               e.A >= r1 - radiusTolerance && e.A <= r2 + radiusTolerance && e.B >= r1 - radiusTolerance && e.B <= r2 + radiusTolerance ->
+
+                                     let prout = areVectorsValid p1xf p1yf p2xf p2yf -xDirData.[p1y, p1x, 0] -yDirData.[p1y, p1x, 0] -xDirData.[p2y, p2x, 0] -yDirData.[p2y, p2x, 0]
                                      ellipses.Add e
                                 | _ -> ()
                             | _ -> ()
 
         currentElements.Clear()
-        
-    ellipses.Ellipses
+
+    ellipses