Little adjustments.
authorGreg Burri <greg.burri@gmail.com>
Wed, 23 Dec 2015 00:00:39 +0000 (01:00 +0100)
committerGreg Burri <greg.burri@gmail.com>
Wed, 23 Dec 2015 00:00:39 +0000 (01:00 +0100)
Parasitemia/Parasitemia/Classifier.fs
Parasitemia/Parasitemia/Config.fs
Parasitemia/Parasitemia/KMedians.fs
Parasitemia/Parasitemia/MainAnalysis.fs
Parasitemia/Parasitemia/MatchingEllipses.fs
Parasitemia/Parasitemia/ParasitesMarker2.fs
Parasitemia/Parasitemia/Program.fs

index c54a3b1..3113d6f 100644 (file)
@@ -75,7 +75,8 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker2.Result) (img
                     | _ ->
                         None )
 
                     | _ ->
                         None )
 
-        let ellipsesWithNeigbors = ellipses |> List.map (fun e -> e, neighbors e)
+        // We reverse the list to get the lower score ellipses first.
+        let ellipsesWithNeigbors = ellipses |> List.map (fun e -> e, neighbors e) |> List.rev
 
         // 2) Remove ellipses with a high standard deviation (high contrast).
         let globalStdDiviation = MathNet.Numerics.Statistics.StreamingStatistics.StandardDeviation(seq {
 
         // 2) Remove ellipses with a high standard deviation (high contrast).
         let globalStdDiviation = MathNet.Numerics.Statistics.StreamingStatistics.StandardDeviation(seq {
@@ -83,14 +84,13 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker2.Result) (img
                 for x in 0 .. w - 1 do
                     yield img.Data.[y, x, 0] |> float })
 
                 for x in 0 .. w - 1 do
                     yield img.Data.[y, x, 0] |> float })
 
-        for e, neighbors in List.rev ellipsesWithNeigbors do
+        for e in ellipses do
             let minX, minY, maxX, maxY = ellipseWindow e
 
             let stdDiviation = MathNet.Numerics.Statistics.StreamingStatistics.StandardDeviation(seq {
                 for y in (if minY < 0 then 0 else minY) .. (if maxY >= h then h - 1 else maxY) do
                     for x in (if minX < 0 then 0 else minX) .. (if maxX >= w then w - 1 else maxX) do
             let minX, minY, maxX, maxY = ellipseWindow e
 
             let stdDiviation = MathNet.Numerics.Statistics.StreamingStatistics.StandardDeviation(seq {
                 for y in (if minY < 0 then 0 else minY) .. (if maxY >= h then h - 1 else maxY) do
                     for x in (if minX < 0 then 0 else minX) .. (if maxX >= w then w - 1 else maxX) do
-                        let p = PointD(float x, float y)
-                        if pixelOwnedByE p e (neighbors |> List.choose (fun (otherE, p1, p2) -> if otherE.Removed then None else Some (Utils.lineFromTwoPoints p1 p2)))
+                        if e.Contains (float x) (float y)
                         then
                             yield img.Data.[y, x, 0] |> float })
 
                         then
                             yield img.Data.[y, x, 0] |> float })
 
@@ -157,7 +157,8 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker2.Result) (img
                                 darkStainPixels <- darkStainPixels + 1
 
                 let cellClass =
                                 darkStainPixels <- darkStainPixels + 1
 
                 let cellClass =
-                    if float darkStainPixels > config.Parameters.maxDarkStainRatio * (float nbElement) (* ||
+                    if float darkStainPixels > config.Parameters.maxDarkStainRatio * (float nbElement) ||
+                       float stainPixels > config.Parameters.maxStainRatio * (float nbElement) (* ||
                         sqrt (((float sumCoords_x) / (float nbElement) - e.Cx) ** 2.0 + ((float sumCoords_y) / (float nbElement) - e.Cy) ** 2.0) > e.A * config.maxOffcenter *)
                     then
                         Peculiar
                         sqrt (((float sumCoords_x) / (float nbElement) - e.Cx) ** 2.0 + ((float sumCoords_y) / (float nbElement) - e.Cy) ** 2.0) > e.A * config.maxOffcenter *)
                     then
                         Peculiar
index 261d87b..3e3a06c 100644 (file)
@@ -20,16 +20,16 @@ type Parameters = {
 
     // Parasites detection.
     darkStainLevel: float
 
     // Parasites detection.
     darkStainLevel: float
+    maxDarkStainRatio: float
 
     stainArea: float // Factor of a RBC area. 0.5 means the half of RBC area.
     stainLevel: float // [0, 1]
 
     stainArea: float // Factor of a RBC area. 0.5 means the half of RBC area.
     stainLevel: float // [0, 1]
+    maxStainRatio: float // [0, 1]
 
     infectionArea: float // Factor of a RBC area. 0.5 means the half of RBC area.
     infectionLevel: float // [0, 1]
     parasitePixelsRequired: int
 
 
     infectionArea: float // Factor of a RBC area. 0.5 means the half of RBC area.
     infectionLevel: float // [0, 1]
     parasitePixelsRequired: int
 
-    maxDarkStainRatio: float
-
     standardDeviationMaxRatio: float // The standard deviation of the pixel values of a cell can't be greater than standardDeviationMaxRatio * global standard deviation
     minimumCellArea: float // Factor of RBC area.
 }
     standardDeviationMaxRatio: float // The standard deviation of the pixel values of a cell can't be greater than standardDeviationMaxRatio * global standard deviation
     minimumCellArea: float // Factor of RBC area.
 }
@@ -42,7 +42,8 @@ type Config (param: Parameters) =
     member this.RBCMinRadius = this.RBCRadius + param.minRbcRadius * this.RBCRadius
     member this.RBCMaxRadius = this.RBCRadius + param.maxRbcRadius * this.RBCRadius
 
     member this.RBCMinRadius = this.RBCRadius + param.minRbcRadius * this.RBCRadius
     member this.RBCMaxRadius = this.RBCRadius + param.maxRbcRadius * this.RBCRadius
 
-    member this.RBCMinArea = param.minimumCellArea * Math.PI * this.RBCRadius ** 2.0
+    member this.RBCArea = Math.PI * this.RBCRadius ** 2.0
+    member this.RBCMinArea = param.minimumCellArea * this.RBCArea
 
 
-    member this.InfectionArea = param.infectionArea * Math.PI * this.RBCRadius ** 2.0
-    member this.StainArea = param.stainArea * Math.PI * this.RBCRadius ** 2.0
+    member this.InfectionArea = param.infectionArea * this.RBCArea
+    member this.StainArea = param.stainArea * this.RBCArea
index 9b5d50f..8a3fd2b 100644 (file)
@@ -30,8 +30,9 @@ let kmedians (img: Image<Gray, float32>) (fgFactor: float) : Result =
     let mutable fg = new Image<Gray, byte>(img.Size)
 
     for i in 1 .. nbIteration do
     let mutable fg = new Image<Gray, byte>(img.Size)
 
     for i in 1 .. nbIteration do
-        CvInvoke.Pow(img - median_bg, 2.0, d_bg)
-        CvInvoke.Pow(img - median_fg, 2.0, d_fg)
+        d_bg <- img.AbsDiff(Gray(median_bg))
+        d_fg <- img.AbsDiff(Gray(median_fg))
+
         CvInvoke.Compare(d_fg, d_bg, fg, CvEnum.CmpType.LessThan)
 
         let bg_values = List<float>()
         CvInvoke.Compare(d_fg, d_bg, fg, CvEnum.CmpType.LessThan)
 
         let bg_values = List<float>()
@@ -46,8 +47,6 @@ let kmedians (img: Image<Gray, float32>) (fgFactor: float) : Result =
         median_bg <- MathNet.Numerics.Statistics.Statistics.Median(bg_values)
         median_fg <- MathNet.Numerics.Statistics.Statistics.Median(fg_values)
 
         median_bg <- MathNet.Numerics.Statistics.Statistics.Median(bg_values)
         median_fg <- MathNet.Numerics.Statistics.Statistics.Median(fg_values)
 
-    CvInvoke.Sqrt(d_fg, d_fg)
-
     { fg = fg; median_bg = median_bg; median_fg = median_fg; d_fg = d_fg }
 
 
     { fg = fg; median_bg = median_bg; median_fg = median_fg; d_fg = d_fg }
 
 
index 6f78b7c..fd341ac 100644 (file)
@@ -30,15 +30,19 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) : Cell li
     let greenOpen1 = filteredGreen.Copy()
     logTime "areaOpen1" (fun () -> ImgTools.areaOpen greenOpen1 2000)*)
 
     let greenOpen1 = filteredGreen.Copy()
     logTime "areaOpen1" (fun () -> ImgTools.areaOpen greenOpen1 2000)*)
 
-    logTime "areaOpen" (fun () -> ImgTools.areaOpen filteredGreen 2000)
+    let initialAreaOpen = 2000
+    logTime "areaOpen 1" (fun () -> ImgTools.areaOpen filteredGreen 2000)
 
     config.RBCRadius <- Granulometry.findRadius filteredGreen (10, 100) 0.5 |> float
 
 
     config.RBCRadius <- Granulometry.findRadius filteredGreen (10, 100) 0.5 |> float
 
-    let filteredGreenFloat = filteredGreen.Convert<Gray, float32>() // Is it neccessary?
+    let secondAreaOpen = int <| config.RBCArea / 3.
+    if secondAreaOpen > initialAreaOpen
+    then
+        logTime "areaOpen 2" (fun () -> ImgTools.areaOpen filteredGreen secondAreaOpen)
 
 
-    let kmediansResults = logTime "Finding foreground (k-medians)" (fun () -> KMedians.kmedians filteredGreenFloat 1.0)
+    let filteredGreenFloat = filteredGreen.Convert<Gray, float32>() // Is it neccessary?
 
 
-    let parasites, filteredGreenWhitoutInfection, filteredGreenWhitoutStain = ParasitesMarker2.find filteredGreen filteredGreenFloat kmediansResults config
+    let parasites, filteredGreenWhitoutInfection, filteredGreenWhitoutStain = ParasitesMarker2.find filteredGreen filteredGreenFloat config
     let filteredGreenWhitoutInfectionFloat = filteredGreenWhitoutInfection.Convert<Gray, float32>()
     let filteredGreenWhitoutStainFloat = filteredGreenWhitoutStain.Convert<Gray, float32>()
 
     let filteredGreenWhitoutInfectionFloat = filteredGreenWhitoutInfection.Convert<Gray, float32>()
     let filteredGreenWhitoutStainFloat = filteredGreenWhitoutStain.Convert<Gray, float32>()
 
@@ -108,7 +112,7 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) : Cell li
         drawEllipses imgEllipses ellipses (Bgr(0.0, 240.0, 240.0)) 1.0
         saveImg imgEllipses (buildFileName " - ellipses.png")
 
         drawEllipses imgEllipses ellipses (Bgr(0.0, 240.0, 240.0)) 1.0
         saveImg imgEllipses (buildFileName " - ellipses.png")
 
-        saveImg (kmediansResults.fg * 255.0) (buildFileName " - foreground.png")
+        // saveImg (kmediansResults.fg * 255.0) (buildFileName " - foreground.png")
 
         let imgCells = img.Copy()
         drawCells imgCells false cells
 
         let imgCells = img.Copy()
         drawCells imgCells false cells
@@ -124,7 +128,8 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) : Cell li
         saveImg filteredGreenMaxima (buildFileName " - filtered - maxima.png")
 
         saveImg filteredGreen (buildFileName " - filtered.png")
         saveImg filteredGreenMaxima (buildFileName " - filtered - maxima.png")
 
         saveImg filteredGreen (buildFileName " - filtered.png")
-        saveImg filteredGreenWhitoutStain (buildFileName " - filtered closed.png")
+        saveImg filteredGreenWhitoutStain (buildFileName " - filtered closed stain.png")
+        saveImg filteredGreenWhitoutInfection (buildFileName " - filtered closed infection.png")
 
         (*saveImg parasitesMarker (buildFileName " - parasites (area closing).png")
         saveImg stainMarker (buildFileName " - stain (area closing).png")*)
 
         (*saveImg parasitesMarker (buildFileName " - parasites (area closing).png")
         saveImg stainMarker (buildFileName " - stain (area closing).png")*)
index 7a4c51b..6e6218d 100644 (file)
@@ -13,7 +13,7 @@ open Utils
 let matchingScoreThreshold1 = 0.6
 
 // All ellipses with a score below this are removed.
 let matchingScoreThreshold1 = 0.6
 
 // All ellipses with a score below this are removed.
-let matchingScoreThreshold2 = 2.
+let matchingScoreThreshold2 = 600.
 
 type private EllipseScoreFlaggedKd (matchingScore: float, e: Ellipse) =
     let mutable matchingScore = matchingScore
 
 type private EllipseScoreFlaggedKd (matchingScore: float, e: Ellipse) =
     let mutable matchingScore = matchingScore
@@ -69,8 +69,8 @@ type MatchingEllipses (radiusMin: float) =
                             let matchingScore = 2.0 * commonArea / (areaE + areaOther)
                             if matchingScore >= matchingScoreThreshold1
                             then
                             let matchingScore = 2.0 * commonArea / (areaE + areaOther)
                             if matchingScore >= matchingScoreThreshold1
                             then
-                                other.AddMatchingScore(matchingScore)
-                                e.AddMatchingScore(matchingScore)
+                                other.AddMatchingScore(matchingScore * e.Ellipse.Perimeter)
+                                e.AddMatchingScore(matchingScore * other.Ellipse.Perimeter)
                         | _ -> ()
 
             // 3) Sort ellipses by their score.
                         | _ -> ()
 
             // 3) Sort ellipses by their score.
index 17a032d..6afef44 100644 (file)
@@ -5,6 +5,8 @@ open System.Drawing
 open Emgu.CV
 open Emgu.CV.Structure
 
 open Emgu.CV
 open Emgu.CV.Structure
 
+open Utils
+
 type Result = {
     darkStain: Image<Gray, byte>
     infection: Image<Gray, byte>
 type Result = {
     darkStain: Image<Gray, byte>
     infection: Image<Gray, byte>
@@ -14,31 +16,38 @@ type Result = {
 // * 'Dark stain' corresponds to the colored pixel, it's independent of the size of the areas.
 // * 'Stain' corresponds to the stain around the parasites.
 // * 'Infection' corresponds to the parasite. It shouldn't contain thrombocytes.
 // * 'Dark stain' corresponds to the colored pixel, it's independent of the size of the areas.
 // * 'Stain' corresponds to the stain around the parasites.
 // * 'Infection' corresponds to the parasite. It shouldn't contain thrombocytes.
-let find (filteredGreen: Image<Gray, byte>) (filteredGreenFloat: Image<Gray, float32>) (kmediansResult: KMedians.Result) (config: Config.Config) : Result * Image<Gray, byte> * Image<Gray, byte> =
+let find (filteredGreen: Image<Gray, byte>) (filteredGreenFloat: Image<Gray, float32>) (config: Config.Config) : Result * Image<Gray, byte> * Image<Gray, byte> =
+
+    let filteredGreenWithoutInfection = filteredGreen.Copy()
+    ImgTools.areaClose filteredGreenWithoutInfection (int config.InfectionArea)
+
+    let filteredGreenWithoutStain = filteredGreenWithoutInfection.Copy()
+    ImgTools.areaClose filteredGreenWithoutStain (int config.StainArea)
 
     // We use the filtered image to find the dark stain.
 
     // We use the filtered image to find the dark stain.
-    let { KMedians.fg = fg; KMedians.median_bg = median_bg; KMedians.median_fg = median_fg; KMedians.d_fg = d_fg } = kmediansResult
+    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)
     let darkStain = d_fg.Cmp(median_bg * config.Parameters.darkStainLevel, CvEnum.CmpType.GreaterThan)
-    darkStain._And(filteredGreenFloat.Cmp(median_fg, CvEnum.CmpType.LessThan))
+    darkStain._And(filteredGreenWithoutInfection.Cmp(median_fg, CvEnum.CmpType.LessThan))
 
 
-    let marker (area: int) (threshold: float) : Image<Gray, byte> * Image<Gray, byte> =
-        let closed = filteredGreen.Copy()
-        ImgTools.areaClose closed area
-        let diff = closed - filteredGreen
+    let marker (img: Image<Gray, byte>) (closed: Image<Gray, byte>) (threshold: float) : Image<Gray, byte> =
+        let diff = closed - img
 
         let min = ref [| 0. |]
         let minLocation = ref <| [| Point() |]
         let max = ref [| 0. |]
         let maxLocation = ref <| [| Point() |]
         diff.MinMax(min, max, minLocation, maxLocation)
 
         let min = ref [| 0. |]
         let minLocation = ref <| [| Point() |]
         let max = ref [| 0. |]
         let maxLocation = ref <| [| Point() |]
         diff.MinMax(min, max, minLocation, maxLocation)
+        let max = (!max).[0]
 
 
-        let valueThreshold = if (!max).[0] * threshold < 0.1 * (median_bg - median_fg) then 0.1 * (median_bg - median_fg) else (!max).[0] * threshold
+        let limitThreshold = 0.1
+        let valueThreshold = (*if max < limitThreshold * (median_bg - median_fg) then max / 2. else *) max * threshold
 
         diff._ThresholdBinary(Gray(valueThreshold), Gray(255.))
 
         diff._ThresholdBinary(Gray(valueThreshold), Gray(255.))
-        diff, closed
+        diff
 
 
-    let infectionMarker, filteredGreenWithoutInfection  = marker (int config.InfectionArea) config.Parameters.infectionLevel
-    let stainMarker, filteredGreenWithoutStain = marker (int config.StainArea) config.Parameters.stainLevel
+    let infectionMarker = marker filteredGreen filteredGreenWithoutInfection config.Parameters.infectionLevel
+    let stainMarker = marker filteredGreenWithoutInfection filteredGreenWithoutStain config.Parameters.stainLevel
 
     { darkStain = darkStain
       infection = infectionMarker
 
     { darkStain = darkStain
       infection = infectionMarker
index 5b8c3e4..781d9ad 100644 (file)
@@ -61,26 +61,26 @@ let main args =
             Config(
               { scale = 1.
 
             Config(
               { scale = 1.
 
-                minRbcRadius = -0.35
-                maxRbcRadius = 0.35
+                minRbcRadius = -0.32
+                maxRbcRadius = 0.32
 
                 preFilterSigma = 1.5
 
                 factorNbPick = 1.0
                 factorWindowSize = 2.0
 
 
                 preFilterSigma = 1.5
 
                 factorNbPick = 1.0
                 factorWindowSize = 2.0
 
-                darkStainLevel = 0.4 // Lower -> more sensitive.
+                darkStainLevel = 0.3 // Lower -> more sensitive.
+                maxDarkStainRatio = 0.1
 
 
-                infectionArea = 0.015
+                infectionArea = 0.012 // 1.2 %
                 infectionLevel = 0.2
                 parasitePixelsRequired = 1
 
                 infectionLevel = 0.2
                 parasitePixelsRequired = 1
 
-                stainArea = 0.06
-                stainLevel = 0.2
-
-                maxDarkStainRatio = 0.1
+                stainArea = 0.08
+                stainLevel = 0.1
+                maxStainRatio = 0.12 // 12 %
 
 
-                standardDeviationMaxRatio = 0.65
+                standardDeviationMaxRatio = 0.55
                 minimumCellArea = 0.5 })
 
         match mode with
                 minimumCellArea = 0.5 })
 
         match mode with