From ca3f764fc93defb480bb9b1c34e9c41447cc07e0 Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Wed, 23 Dec 2015 01:00:39 +0100 Subject: [PATCH] Little adjustments. --- Parasitemia/Parasitemia/Classifier.fs | 11 ++++---- Parasitemia/Parasitemia/Config.fs | 11 ++++---- Parasitemia/Parasitemia/KMedians.fs | 7 ++--- Parasitemia/Parasitemia/MainAnalysis.fs | 17 +++++++---- Parasitemia/Parasitemia/MatchingEllipses.fs | 6 ++-- Parasitemia/Parasitemia/ParasitesMarker2.fs | 31 +++++++++++++-------- Parasitemia/Parasitemia/Program.fs | 18 ++++++------ 7 files changed, 58 insertions(+), 43 deletions(-) diff --git a/Parasitemia/Parasitemia/Classifier.fs b/Parasitemia/Parasitemia/Classifier.fs index c54a3b1..3113d6f 100644 --- a/Parasitemia/Parasitemia/Classifier.fs +++ b/Parasitemia/Parasitemia/Classifier.fs @@ -75,7 +75,8 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker2.Result) (img | _ -> 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 { @@ -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 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 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 }) @@ -157,7 +157,8 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker2.Result) (img 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 diff --git a/Parasitemia/Parasitemia/Config.fs b/Parasitemia/Parasitemia/Config.fs index 261d87b..3e3a06c 100644 --- a/Parasitemia/Parasitemia/Config.fs +++ b/Parasitemia/Parasitemia/Config.fs @@ -20,16 +20,16 @@ type Parameters = { // 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] + 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 - 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. } @@ -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.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 diff --git a/Parasitemia/Parasitemia/KMedians.fs b/Parasitemia/Parasitemia/KMedians.fs index 9b5d50f..8a3fd2b 100644 --- a/Parasitemia/Parasitemia/KMedians.fs +++ b/Parasitemia/Parasitemia/KMedians.fs @@ -30,8 +30,9 @@ let kmedians (img: Image) (fgFactor: float) : Result = let mutable fg = new Image(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() @@ -46,8 +47,6 @@ let kmedians (img: Image) (fgFactor: float) : Result = 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 } diff --git a/Parasitemia/Parasitemia/MainAnalysis.fs b/Parasitemia/Parasitemia/MainAnalysis.fs index 6f78b7c..fd341ac 100644 --- a/Parasitemia/Parasitemia/MainAnalysis.fs +++ b/Parasitemia/Parasitemia/MainAnalysis.fs @@ -30,15 +30,19 @@ let doAnalysis (img: Image) (name: string) (config: Config) : Cell li 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 - let filteredGreenFloat = filteredGreen.Convert() // 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() // 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() let filteredGreenWhitoutStainFloat = filteredGreenWhitoutStain.Convert() @@ -108,7 +112,7 @@ let doAnalysis (img: Image) (name: string) (config: Config) : Cell li 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 @@ -124,7 +128,8 @@ let doAnalysis (img: Image) (name: string) (config: Config) : Cell li 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")*) diff --git a/Parasitemia/Parasitemia/MatchingEllipses.fs b/Parasitemia/Parasitemia/MatchingEllipses.fs index 7a4c51b..6e6218d 100644 --- a/Parasitemia/Parasitemia/MatchingEllipses.fs +++ b/Parasitemia/Parasitemia/MatchingEllipses.fs @@ -13,7 +13,7 @@ open Utils 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 @@ -69,8 +69,8 @@ type MatchingEllipses (radiusMin: float) = 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. diff --git a/Parasitemia/Parasitemia/ParasitesMarker2.fs b/Parasitemia/Parasitemia/ParasitesMarker2.fs index 17a032d..6afef44 100644 --- a/Parasitemia/Parasitemia/ParasitesMarker2.fs +++ b/Parasitemia/Parasitemia/ParasitesMarker2.fs @@ -5,6 +5,8 @@ open System.Drawing open Emgu.CV open Emgu.CV.Structure +open Utils + type Result = { darkStain: Image infection: Image @@ -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. -let find (filteredGreen: Image) (filteredGreenFloat: Image) (kmediansResult: KMedians.Result) (config: Config.Config) : Result * Image * Image = +let find (filteredGreen: Image) (filteredGreenFloat: Image) (config: Config.Config) : Result * Image * Image = + + 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. - 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()) 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(filteredGreenFloat.Cmp(median_fg, CvEnum.CmpType.LessThan)) + darkStain._And(filteredGreenWithoutInfection.Cmp(median_fg, CvEnum.CmpType.LessThan)) - let marker (area: int) (threshold: float) : Image * Image = - let closed = filteredGreen.Copy() - ImgTools.areaClose closed area - let diff = closed - filteredGreen + let marker (img: Image) (closed: Image) (threshold: float) : Image = + 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 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, 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 diff --git a/Parasitemia/Parasitemia/Program.fs b/Parasitemia/Parasitemia/Program.fs index 5b8c3e4..781d9ad 100644 --- a/Parasitemia/Parasitemia/Program.fs +++ b/Parasitemia/Parasitemia/Program.fs @@ -61,26 +61,26 @@ let main args = Config( { scale = 1. - minRbcRadius = -0.35 - maxRbcRadius = 0.35 + minRbcRadius = -0.32 + maxRbcRadius = 0.32 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 - 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 -- 2.43.0