Use k-means instead of k-medians.
authorGreg Burri <greg.burri@gmail.com>
Sat, 2 Jan 2016 20:42:40 +0000 (21:42 +0100)
committerGreg Burri <greg.burri@gmail.com>
Sat, 2 Jan 2016 20:42:40 +0000 (21:42 +0100)
Parasitemia/Parasitemia/Heap.fs
Parasitemia/Parasitemia/ImgTools.fs
Parasitemia/Parasitemia/KMeans.fs [new file with mode: 0644]
Parasitemia/Parasitemia/MainAnalysis.fs
Parasitemia/Parasitemia/Parasitemia.fsproj
Parasitemia/Parasitemia/ParasitesMarker.fs
Parasitemia/Parasitemia/Program.fs

index c2ab69f..39c6a4b 100644 (file)
@@ -18,6 +18,7 @@ type Heap<'k, 'v when 'k : comparison> () =
         if r < a.Count && a.[r].key > a.[max].key
         then
             max <- r
+
         if max <> i
         then
             let tmp = a.[i]
index f567e68..5e32ccb 100644 (file)
@@ -36,11 +36,13 @@ let suppressMConnections (img: Matrix<byte>) =
     let h = img.Height
     for i in 1 .. h - 2 do
         for j in 1 .. w - 2 do
-            if img.[i, j] > 0uy && img.Data.[i + 1, j] > 0uy && (img.Data.[i, j - 1] > 0uy && img.Data.[i - 1, j + 1] = 0uy || img.Data.[i, j + 1] > 0uy && img.Data.[i - 1, j - 1] = 0uy) then
+            if img.[i, j] > 0uy && img.Data.[i + 1, j] > 0uy && (img.Data.[i, j - 1] > 0uy && img.Data.[i - 1, j + 1] = 0uy || img.Data.[i, j + 1] > 0uy && img.Data.[i - 1, j - 1] = 0uy)
+            then
                 img.[i, j] <- 0uy
     for i in 1 .. h - 2 do
         for j in 1 .. w - 2 do
-            if img.[i, j] > 0uy && img.Data.[i - 1, j] > 0uy && (img.Data.[i, j - 1] > 0uy && img.Data.[i + 1, j + 1] = 0uy || img.Data.[i, j + 1] > 0uy && img.Data.[i + 1, j - 1] = 0uy) then
+            if img.[i, j] > 0uy && img.Data.[i - 1, j] > 0uy && (img.Data.[i, j - 1] > 0uy && img.Data.[i + 1, j + 1] = 0uy || img.Data.[i, j + 1] > 0uy && img.Data.[i + 1, j - 1] = 0uy)
+            then
                 img.[i, j] <- 0uy
 
 let findEdges (img: Image<Gray, float32>) : Matrix<byte> * Image<Gray, float> * Image<Gray, float> =
@@ -109,7 +111,8 @@ let findEdges (img: Image<Gray, float32>) : Matrix<byte> * Image<Gray, float> *
                 else magnitudes.Data.[i - sign, j + sign]
 
             let m = magnitudes.Data.[i, j]
-            if m < mNeigbors 1 || m < mNeigbors -1 || m < thresholdLow then
+            if m < mNeigbors 1 || m < mNeigbors -1 || m < thresholdLow
+            then
                 nms.Data.[i, j] <- 0uy
 
     // suppressMConnections nms // It's not usefull for the rest of the process (ellipse detection).
@@ -120,7 +123,8 @@ let findEdges (img: Image<Gray, float32>) : Matrix<byte> * Image<Gray, float> *
     let toVisit = Stack<Point>()
     for i in 0 .. h - 1 do
         for j in 0 .. w - 1 do
-            if nms.Data.[i, j] = 1uy && magnitudes.Data.[i, j] >= thresholdHigh then
+            if nms.Data.[i, j] = 1uy && magnitudes.Data.[i, j] >= thresholdHigh
+            then
                 nms.Data.[i, j] <- 0uy
                 toVisit.Push(Point(j, i))
                 while toVisit.Count > 0 do
@@ -128,10 +132,12 @@ let findEdges (img: Image<Gray, float32>) : Matrix<byte> * Image<Gray, float> *
                     edges.Data.[p.Y, p.X] <- 1uy
                     for i' in -1 .. 1  do
                         for j' in -1 .. 1 do
-                            if i' <> 0 || j' <> 0 then
+                            if i' <> 0 || j' <> 0
+                            then
                                 let ni = p.Y + i'
                                 let nj = p.X + j'
-                                if ni >= 0 && ni < h && nj >= 0 && nj < w && nms.Data.[ni, nj] = 1uy then
+                                if ni >= 0 && ni < h && nj >= 0 && nj < w && nms.Data.[ni, nj] = 1uy
+                                then
                                     nms.Data.[ni, nj] <- 0uy
                                     toVisit.Push(Point(nj, ni))
 
@@ -580,12 +586,6 @@ let thin (mat: Matrix<byte>) =
         data2 <- tmp
 
 
-// FIXME: replace by a queue or stack.
-let pop (l: List<'a>) : 'a =
-    let n = l.[l.Count - 1]
-    l.RemoveAt(l.Count - 1)
-    n
-
 // Remove all 8-connected pixels with an area equal or greater than 'areaSize'.
 // Modify 'mat' in place.
 let removeArea (mat: Matrix<byte>) (areaSize: int) =
@@ -611,37 +611,37 @@ let removeArea (mat: Matrix<byte>) (areaSize: int) =
         for j in 0..w-1 do
             if data'.[i, j] = 1uy
             then
-                let neighborhood = List<(int*int)>()
-                let neighborsToCheck = List<(int*int)>()
-                neighborsToCheck.Add((i, j))
+                let neighborhood = List<Point>()
+                let neighborsToCheck = Stack<Point>()
+                neighborsToCheck.Push(Point(j, i))
                 data'.[i, j] <- 0uy
 
                 while neighborsToCheck.Count > 0 do
-                    let (ci, cj) = pop neighborsToCheck
-                    neighborhood.Add((ci, cj))
+                    let n = neighborsToCheck.Pop()
+                    neighborhood.Add(n)
                     for (ni, nj) in neighbors do
-                        let pi = ci + ni
-                        let pj = cj + nj
+                        let pi = n.Y + ni
+                        let pj = n.X + nj
                         if pi >= 0 && pi < h && pj >= 0 && pj < w && data'.[pi, pj] = 1uy
                         then
-                            neighborsToCheck.Add((pi, pj))
+                            neighborsToCheck.Push(Point(pj, pi))
                             data'.[pi, pj] <- 0uy
                 if neighborhood.Count <= areaSize
                 then
-                    for (ni, nj) in neighborhood do
-                        data.[ni, nj] <- 0uy
+                    for n in neighborhood do
+                        data.[n.Y, n.X] <- 0uy
 
 let connectedComponents (img: Image<Gray, byte>) (startPoints: List<Point>) : List<Point> =
     let w = img.Width
     let h = img.Height
 
     let pointChecked = Points()
-    let pointToCheck = List<Point>(startPoints);
+    let pointToCheck = Stack<Point>(startPoints);
 
     let data = img.Data
 
     while pointToCheck.Count > 0 do
-        let next = pop pointToCheck
+        let next = pointToCheck.Pop()
         pointChecked.Add(next) |> ignore
         for ny in -1 .. 1 do
             for nx in -1 .. 1 do
@@ -650,7 +650,7 @@ let connectedComponents (img: Image<Gray, byte>) (startPoints: List<Point>) : Li
                     let p = Point(next.X + nx, next.Y + ny)
                     if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h && data.[p.Y, p.X, 0] > 0uy && not (pointChecked.Contains p)
                     then
-                        pointToCheck.Add(p)
+                        pointToCheck.Push(p)
 
     List<Point>(pointChecked)
 
diff --git a/Parasitemia/Parasitemia/KMeans.fs b/Parasitemia/Parasitemia/KMeans.fs
new file mode 100644 (file)
index 0000000..7c18120
--- /dev/null
@@ -0,0 +1,57 @@
+module KMeans
+
+open System.Collections.Generic
+open System.Drawing
+
+open Emgu.CV
+open Emgu.CV.Structure
+
+type Result = {
+    fg: Image<Gray, byte>
+    mean_bg: float
+    mean_fg: float
+    d_fg: Image<Gray, float32> } // Euclidean distances of the foreground to mean_fg.
+
+let kmeans (img: Image<Gray, float32>) (fgFactor: float) : Result =
+    let nbIteration = 3
+    let w = img.Width
+    let h = img.Height
+
+    let min = ref [| 0.0 |]
+    let minLocation = ref <| [| Point() |]
+    let max = ref [| 0.0 |]
+    let maxLocation = ref <| [| Point() |]
+    img.MinMax(min, max, minLocation, maxLocation)
+
+    let mutable mean_bg = (!max).[0] - ((!max).[0] - (!min).[0]) / 4.0
+    let mutable mean_fg = (!min).[0] + ((!max).[0] - (!min).[0]) / 4.0
+    use mutable d_bg = new Image<Gray, float32>(img.Size)
+    let mutable d_fg = new Image<Gray, float32>(img.Size)
+    let mutable fg = new Image<Gray, byte>(img.Size)
+
+    for i in 1 .. nbIteration do
+        d_bg <- img.AbsDiff(Gray(mean_bg))
+        d_fg <- img.AbsDiff(Gray(mean_fg))
+
+        CvInvoke.Compare(d_fg, d_bg, fg, CvEnum.CmpType.LessThan)
+
+        let mutable bg_total = 0.0
+        let mutable bg_nb = 0
+
+        let mutable fg_total = 0.0
+        let mutable fg_nb = 0
+
+        for i in 0 .. h - 1 do
+            for j in 0 .. w - 1 do
+                if fg.Data.[i, j, 0] > 0uy
+                then
+                    fg_total <- fg_total + float img.Data.[i, j, 0]
+                    fg_nb <- fg_nb + 1
+                else
+                    bg_total <- bg_total + float img.Data.[i, j, 0]
+                    bg_nb <- bg_nb + 1
+
+        mean_bg <- bg_total / float bg_nb
+        mean_fg <- fg_total / float fg_nb
+
+    { fg = fg; mean_bg = mean_bg; mean_fg = mean_fg; d_fg = d_fg }
\ No newline at end of file
index 6111f06..ff82b8b 100644 (file)
@@ -47,6 +47,8 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) : Cell li
     let filteredGreenWhitoutInfectionFloat = filteredGreenWhitoutInfection.Convert<Gray, float32>()
     let filteredGreenWhitoutStainFloat = filteredGreenWhitoutStain.Convert<Gray, float32>()
 
+    let canny = green.Canny(60., 40.)
+
     (*let min = ref 0.0
     let minLocation = ref <| Point()
     let max = ref 0.0
@@ -77,6 +79,8 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) : Cell li
 
         let buildFileName postfix = System.IO.Path.Combine(dirPath, name + postfix)
 
+        saveImg canny (buildFileName " - canny.png")
+
         saveMat (edges * 255.0) (buildFileName " - edges.png")
 
         saveImg parasites.darkStain (buildFileName " - parasites - dark stain.png")
index 3282961..b4e3645 100644 (file)
@@ -71,6 +71,7 @@
     <Compile Include="Granulometry.fs" />
     <Compile Include="Config.fs" />
     <Compile Include="KMedians.fs" />
+    <Compile Include="KMeans.fs" />
     <Compile Include="EEOver.fs" />
     <Compile Include="ParasitesMarker.fs" />
     <Compile Include="KdTree.fs" />
index 20dbf4c..b50f7e9 100644 (file)
@@ -59,10 +59,17 @@ let find (filteredGreen: Image<Gray, byte>) (filteredGreenFloat: Image<Gray, flo
     ImgTools.areaClose filteredGreenWithoutStain (int config.StainArea)
 
     // We use the filtered image to find the dark stain.
-    let kmediansResults = logTime "Finding fg/bg (k-medians)" (fun () -> KMedians.kmedians (filteredGreenWithoutInfection.Convert<Gray, float32>()) 1.0) // FIXME: avoid converting again this in MainAnalysis
-    let { KMedians.fg = fg; KMedians.median_bg = median_bg; KMedians.median_fg = median_fg; KMedians.d_fg = d_fg } = kmediansResults
-    let darkStain = d_fg.Cmp(median_bg * config.Parameters.darkStainLevel, CvEnum.CmpType.GreaterThan)
-    darkStain._And(filteredGreenWithoutInfection.Cmp(median_fg, CvEnum.CmpType.LessThan))
+
+    // With K-Means.
+    let kmeansResults = logTime "Finding fg/bg (k-means)" (fun () -> KMeans.kmeans (filteredGreenWithoutInfection.Convert<Gray, float32>()) 1.0)
+    let { KMeans.mean_bg = value_bg; KMeans.mean_fg = value_fg; KMeans.d_fg = d_fg } = kmeansResults
+
+    // With K-Medians.
+    (* let kmediansResults = logTime "Finding fg/bg (k-medians)" (fun () -> KMedians.kmedians (filteredGreenWithoutInfection.Convert<Gray, float32>()) 1.0) // FIXME: avoid converting this again in MainAnalysis
+    let { KMedians.median_bg = value_bg; KMedians.median_fg = value_fg; KMedians.d_fg = d_fg } = kmediansResults *)
+
+    let darkStain = d_fg.Cmp(value_bg * config.Parameters.darkStainLevel, CvEnum.CmpType.GreaterThan)
+    darkStain._And(filteredGreenWithoutInfection.Cmp(value_fg, CvEnum.CmpType.LessThan))
 
     let marker (img: Image<Gray, byte>) (closed: Image<Gray, byte>) (level: float) : Image<Gray, byte> =
         let diff = closed - (img * level)
index dafff56..b14e6bd 100644 (file)
@@ -64,7 +64,7 @@ let main args =
                 minRbcRadius = -0.32
                 maxRbcRadius = 0.32
 
-                preFilterSigma = 1.5
+                preFilterSigma = 2. // 1.5
 
                 factorNbPick = 1.0
                 factorWindowSize = 2.0