From 828e126c88524d3dc123abc966a132532aed118b Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Mon, 25 Jan 2016 17:12:09 +0100 Subject: [PATCH] Remove the parasite detection function from Ma. --- Parasitemia/ParasitemiaCore/Classifier.fs | 24 +++--- Parasitemia/ParasitemiaCore/Config.fs | 26 +++--- Parasitemia/ParasitemiaCore/MainAnalysis.fs | 8 +- .../ParasitemiaCore/ParasitesMarker.fs | 79 ++++--------------- Parasitemia/ParasitemiaCore/Types.fs | 4 +- Parasitemia/ParasitemiaUI/State.fs | 4 +- 6 files changed, 49 insertions(+), 96 deletions(-) diff --git a/Parasitemia/ParasitemiaCore/Classifier.fs b/Parasitemia/ParasitemiaCore/Classifier.fs index 96532f8..2b6f3be 100644 --- a/Parasitemia/ParasitemiaCore/Classifier.fs +++ b/Parasitemia/ParasitemiaCore/Classifier.fs @@ -24,7 +24,7 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img: then [] else - let infection = parasites.infection.Copy() // To avoid to modify the parameter. + let infection = parasites.nucleus.Copy() // To avoid to modify the parameter. // This is the minimum window size to check if other ellipses touch 'e'. let searchRegion (e: Ellipse) = { KdTree.minX = e.Cx - (e.A + config.RBCRadius.Max) @@ -159,7 +159,7 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img: let minX, minY, maxX, maxY = ellipseWindow e let infectedPixels = List() - let stainPixels = List() + let cytoplasmPixels = List() //let mutable stainPixels = 0 let mutable darkStainPixels = 0 @@ -175,7 +175,7 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img: nbElement <- nbElement + 1 let infected = infection.Data.[y, x, 0] > 0uy - let stain = parasites.stain.Data.[y, x, 0] > 0uy + let stain = parasites.cytoplasm.Data.[y, x, 0] > 0uy let darkStain = parasites.darkStain.Data.[y, x, 0] > 0uy if infected @@ -184,19 +184,19 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img: if stain then - stainPixels.Add(Point(x, y)) + cytoplasmPixels.Add(Point(x, y)) if darkStain then darkStainPixels <- darkStainPixels + 1 - let mutable stainArea = 0 + let mutable cytoplasmArea = 0 if infectedPixels.Count > 0 then - for stainPixel in stainPixels do - if infectedPixels.Exists(fun p -> pown (p.X - stainPixel.X) 2 + pown (p.Y - stainPixel.Y) 2 <= perimeterParasiteSquared) + for cytoplasmPixel in cytoplasmPixels do + if infectedPixels.Exists(fun p -> pown (p.X - cytoplasmPixel.X) 2 + pown (p.Y - cytoplasmPixel.Y) 2 <= perimeterParasiteSquared) then - stainArea <- stainArea + 1 + cytoplasmArea <- cytoplasmArea + 1 let cellClass = @@ -205,9 +205,9 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img: then Peculiar - elif infectedPixels.Count > 0 && stainArea >= minimumParasiteArea + elif infectedPixels.Count > 0 && cytoplasmArea >= minimumParasiteArea then - let infectionToRemove = ImgTools.connectedComponents parasites.stain infectedPixels + let infectionToRemove = ImgTools.connectedComponents parasites.cytoplasm infectedPixels for p in infectionToRemove do infection.Data.[p.Y, p.X, 0] <- 0uy InfectedRBC @@ -217,6 +217,6 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img: Some { cellClass = cellClass center = Point(roundInt e.Cx, roundInt e.Cy) - infectedArea = if cellClass = InfectedRBC then infectedPixels.Count else 0 - stainArea = stainArea + nucleusArea = if cellClass = InfectedRBC then infectedPixels.Count else 0 + parasiteArea = cytoplasmArea elements = elements }) diff --git a/Parasitemia/ParasitemiaCore/Config.fs b/Parasitemia/ParasitemiaCore/Config.fs index 1110096..3089953 100644 --- a/Parasitemia/ParasitemiaCore/Config.fs +++ b/Parasitemia/ParasitemiaCore/Config.fs @@ -13,8 +13,9 @@ type Parameters = { rbcDiameter: float<μm> resolution: float - colorContribution_BG_RBC: float * float * float // (R, G, B). - colorContribution_RBC_parasite: float * float * float // (R, G, B). + averageColor_BG: float32 * float32 * float32 // R * G * B. + averageColor_RBC: float32 * float32 * float32 // R * G * B. + averageColor_Parasite: float32 * float32 * float32 // R * G * B. ratioAreaPaleCenter: float32 // The area of the second opening is 'ratioSecondAreaOpen' * mean RBC area. It's applied only if greater than 'initialAreaOpen'. @@ -24,7 +25,6 @@ type Parameters = { maxRbcRadius: float32 // Factor of the mean RBC radius. LPFStandardDeviationParasite: float<μm> // Sigma parameter of the gaussian to remove the high frequency noise. - LPFStandardDeviationStain: float<μm> LPFStandardDeviationRBC: float<μm> // Ellipse. @@ -35,8 +35,8 @@ type Parameters = { maxDarkStainRatio: float // When a cell must own less than this ratio to be a RBC. parasiteRadiusRatio: float32 // The ratio of the parasite radius of the RBC radius. - minimumParasiteAreaRatio: float32 // Factor of a RBC area. 0.5 means the half of RBC area. + cytoplasmSensitivity: float // between 0 (the least sensitive) and 1 (the most sensitive). nucleusAreaRatio: float32 // Factor of a RBC area. 0.5 means the half of RBC area. @@ -50,8 +50,13 @@ let defaultParameters = { rbcDiameter = 8.<μm> resolution = 220.e3 // 220.e3 Correspond to 50X. - colorContribution_BG_RBC = (* 0., 1., 0. *) 0.16, 0.44, 0.4 - colorContribution_RBC_parasite = (* 1., 0., 0. *) 0.54, 0.41, 0.05 + averageColor_BG = 113.3f, 135.3f, 150.3f + averageColor_RBC = 94.7f, 80.7f, 99.3f + averageColor_Parasite = 76.f, 58.f, 94.f + + (*averageColor_BG = 179.f, 148.f, 121.f + averageColor_RBC = 141.f, 96.f, 83.f + averageColor_Parasite = 123.f, 89.f, 83.f*) ratioAreaPaleCenter = 2.f / 5.f // The ratio between an RBC area and the area of the its pale center. @@ -61,12 +66,11 @@ let defaultParameters = { maxRbcRadius = 0.3f LPFStandardDeviationParasite = 0.15<μm> - LPFStandardDeviationStain = 0.15<μm> // 0.12 - LPFStandardDeviationRBC = 0.2<μm> // 8.5e-6. // 0.2<μm> + LPFStandardDeviationRBC = 0.2<μm> factorNbPick = 1.0 - darkStainLevel = 0.25 // 0.3 + darkStainLevel = 0.25 maxDarkStainRatio = 0.1 // 10 % parasiteRadiusRatio = 0.5f // 40 % @@ -124,10 +128,6 @@ type Config (param: Parameters) = let stdDeviation: float = (μmToInch parameters.LPFStandardDeviationParasite) * parameters.resolution float stdDeviation - member this.LPFStandardDeviationStain = - let stdDeviation: float = (μmToInch parameters.LPFStandardDeviationStain) * parameters.resolution - float stdDeviation - member this.LPFStandardDeviationRBC = let stdDeviation: float = (μmToInch parameters.LPFStandardDeviationRBC) * parameters.resolution float stdDeviation diff --git a/Parasitemia/ParasitemiaCore/MainAnalysis.fs b/Parasitemia/ParasitemiaCore/MainAnalysis.fs index cae6427..5a8e16d 100644 --- a/Parasitemia/ParasitemiaCore/MainAnalysis.fs +++ b/Parasitemia/ParasitemiaCore/MainAnalysis.fs @@ -53,11 +53,11 @@ let doAnalysis (img: Image) (name: string) (config: Config) (reportPr use img_float = img.Convert() // use img_RBC = mergeChannels img_float config.Parameters.colorContribution_BG_RBC - use img_RBC = mergeChannelsWithProjection img_float (94.7f, 80.7f, 99.3f) (113.3f, 135.3f, 150.3f) 255. + use img_RBC = mergeChannelsWithProjection img_float config.Parameters.averageColor_RBC config.Parameters.averageColor_BG 255. let img_RBC_filtered = gaussianFilter img_RBC config.LPFStandardDeviationRBC //use img_parasites = mergeChannels img_float config.Parameters.colorContribution_RBC_parasite - use img_parasites = mergeChannelsWithProjection img_float (76.f, 58.f, 94.f) (94.7f, 80.7f, 99.3f) 255. + use img_parasites = mergeChannelsWithProjection img_float config.Parameters.averageColor_Parasite config.Parameters.averageColor_RBC 255. logWithName (sprintf "Nominal erytrocyte diameter: %A" config.RBCRadiusByResolution) @@ -114,8 +114,8 @@ let doAnalysis (img: Image) (name: string) (config: Config) (reportPr saveMat (edges * 255.0) (buildFileName " - edges.png") saveImg parasites.darkStain (buildFileName " - parasites - dark stain.png") - saveImg parasites.stain (buildFileName " - parasites - stain.png") - saveImg parasites.infection (buildFileName " - parasites - infection.png") + saveImg parasites.cytoplasm (buildFileName " - parasites - stain.png") + saveImg parasites.nucleus (buildFileName " - parasites - infection.png") let imgAllEllipses = img.Copy() drawEllipses imgAllEllipses matchingEllipses.Ellipses (Bgr(255.0, 255.0, 255.0)) 0.04 diff --git a/Parasitemia/ParasitemiaCore/ParasitesMarker.fs b/Parasitemia/ParasitemiaCore/ParasitesMarker.fs index 73f9fe1..e3c3ca2 100644 --- a/Parasitemia/ParasitemiaCore/ParasitesMarker.fs +++ b/Parasitemia/ParasitemiaCore/ParasitesMarker.fs @@ -9,61 +9,23 @@ open Emgu.CV.Structure open Utils type Result = { - darkStain: Image - infection: Image - stain: Image } + darkStain: Image // Colored pixel, it's independent of the size of the areas. It corresponds to white cells, schizontes, gametocytes, throphozoites. + nucleus: Image // Parasite nucleus. It may contain some debris. It shouldn't contain thrombocytes or larger elements. + cytoplasm: Image } // Parasite cytoplasm. -// Create three binary markers : -// * '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 findMa (green: Image) (filteredGreen: Image) (config: Config.Config) : Result * Image * Image = - // We use the filtered image to find the dark stain. - let kmediansResults = KMedians.kmedians filteredGreen - 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 * float config.Parameters.darkStainLevel, CvEnum.CmpType.GreaterThan) - darkStain._And(filteredGreen.Cmp(median_fg, CvEnum.CmpType.LessThan)) - darkStain._And(fg) - - let fgFloat = (fg / 255.0).Convert() - use greenWithoutBg = ImgTools.gaussianFilter green 1.0 - greenWithoutBg.SetValue(Gray(0.0), fg.Not()) - - let findSmears (sigma: float) (level: float) : Image = - use greenWithoutBgSmoothed = ImgTools.gaussianFilter greenWithoutBg sigma - use fgSmoothed = ImgTools.gaussianFilter fgFloat sigma - let smears = (greenWithoutBg.Mul(fgSmoothed)).Cmp(greenWithoutBgSmoothed.Mul(level), CvEnum.CmpType.LessThan) - smears._And(fg) - smears - - let tmp = filteredGreen.Convert() - - { darkStain = darkStain; - stain = findSmears 10. 0.9 - infection = findSmears 2.2 0.87 }, - tmp, - tmp - -// Create three binary markers : -// * '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 (img: Image) (config: Config.Config) : Result * Image * Image = - let imgFilteredInfection = ImgTools.gaussianFilter img config.LPFStandardDeviationParasite - let filteredGreenWithoutInfection = imgFilteredInfection.Copy() - ImgTools.areaCloseF filteredGreenWithoutInfection (roundInt config.RBCRadius.NucleusArea) + let imgFilteredParasite = ImgTools.gaussianFilter img config.LPFStandardDeviationParasite - (* - let filteredGreenWithoutStain = filteredGreenWithoutInfection.Copy() - ImgTools.areaCloseF filteredGreenWithoutStain (int config.RBCRadius.StainArea) *) + let filteredGreenWithoutNucleus = imgFilteredParasite.Copy() + ImgTools.areaCloseF filteredGreenWithoutNucleus (roundInt config.RBCRadius.NucleusArea) let darkStain = // We use the filtered image to find the dark stain. let _, mean_fg, mean_bg = - let hist = ImgTools.histogramImg filteredGreenWithoutInfection 300 + let hist = ImgTools.histogramImg filteredGreenWithoutNucleus 300 ImgTools.otsu hist - filteredGreenWithoutInfection.Cmp(-(float mean_bg) * config.Parameters.darkStainLevel + (float mean_fg), CvEnum.CmpType.LessThan) + filteredGreenWithoutNucleus.Cmp(-(float mean_bg) * config.Parameters.darkStainLevel + (float mean_fg), CvEnum.CmpType.LessThan) let marker (img: Image) (closed: Image) (level: float) : Image = let diff = img.Copy() @@ -72,13 +34,9 @@ let find (img: Image) (config: Config.Config) : Result * Image() - let infectionMarker = marker imgFilteredInfection filteredGreenWithoutInfection (1. / config.Parameters.infectionSensitivity) - - let imgFilteredStain = ImgTools.gaussianFilter img config.LPFStandardDeviationStain - let areaOpening = int <| config.RBCRadius.Area * config.Parameters.ratioAreaPaleCenter - //ImgTools.areaOpenF imgFilteredStain areaOpening + let nucleusMarker = marker imgFilteredParasite filteredGreenWithoutNucleus (1. / config.Parameters.infectionSensitivity) - let filteredGreenWithoutStain = imgFilteredStain.CopyBlank() + let filteredGreenWithoutCytoplasm = imgFilteredParasite.CopyBlank() let kernelSize = let size = roundInt (config.RBCRadius.Pixel / 5.f) if size % 2 = 0 then size + 1 else size @@ -89,18 +47,13 @@ let find (img: Image) (config: Config.Config) : Result * Image } [] diff --git a/Parasitemia/ParasitemiaUI/State.fs b/Parasitemia/ParasitemiaUI/State.fs index e15dc9b..5b6a91c 100644 --- a/Parasitemia/ParasitemiaUI/State.fs +++ b/Parasitemia/ParasitemiaUI/State.fs @@ -142,7 +142,7 @@ type State (defaultConfig: ParasitemiaCore.Config.Config) = sourceImage.rbcs <- cells |> List.filter (fun cell -> match cell.cellClass with ParasitemiaCore.Types.HealthyRBC | ParasitemiaCore.Types.InfectedRBC -> true | _ -> false ) - |> List.sortByDescending (fun cell -> cell.infectedArea, (w - cell.center.X) + (h - cell.center.Y)) + |> List.sortByDescending (fun cell -> cell.nucleusArea, (w - cell.center.X) + (h - cell.center.Y)) |> List.mapi (fun i cell -> let center = Point(float cell.center.X, float cell.center.Y) let infected, setManually = @@ -155,7 +155,7 @@ type State (defaultConfig: ParasitemiaCore.Config.Config) = setManually = setManually center = center size = Size(float cell.elements.Width, float cell.elements.Height) - infectedArea = cell.infectedArea }) + infectedArea = cell.nucleusArea }) alteredSinceLastSave <- true -- 2.43.0