From 3b645f8ff5259f88a33ffbd9a63b10a8640c439f Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Mon, 25 Jan 2016 16:01:36 +0100 Subject: [PATCH] Project the colors to have the best contrast for RBCs and parasites analyze. --- Parasitemia/ParasitemiaCore/AssemblyInfo.fs | 4 +-- Parasitemia/ParasitemiaCore/Classifier.fs | 8 ++--- Parasitemia/ParasitemiaCore/Config.fs | 30 +++++++++------- Parasitemia/ParasitemiaCore/ImgTools.fs | 36 ++++++++++++++++++- Parasitemia/ParasitemiaCore/MainAnalysis.fs | 18 +++++----- .../ParasitemiaCore/ParasitesMarker.fs | 5 +-- Parasitemia/ParasitemiaUI/AssemblyInfo.fs | 4 +-- 7 files changed, 69 insertions(+), 36 deletions(-) diff --git a/Parasitemia/ParasitemiaCore/AssemblyInfo.fs b/Parasitemia/ParasitemiaCore/AssemblyInfo.fs index 89d76f0..c129f42 100644 --- a/Parasitemia/ParasitemiaCore/AssemblyInfo.fs +++ b/Parasitemia/ParasitemiaCore/AssemblyInfo.fs @@ -34,8 +34,8 @@ open System.Runtime.InteropServices // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [] -[] -[] +[] +[] do () \ No newline at end of file diff --git a/Parasitemia/ParasitemiaCore/Classifier.fs b/Parasitemia/ParasitemiaCore/Classifier.fs index a3bfa9c..96532f8 100644 --- a/Parasitemia/ParasitemiaCore/Classifier.fs +++ b/Parasitemia/ParasitemiaCore/Classifier.fs @@ -148,12 +148,8 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img: e.Removed <- true // 5) Define pixels associated to each ellipse and create the cells. - let radiusParasiteRatio = 0.4f - let radiusParasite = config.RBCRadius.Pixel * 0.5f - let perimeterParasiteSquared = (2.f * radiusParasite) ** 2.f |> roundInt - let parasiteOccupation = 0.08f // 8 % - let minimumParasiteArea = Const.PI * radiusParasite ** 2.f * parasiteOccupation |> roundInt - //let minimumStainArea = roundInt <| config.RBCRadius.Area * 0.02f // 1.5 % + let perimeterParasiteSquared = (2.f * config.RBCRadius.ParasiteRadius) ** 2.f |> roundInt + let minimumParasiteArea = config.RBCRadius.MinimumParasiteArea |> roundInt ellipsesWithNeigbors |> List.choose (fun (e, neighbors) -> if e.Removed diff --git a/Parasitemia/ParasitemiaCore/Config.fs b/Parasitemia/ParasitemiaCore/Config.fs index 9df9dc4..1110096 100644 --- a/Parasitemia/ParasitemiaCore/Config.fs +++ b/Parasitemia/ParasitemiaCore/Config.fs @@ -34,11 +34,12 @@ type Parameters = { darkStainLevel: float // Lower -> more sensitive. Careful about illumination on the borders. maxDarkStainRatio: float // When a cell must own less than this ratio to be a RBC. - stainArea: float32 // Factor of a RBC area. 0.5 means the half of RBC area. - stainSensitivity: float // between 0 (the least sensitive) and 1 (the most sensitive). - maxStainRatio: float // A cell must own less than this ratio to be a RBC. + parasiteRadiusRatio: float32 // The ratio of the parasite radius of the RBC radius. - infectionArea: float32 // Factor of a RBC area. 0.5 means the half of RBC area. + 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. infectionSensitivity: float // between 0 (the least sensitive) and 1 (the most sensitive). standardDeviationMaxRatio: float // The standard deviation of the pixel values of a cell can't be greater than standardDeviationMaxRatio * global standard deviation @@ -49,8 +50,8 @@ let defaultParameters = { rbcDiameter = 8.<μm> resolution = 220.e3 // 220.e3 Correspond to 50X. - colorContribution_BG_RBC = 0.16, 0.44, 0.4 - colorContribution_RBC_parasite = 0.54, 0.41, 0.05 + colorContribution_BG_RBC = (* 0., 1., 0. *) 0.16, 0.44, 0.4 + colorContribution_RBC_parasite = (* 1., 0., 0. *) 0.54, 0.41, 0.05 ratioAreaPaleCenter = 2.f / 5.f // The ratio between an RBC area and the area of the its pale center. @@ -68,12 +69,13 @@ let defaultParameters = { darkStainLevel = 0.25 // 0.3 maxDarkStainRatio = 0.1 // 10 % - infectionArea = 0.01f // 0.8 % // 0.012f - infectionSensitivity = 0.9 // 1) 0.93, 2) 0.94 + parasiteRadiusRatio = 0.5f // 40 % - stainArea = 0.08f // 6 % // 0.08f - stainSensitivity = 0.96 // 1) 0.91, 2) 0.92 - maxStainRatio = 0.12 // 12 % + minimumParasiteAreaRatio = 0.02f // 2 % + cytoplasmSensitivity = 0.96 // 1) 0.91, 2) 0.92 + + nucleusAreaRatio = 0.01f // 1.0 % + infectionSensitivity = 0.9 // 1) 0.93, 2) 0.94 standardDeviationMaxRatio = 0.5 // 0.5 minimumCellAreaFactor = 0.4f } @@ -89,8 +91,10 @@ type RBCRadius (radius: float32, parameters: Parameters) = member this.Area = PI * radius ** 2.f member this.MinArea = parameters.minimumCellAreaFactor * this.Area - member this.InfectionArea = parameters.infectionArea * this.Area - member this.StainArea = parameters.stainArea * this.Area + member this.ParasiteRadius = parameters.parasiteRadiusRatio * radius + + member this.NucleusArea = parameters.nucleusAreaRatio * this.Area + member this.MinimumParasiteArea = parameters.minimumParasiteAreaRatio * this.Area override this.ToString() = sprintf "%d px (%.1f μm)" (Utils.roundInt <| 2.f * radius) (2. * this.μm) diff --git a/Parasitemia/ParasitemiaCore/ImgTools.fs b/Parasitemia/ParasitemiaCore/ImgTools.fs index 4be893f..51b0c19 100644 --- a/Parasitemia/ParasitemiaCore/ImgTools.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools.fs @@ -13,6 +13,40 @@ open Const open Types open Utils +let normalize (img: Image) (upperLimit: float) : Image = + 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 normalized = (img - (!min).[0]) / ((!max).[0] - (!min).[0]) + if upperLimit = 1.0 + then normalized + else upperLimit * normalized + +let mergeChannels (img: Image) (rgbWeights: float * float * float) : Image = + match rgbWeights with + | 1., 0., 0. -> img.[2] + | 0., 1., 0. -> img.[1] + | 0., 0., 1. -> img.[0] + | redFactor, greenFactor, blueFactor -> + let result = new Image(img.Size) + CvInvoke.AddWeighted(result, 1., img.[2], redFactor, 0., result) + CvInvoke.AddWeighted(result, 1., img.[1], greenFactor, 0., result) + CvInvoke.AddWeighted(result, 1., img.[0], blueFactor, 0., result) + result + +let mergeChannelsWithProjection (img: Image) (v1r: float32, v1g: float32, v1b: float32) (v2r: float32, v2g: float32, v2b: float32) (upperLimit: float) : Image = + let vr, vg, vb = v2r - v1r, v2g - v1g, v2b - v1b + let vMagnitude = sqrt (vr ** 2.f + vg ** 2.f + vb ** 2.f) + let project (r: float32) (g: float32) (b: float32) = ((r - v1r) * vr + (g - v1g) * vg + (b - v1b) * vb) / vMagnitude + let result = new Image(img.Size) + // TODO: Essayer en bindant Data pour gagner du temps + for i in 0 .. img.Height - 1 do + for j in 0 .. img.Width - 1 do + result.Data.[i, j, 0] <- project img.Data.[i, j, 2] img.Data.[i, j, 1] img.Data.[i, j, 0] + normalize result upperLimit + // Normalize image values between 0uy and 255uy. let normalizeAndConvert (img: Image) : Image = let min = ref [| 0.0 |] @@ -107,7 +141,7 @@ let otsu (hist: Histogram) : float32 * float32 * float32 = let mutable wB = 0 let mutable maximum = 0.0 let mutable level = 0 - let sum = hist.data |> Array.mapi (fun i v -> i * v) |> Array.sum |> float + let sum = hist.data |> Array.mapi (fun i v -> i * v |> float) |> Array.sum for i in 0 .. hist.data.Length - 1 do wB <- wB + hist.data.[i] diff --git a/Parasitemia/ParasitemiaCore/MainAnalysis.fs b/Parasitemia/ParasitemiaCore/MainAnalysis.fs index 813d090..cae6427 100644 --- a/Parasitemia/ParasitemiaCore/MainAnalysis.fs +++ b/Parasitemia/ParasitemiaCore/MainAnalysis.fs @@ -50,17 +50,14 @@ let doAnalysis (img: Image) (name: string) (config: Config) (reportPr logWithName "Starting analysis ..." - use img_RBC = - use imgFloat = img.Convert() - let redFactor, greenFactor, blueFactor = config.Parameters.colorContribution_BG_RBC - blueFactor * imgFloat.[0] + greenFactor * imgFloat.[1] + redFactor * imgFloat.[2] + 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. let img_RBC_filtered = gaussianFilter img_RBC config.LPFStandardDeviationRBC - use img_parasites = - use imgFloat = img.Convert() - let redFactor, greenFactor, blueFactor = config.Parameters.colorContribution_RBC_parasite - blueFactor * imgFloat.[0] + greenFactor * imgFloat.[1] + redFactor * imgFloat.[2] + //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. logWithName (sprintf "Nominal erytrocyte diameter: %A" config.RBCRadiusByResolution) @@ -86,7 +83,8 @@ let doAnalysis (img: Image) (name: string) (config: Config) (reportPr else report 30 - ImgTools.areaCloseF img_RBC_filtered (config.RBCRadius.Area * 0.1f |> roundInt) + // Removing of parasites. + ImgTools.areaCloseF img_RBC_filtered (roundInt <| Const.PI * config.RBCRadius.ParasiteRadius ** 2.f) let parasites, filteredGreenWhitoutStain, filteredGreenWithoutInfection = ParasitesMarker.find img_parasites config //let parasites, filteredGreenWhitoutInfection, filteredGreenWhitoutStain = ParasitesMarker.findMa greenFloat filteredGreenFloat config @@ -123,7 +121,7 @@ let doAnalysis (img: Image) (name: string) (config: Config) (reportPr drawEllipses imgAllEllipses matchingEllipses.Ellipses (Bgr(255.0, 255.0, 255.0)) 0.04 saveImg imgAllEllipses (buildFileName " - ellipses - all.png") - let imgEllipses = filteredGreenWhitoutStain.Convert() + let imgEllipses = img_RBC_filtered.Convert() drawEllipses imgEllipses prunedEllipses (Bgr(0.0, 240.0, 240.0)) 1.0 saveImg imgEllipses (buildFileName " - ellipses.png") diff --git a/Parasitemia/ParasitemiaCore/ParasitesMarker.fs b/Parasitemia/ParasitemiaCore/ParasitesMarker.fs index f4d9f35..73f9fe1 100644 --- a/Parasitemia/ParasitemiaCore/ParasitesMarker.fs +++ b/Parasitemia/ParasitemiaCore/ParasitesMarker.fs @@ -52,7 +52,7 @@ let find (img: Image) (config: Config.Config) : Result * Image) (config: Config.Config) : Result * Image] -[] -[] +[] +[] do () \ No newline at end of file -- 2.43.0