Cleaning + renaming.
authorGreg Burri <greg.burri@gmail.com>
Mon, 25 Jan 2016 23:15:47 +0000 (00:15 +0100)
committerGreg Burri <greg.burri@gmail.com>
Mon, 25 Jan 2016 23:15:47 +0000 (00:15 +0100)
Parasitemia/ParasitemiaCore/Classifier.fs
Parasitemia/ParasitemiaCore/Config.fs
Parasitemia/ParasitemiaCore/ImgTools.fs
Parasitemia/ParasitemiaCore/MainAnalysis.fs
Parasitemia/ParasitemiaCore/ParasitesMarker.fs

index 2b6f3be..615840c 100644 (file)
@@ -24,8 +24,6 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img:
     then
         []
     else
-        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)
                                           KdTree.maxX = e.Cx + (e.A + config.RBCRadius.Max)
@@ -150,6 +148,11 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img:
         // 5) Define pixels associated to each ellipse and create the cells.
         let perimeterParasiteSquared = (2.f * config.RBCRadius.ParasiteRadius) ** 2.f |> roundInt
         let minimumParasiteArea = config.RBCRadius.MinimumParasiteArea |> roundInt
+
+        let nucleusData = parasites.nucleus.Copy().Data // Will be modified thus the copy.
+        let parasiteData = parasites.parasite.Data
+        let darkStainData = parasites.darkStain.Data
+
         ellipsesWithNeigbors
         |> List.choose (fun (e, neighbors) ->
             if e.Removed
@@ -158,10 +161,9 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img:
             else
                 let minX, minY, maxX, maxY = ellipseWindow e
 
-                let infectedPixels = List<Point>()
-                let cytoplasmPixels = List<Point>()
+                let nucleusPixels = List<Point>()
+                let parasitePixels = List<Point>()
 
-                //let mutable stainPixels = 0
                 let mutable darkStainPixels = 0
                 let mutable nbElement = 0
 
@@ -171,45 +173,40 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img:
                         let p = PointF(float32 x, float32 y)
                         if pixelOwnedByE p e (neighbors |> List.choose (fun (otherE, p1, p2) -> if otherE.Removed then None else Some (otherE :> Ellipse, Utils.lineFromTwoPoints p1 p2)))
                         then
-                            elements.[y-minY, x-minX] <- 1uy
+                            elements.[y - minY, x - minX] <- 1uy
                             nbElement <- nbElement + 1
 
-                            let infected = infection.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
+                            if nucleusData.[y, x, 0] > 0uy
                             then
-                                infectedPixels.Add(Point(x, y))
+                                nucleusPixels.Add(Point(x, y))
 
-                            if stain
+                            if parasiteData.[y, x, 0] > 0uy
                             then
-                                cytoplasmPixels.Add(Point(x, y))
+                                parasitePixels.Add(Point(x, y))
 
-                            if darkStain
+                            if darkStainData.[y, x, 0] > 0uy
                             then
                                 darkStainPixels <- darkStainPixels + 1
 
-                let mutable cytoplasmArea = 0
-                if infectedPixels.Count > 0
+                let mutable parasiteArea = 0
+                if nucleusPixels.Count > 0
                 then
-                    for cytoplasmPixel in cytoplasmPixels do
-                        if infectedPixels.Exists(fun p -> pown (p.X - cytoplasmPixel.X) 2 + pown (p.Y - cytoplasmPixel.Y) 2 <= perimeterParasiteSquared)
+                    for parasitePixel in parasitePixels do
+                        if nucleusPixels.Exists(fun p -> pown (p.X - parasitePixel.X) 2 + pown (p.Y - parasitePixel.Y) 2 <= perimeterParasiteSquared)
                         then
-                            cytoplasmArea <- cytoplasmArea + 1
+                            parasiteArea <- parasiteArea + 1
 
 
                 let cellClass =
                     if float darkStainPixels > config.Parameters.maxDarkStainRatio * (float nbElement)
-                       //|| float stainPixels > config.Parameters.maxStainRatio * (float nbElement)
                     then
                         Peculiar
 
-                    elif infectedPixels.Count > 0 && cytoplasmArea >= minimumParasiteArea
+                    elif nucleusPixels.Count > 0 && parasiteArea >= minimumParasiteArea
                     then
-                        let infectionToRemove = ImgTools.connectedComponents parasites.cytoplasm infectedPixels
+                        let infectionToRemove = ImgTools.connectedComponents parasites.parasite nucleusPixels
                         for p in infectionToRemove do
-                            infection.Data.[p.Y, p.X, 0] <- 0uy
+                            nucleusData.[p.Y, p.X, 0] <- 0uy
                         InfectedRBC
 
                     else
@@ -217,6 +214,6 @@ let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img:
 
                 Some { cellClass = cellClass
                        center = Point(roundInt e.Cx, roundInt e.Cy)
-                       nucleusArea = if cellClass = InfectedRBC then infectedPixels.Count else 0
-                       parasiteArea = cytoplasmArea
+                       nucleusArea = if cellClass = InfectedRBC then nucleusPixels.Count else 0
+                       parasiteArea = parasiteArea
                        elements = elements })
index 3089953..4b38483 100644 (file)
@@ -13,10 +13,6 @@ type Parameters = {
     rbcDiameter: float<μm>
     resolution: float<ppi>
 
-    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'.
 
     granulometryRange: float32 // The radius will be seeked from radius - granulometryRange * radius to radius + granulometryRange * radius.
@@ -43,21 +39,12 @@ type Parameters = {
     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
-    minimumCellAreaFactor: float32 // Factor of the mean RBC area. A cell with an area below this will be rejected.
-}
+    minimumCellAreaFactor: float32 } // Factor of the mean RBC area. A cell with an area below this will be rejected.
 
 let defaultParameters = {
     rbcDiameter = 8.<μm>
     resolution = 220.e3<ppi> // 220.e3<ppi> Correspond to 50X.
 
-    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.
 
     granulometryRange = 0.5f
@@ -76,12 +63,12 @@ let defaultParameters = {
     parasiteRadiusRatio = 0.5f // 40 %
 
     minimumParasiteAreaRatio = 0.02f // 2 %
-    cytoplasmSensitivity = 0.96 //  1) 0.91, 2) 0.92
+    cytoplasmSensitivity = 0.96
 
     nucleusAreaRatio = 0.01f // 1.0 %
-    infectionSensitivity = 0.9 // 1) 0.93, 2) 0.94
+    infectionSensitivity = 0.9
 
-    standardDeviationMaxRatio = 0.5 // 0.5
+    standardDeviationMaxRatio = 0.5
     minimumCellAreaFactor = 0.4f }
 
 type RBCRadius (radius: float32, parameters: Parameters) =
index 51b0c19..cbb65b4 100644 (file)
@@ -49,12 +49,7 @@ let mergeChannelsWithProjection (img: Image<Bgr, float32>) (v1r: float32, v1g: f
 
 // Normalize image values between 0uy and 255uy.
 let normalizeAndConvert (img: Image<Gray, 'TDepth>) : Image<Gray, byte> =
-    let min = ref [| 0.0 |]
-    let minLocation = ref <| [| Point() |]
-    let max = ref [| 0.0 |]
-    let maxLocation = ref <| [| Point() |]
-    img.MinMax(min, max, minLocation, maxLocation)
-    ((img.Convert<Gray, float32>() - (!min).[0]) / ((!max).[0] - (!min).[0]) * 255.0).Convert<Gray, byte>()
+    (normalize (img.Convert<Gray, float32>()) 255.).Convert<Gray, byte>()
 
 let saveImg (img: Image<'TColor, 'TDepth>) (filepath: string) =
     img.Save(filepath)
index 5a8e16d..9c85c7b 100644 (file)
@@ -52,22 +52,20 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) (reportPr
 
         use img_float = img.Convert<Bgr, float32>()
 
-        // use img_RBC = mergeChannels img_float config.Parameters.colorContribution_BG_RBC
-        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_RBC = img_float.[1] // mergeChannelsWithProjection img_float config.Parameters.averageColor_RBC config.Parameters.averageColor_BG 255.
+        use 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 config.Parameters.averageColor_Parasite config.Parameters.averageColor_RBC 255.
+        use img_parasites = img_float.[2] // mergeChannelsWithProjection img_float config.Parameters.averageColor_Parasite config.Parameters.averageColor_RBC 255.
+        use img_parasites_filtered = gaussianFilter img_parasites config.LPFStandardDeviationParasite
 
         logWithName (sprintf "Nominal erytrocyte diameter: %A" config.RBCRadiusByResolution)
 
         let initialAreaOpening = int <| config.RBCRadiusByResolution.Area * config.Parameters.ratioAreaPaleCenter * 1.1f // We do an area opening a little larger to avoid to do a second one in the case the radius found is near the initial one.
-        do! logTimeWithName "First area opening" (fun () -> ImgTools.areaOpenF img_RBC_filtered initialAreaOpening; report 10)
+        do! logTimeWithName "First area opening" (fun () -> areaOpenF img_RBC_filtered initialAreaOpening; report 10)
 
         let range =
             let delta = config.Parameters.granulometryRange * config.RBCRadiusByResolution.Pixel
             int <| config.RBCRadiusByResolution.Pixel - delta, int <| config.RBCRadiusByResolution.Pixel + delta
-        //let r1 = log "Granulometry (morpho)" (fun() -> Granulometry.findRadiusByClosing (filteredGreen.Convert<Gray, byte>()) range 1.0 |> float32)
         let! radius = logTimeWithName "Granulometry (area)" (fun() -> reportWithVal 10 (Granulometry.findRadiusByAreaClosing img_RBC_filtered range |> float32))
         config.SetRBCRadius <| radius
 
@@ -79,20 +77,20 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) (reportPr
             let secondAreaOpening = int <| config.RBCRadius.Area * config.Parameters.ratioAreaPaleCenter
             if secondAreaOpening > initialAreaOpening
             then
-                logTimeWithName "Second area opening" (fun () -> ImgTools.areaOpenF img_RBC_filtered secondAreaOpening; report 30)
+                logTimeWithName "Second area opening" (fun () -> areaOpenF img_RBC_filtered secondAreaOpening; report 30)
             else
                 report 30
 
-        // Removing of parasites.
-        ImgTools.areaCloseF img_RBC_filtered (roundInt <| Const.PI * config.RBCRadius.ParasiteRadius ** 2.f)
+        // Removing parasites.
+        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
+        let! parasites, imgWhitoutParasite, imgWithoutNucleus =
+            logTimeWithName "Parasites segmentation" (fun () -> reportWithVal 40 (ParasitesMarker.find img_parasites_filtered config))
 
         let! edges, xGradient, yGradient = logTimeWithName "Finding edges" (fun () ->
-            let edges, xGradient, yGradient = ImgTools.findEdges img_RBC_filtered
+            let edges, xGradient, yGradient = findEdges img_RBC_filtered
             removeArea edges (config.RBCRadius.Pixel ** 2.f / 50.f |> int)
-            reportWithVal 40 (edges, xGradient, yGradient))
+            reportWithVal 50 (edges, xGradient, yGradient))
 
         let! matchingEllipses = logTimeWithName "Finding ellipses" (fun () -> reportWithVal 60 (Ellipse.find edges xGradient yGradient config))
 
@@ -114,7 +112,7 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) (reportPr
                 saveMat (edges * 255.0) (buildFileName " - edges.png")
 
                 saveImg parasites.darkStain (buildFileName " - parasites - dark stain.png")
-                saveImg parasites.cytoplasm (buildFileName " - parasites - stain.png")
+                saveImg parasites.parasite (buildFileName " - parasites - stain.png")
                 saveImg parasites.nucleus (buildFileName " - parasites - infection.png")
 
                 let imgAllEllipses = img.Copy()
@@ -134,16 +132,20 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) (reportPr
                 saveImg imgCells' (buildFileName " - cells - full.png")
 
                 let filteredGreenMaxima = gaussianFilter img_RBC config.LPFStandardDeviationRBC
-                for m in ImgTools.findMaxima filteredGreenMaxima do
-                    ImgTools.drawPoints filteredGreenMaxima m 255.f
+                for m in findMaxima filteredGreenMaxima do
+                    drawPoints filteredGreenMaxima m 255.f
                 saveImg filteredGreenMaxima (buildFileName " - filtered - maxima.png")
 
                 saveImg img_RBC_filtered (buildFileName " - filtered.png")
-                saveImg filteredGreenWhitoutStain (buildFileName " - filtered closed stain.png")
-                saveImg filteredGreenWithoutInfection (buildFileName " - filtered closed infection.png")
+                saveImg imgWhitoutParasite (buildFileName " - filtered closed stain.png")
+                saveImg imgWithoutNucleus (buildFileName " - filtered closed infection.png")
 
                 saveImg img_RBC (buildFileName " - source - RBC.png")
                 saveImg img_parasites (buildFileName " - source - parasites.png")
+
+                saveImg (normalize img_float.[2] 255.) (buildFileName " - source - red.png")
+                saveImg (normalize img_float.[1] 255.) (buildFileName " - source - green.png")
+                saveImg (normalize img_float.[0] 255.) (buildFileName " - source - blue.png")
             | _ -> ()
 
         return cells }
index e3c3ca2..6c25d04 100644 (file)
@@ -7,25 +7,24 @@ open Emgu.CV
 open Emgu.CV.Structure
 
 open Utils
+open ImgTools
 
 type Result = {
     darkStain: Image<Gray, byte> // Colored pixel, it's independent of the size of the areas. It corresponds to white cells, schizontes, gametocytes, throphozoites.
     nucleus: Image<Gray, byte> // Parasite nucleus. It may contain some debris. It shouldn't contain thrombocytes or larger elements.
-    cytoplasm: Image<Gray, byte> } // Parasite cytoplasm.
+    parasite: Image<Gray, byte> } // The whole parasites.
 
 let find (img: Image<Gray, float32>) (config: Config.Config) : Result * Image<Gray, float32> * Image<Gray, float32> =
 
-    let imgFilteredParasite = ImgTools.gaussianFilter img config.LPFStandardDeviationParasite
-
-    let filteredGreenWithoutNucleus = imgFilteredParasite.Copy()
-    ImgTools.areaCloseF filteredGreenWithoutNucleus (roundInt config.RBCRadius.NucleusArea)
+    let imgWithoutNucleus = img.Copy()
+    areaCloseF imgWithoutNucleus (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 filteredGreenWithoutNucleus 300
-            ImgTools.otsu hist
-        filteredGreenWithoutNucleus.Cmp(-(float mean_bg) * config.Parameters.darkStainLevel + (float mean_fg), CvEnum.CmpType.LessThan)
+            let hist = histogramImg imgWithoutNucleus 300
+            otsu hist
+        imgWithoutNucleus.Cmp(-(float mean_bg) * config.Parameters.darkStainLevel + (float mean_fg), CvEnum.CmpType.LessThan)
 
     let marker (img: Image<Gray, float32>) (closed: Image<Gray, float32>) (level: float) : Image<Gray, byte> =
         let diff = img.Copy()
@@ -34,9 +33,9 @@ let find (img: Image<Gray, float32>) (config: Config.Config) : Result * Image<Gr
         diff._ThresholdBinary(Gray(0.0), Gray(255.))
         diff.Convert<Gray, byte>()
 
-    let nucleusMarker = marker imgFilteredParasite filteredGreenWithoutNucleus (1. / config.Parameters.infectionSensitivity)
+    let nucleusMarker = marker img imgWithoutNucleus (1. / config.Parameters.infectionSensitivity)
 
-    let filteredGreenWithoutCytoplasm = imgFilteredParasite.CopyBlank()
+    let imgWithoutParasite = img.CopyBlank()
     let kernelSize =
         let size = roundInt (config.RBCRadius.Pixel / 5.f)
         if size % 2 = 0 then size + 1 else size
@@ -47,13 +46,13 @@ let find (img: Image<Gray, float32>) (config: Config.Config) : Result * Image<Gr
         else
             CvInvoke.GetStructuringElement(CvEnum.ElementShape.Ellipse, Size(kernelSize, kernelSize), Point(-1, -1))
 
-    CvInvoke.MorphologyEx(imgFilteredParasite, filteredGreenWithoutCytoplasm, CvEnum.MorphOp.Close, kernel, Point(-1, -1), 1, CvEnum.BorderType.Replicate, MCvScalar())
-    let cytoplasmMarker = marker imgFilteredParasite filteredGreenWithoutCytoplasm (1. / config.Parameters.cytoplasmSensitivity)
+    CvInvoke.MorphologyEx(img, imgWithoutParasite, CvEnum.MorphOp.Close, kernel, Point(-1, -1), 1, CvEnum.BorderType.Replicate, MCvScalar())
+    let parasiteMarker = marker img imgWithoutParasite (1. / config.Parameters.cytoplasmSensitivity)
 
     { darkStain = darkStain
       nucleus = nucleusMarker
-      cytoplasm = cytoplasmMarker },
-    filteredGreenWithoutCytoplasm,
-    filteredGreenWithoutNucleus
+      parasite = parasiteMarker },
+    imgWithoutParasite,
+    imgWithoutNucleus