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.
+ cytoplasmSizeRatio: float32
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.
factorNbValidPick = 0.06 //1.0
factorNbMaxPick = 4.
- darkStainLevel = 0.25
+ darkStainLevel = 1.1
maxDarkStainRatio = 0.1 // 10 %
- parasiteRadiusRatio = 0.5f // 40 %
-
+ parasiteRadiusRatio = 0.5f // 50 %
minimumParasiteAreaRatio = 0.02f // 2 %
+
+ cytoplasmSizeRatio = 1.f / 5.f
cytoplasmSensitivity = 0.96
nucleusAreaRatio = 0.01f // 1.0 %
member this.MinArea = parameters.minimumCellAreaFactor * this.Area
member this.ParasiteRadius = parameters.parasiteRadiusRatio * radius
+ member this.CytoplasmSize = parameters.cytoplasmSizeRatio * radius
member this.NucleusArea = parameters.nucleusAreaRatio * this.Area
member this.MinimumParasiteArea = parameters.minimumParasiteAreaRatio * this.Area
// All ellipses with a score below this are removed.
let matchingScoreThresholdPerRadiusUnit = 0.07f // For a radius of 1. // 0.25
let matchingScorePower = 20.f
-let windowSizeRadiusFactor = 1.f / 2.f
+let windowSizeRadiusFactor = 1.f / 2.f // Used when searching for neighbor ellipses.
let minimumDistanceFromCenterRadiusFactor = 1.f / 3.f
+let minimumAreaFactor = 1.1f;
type private EllipseScoreFlaggedKd (matchingScore: float32, e: Ellipse) =
let mutable matchingScore = matchingScore
else
// Case where ellipses are overlapped.
match EEOver.EEOverlapArea e.Ellipse other.Ellipse with
- | Some (overlapArea, _, _) when e.Ellipse.Area < 1.1f * overlapArea || other.Ellipse.Area < 1.1f * overlapArea ->
+ | Some (overlapArea, _, _) when e.Ellipse.Area < minimumAreaFactor * overlapArea || other.Ellipse.Area < minimumAreaFactor * overlapArea ->
other.Removed <- true
| _ ->
()
let _, mean_fg, mean_bg =
let hist = histogramImg imgWithoutNucleus 300
otsu hist
- imgWithoutNucleus.Cmp(-(float mean_bg) * config.Parameters.darkStainLevel + (float mean_fg), CvEnum.CmpType.LessThan)
+ imgWithoutNucleus.Cmp(float mean_fg - config.Parameters.darkStainLevel * float (mean_bg - mean_fg), CvEnum.CmpType.LessThan)
let marker (img: Image<Gray, float32>) (closed: Image<Gray, float32>) (level: float) : Image<Gray, byte> =
let diff = img.Copy()
diff._ThresholdBinary(Gray(0.0), Gray(255.))
diff.Convert<Gray, byte>()
+ // Nucleus.
let nucleusMarker = marker img imgWithoutNucleus (1. / config.Parameters.infectionSensitivity)
+ // Cytoplasm.
let imgWithoutParasite = img.CopyBlank()
let kernelSize =
- let size = roundInt (config.RBCRadius.Pixel / 5.f)
+ let size = roundInt config.RBCRadius.CytoplasmSize
if size % 2 = 0 then size + 1 else size
use kernel =
if kernelSize <= 3
// To match with previously manually altered RBC.
let manuallyAlteredPreviousRBCS = sourceImage.rbcs |> List.filter (fun rbc -> rbc.setManually)
let tolerance = (float sourceImage.config.RBCRadius.Pixel) * 0.5 // +/-.
- let getPreviousRBC (center: Point) : RBC option =
+ let getPreviousManuallyAlteredRBC (center: Point) : RBC option =
manuallyAlteredPreviousRBCS |> List.tryFind (fun rbc -> rbc.center.X > center.X - tolerance && rbc.center.X < center.X + tolerance &&
rbc.center.Y > center.Y - tolerance && rbc.center.Y < center.Y + tolerance)
|> List.mapi (fun i cell ->
let center = Point(float cell.center.X, float cell.center.Y)
let infected, setManually =
- match getPreviousRBC center with
- | Some rbc -> rbc.infected, true
- | _ -> cell.cellClass = ParasitemiaCore.Types.InfectedRBC, false
+ let infected = cell.cellClass = ParasitemiaCore.Types.InfectedRBC
+ match getPreviousManuallyAlteredRBC center with
+ | Some rbc when rbc.infected <> infected -> rbc.infected, true // If it has been previously manually changed and now match the result, the manually flag is removed.
+ | _ -> infected, false
{ num = i + 1
infected = infected