| _ ->
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 {
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 })
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
// 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.
}
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
let mutable fg = new Image<Gray, byte>(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<float>()
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 }
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<Gray, float32>() // 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<Gray, float32>() // 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<Gray, float32>()
let filteredGreenWhitoutStainFloat = filteredGreenWhitoutStain.Convert<Gray, float32>()
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
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")*)
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
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.
open Emgu.CV
open Emgu.CV.Structure
+open Utils
+
type Result = {
darkStain: Image<Gray, byte>
infection: Image<Gray, byte>
// * '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<Gray, byte>) (filteredGreenFloat: Image<Gray, float32>) (kmediansResult: KMedians.Result) (config: Config.Config) : Result * Image<Gray, byte> * Image<Gray, byte> =
+let find (filteredGreen: Image<Gray, byte>) (filteredGreenFloat: Image<Gray, float32>) (config: Config.Config) : Result * Image<Gray, byte> * Image<Gray, byte> =
+
+ 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<Gray, float32>()) 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<Gray, byte> * Image<Gray, byte> =
- let closed = filteredGreen.Copy()
- ImgTools.areaClose closed area
- let diff = closed - filteredGreen
+ let marker (img: Image<Gray, byte>) (closed: Image<Gray, byte>) (threshold: float) : Image<Gray, byte> =
+ 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
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