// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [<assembly: AssemblyVersion("1.0.*")>]
-[<assembly: AssemblyVersion("1.0.0.3")>]
-[<assembly: AssemblyFileVersion("1.0.0.3")>]
+[<assembly: AssemblyVersion("1.0.0.4")>]
+[<assembly: AssemblyFileVersion("1.0.0.4")>]
do
()
\ No newline at end of file
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
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
rbcDiameter = 8.<μm>
resolution = 220.e3<ppi> // 220.e3<ppi> 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.
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 }
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)
open Types
open Utils
+let normalize (img: Image<Gray, float32>) (upperLimit: float) : Image<Gray, float32> =
+ 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<Bgr, float32>) (rgbWeights: float * float * float) : Image<Gray, float32> =
+ 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<Gray, float32>(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<Bgr, float32>) (v1r: float32, v1g: float32, v1b: float32) (v2r: float32, v2g: float32, v2b: float32) (upperLimit: float) : Image<Gray, float32> =
+ 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<Gray, float32>(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<Gray, 'TDepth>) : Image<Gray, byte> =
let min = ref [| 0.0 |]
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]
logWithName "Starting analysis ..."
- use img_RBC =
- use imgFloat = img.Convert<Bgr, float32>()
- let redFactor, greenFactor, blueFactor = config.Parameters.colorContribution_BG_RBC
- blueFactor * imgFloat.[0] + greenFactor * imgFloat.[1] + redFactor * imgFloat.[2]
+ use img_float = img.Convert<Bgr, float32>()
+ // 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<Bgr, float32>()
- 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)
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
drawEllipses imgAllEllipses matchingEllipses.Ellipses (Bgr(255.0, 255.0, 255.0)) 0.04
saveImg imgAllEllipses (buildFileName " - ellipses - all.png")
- let imgEllipses = filteredGreenWhitoutStain.Convert<Bgr, byte>()
+ let imgEllipses = img_RBC_filtered.Convert<Bgr, byte>()
drawEllipses imgEllipses prunedEllipses (Bgr(0.0, 240.0, 240.0)) 1.0
saveImg imgEllipses (buildFileName " - ellipses.png")
let imgFilteredInfection = ImgTools.gaussianFilter img config.LPFStandardDeviationParasite
let filteredGreenWithoutInfection = imgFilteredInfection.Copy()
- ImgTools.areaCloseF filteredGreenWithoutInfection (int config.RBCRadius.InfectionArea)
+ ImgTools.areaCloseF filteredGreenWithoutInfection (roundInt config.RBCRadius.NucleusArea)
(*
let filteredGreenWithoutStain = filteredGreenWithoutInfection.Copy()
CvInvoke.GetStructuringElement(CvEnum.ElementShape.Rectangle, Size(3, 3), Point(-1, -1))
else
CvInvoke.GetStructuringElement(CvEnum.ElementShape.Ellipse, Size(kernelSize, kernelSize), Point(-1, -1))
+
CvInvoke.MorphologyEx(imgFilteredStain, filteredGreenWithoutStain, CvEnum.MorphOp.Close, kernel, Point(-1, -1), 1, CvEnum.BorderType.Replicate, MCvScalar())
- let stainMarker = marker (*filteredGreenWithoutInfection*) imgFilteredStain filteredGreenWithoutStain (1. / config.Parameters.stainSensitivity)
+ let stainMarker = marker (*filteredGreenWithoutInfection*) imgFilteredStain filteredGreenWithoutStain (1. / config.Parameters.cytoplasmSensitivity)
//
(*let blackTopHat = filteredGreenWithoutStain.CopyBlank()
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [<assembly: AssemblyVersion("1.0.*")>]
-[<assembly: AssemblyVersion("1.0.0.3")>]
-[<assembly: AssemblyFileVersion("1.0.0.3")>]
+[<assembly: AssemblyVersion("1.0.0.4")>]
+[<assembly: AssemblyFileVersion("1.0.0.4")>]
do
()
\ No newline at end of file