let infection = parasites.infection.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.RBCMax) * config.Parameters.scale
- KdTree.maxX = e.Cx + (e.A + config.RBCMax) * config.Parameters.scale
- KdTree.minY = e.Cy - (e.A + config.RBCMax) * config.Parameters.scale
- KdTree.maxY = e.Cy + (e.A + config.RBCMax) * config.Parameters.scale }
+ let searchRegion (e: Ellipse) = { KdTree.minX = e.Cx - (e.A + config.RBCMaxRadius) * config.Parameters.scale
+ KdTree.maxX = e.Cx + (e.A + config.RBCMaxRadius) * config.Parameters.scale
+ KdTree.minY = e.Cy - (e.A + config.RBCMaxRadius) * config.Parameters.scale
+ KdTree.maxY = e.Cy + (e.A + config.RBCMaxRadius) * config.Parameters.scale }
// The minimum window to contain a given ellipse.
let ellipseWindow (e: Ellipse) =
let c = PointD(e.Cx, e.Cy)
for d1 in lines do
let d2 = Utils.lineFromTwoPoints c p
- let p' = Utils.pointFromTwoLines d1 d2
- yield sign (c.X - p.X) <> sign (c.X - p'.X) || Utils.squaredDistanceTwoPoints c p' > Utils.squaredDistanceTwoPoints c p // 'false' -> the point is owned by another ellipse.
+ if d2.Valid
+ then
+ let p' = Utils.pointFromTwoLines d1 d2
+ yield sign (c.X - p.X) <> sign (c.X - p'.X) || Utils.squaredDistanceTwoPoints c p' > Utils.squaredDistanceTwoPoints c p // 'false' -> the point is owned by another ellipse.
+ else
+ yield true
} |> Seq.forall id
ellipsesWithNeigbors
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
- elif infectedPixels.Count > config.Parameters.infectionPixelsRequired
+ elif infectedPixels.Count > config.Parameters.parasitePixelsRequired
then
let infectionToRemove = ImgTools.connectedComponents parasites.stain infectedPixels
for p in infectionToRemove do
infectionArea: float // Factor of a RBC area. 0.5 means the half of RBC area.
infectionLevel: float // [0, 1]
- infectionPixelsRequired: int
+ parasitePixelsRequired: int
maxDarkStainRatio: float
type Config (param: Parameters) =
member this.Parameters = param
member val Debug = DebugOff with get, set
- member val RBCSize = 30. with get, set
+ member val RBCRadius = 30. with get, set
- member this.RBCMin = this.RBCSize + param.minRbcRadius * this.RBCSize
- member this.RBCMax = this.RBCSize + param.maxRbcRadius * this.RBCSize
+ 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.RBCSize ** 2.0
+ member this.RBCMinArea = param.minimumCellArea * Math.PI * this.RBCRadius ** 2.0
- member this.ParasiteArea = param.infectionArea * Math.PI * this.RBCSize ** 2.0
- member this.StainArea = param.stainArea * Math.PI * this.RBCSize ** 2.0
+ member this.InfectionArea = param.infectionArea * Math.PI * this.RBCRadius ** 2.0
+ member this.StainArea = param.stainArea * Math.PI * this.RBCRadius ** 2.0
(yDir: Image<Gray, float>)
(config: Config) : MatchingEllipses =
- let r1, r2 = config.Parameters.scale * config.RBCMin, config.Parameters.scale * config.RBCMax // FIXME: scale factor should be applied in Config!?
+ let r1, r2 = config.Parameters.scale * config.RBCMinRadius, config.Parameters.scale * config.RBCMaxRadius // FIXME: scale factor should be applied in Config!?
let windowSize = roundInt (config.Parameters.factorWindowSize * r2)
let factorNbPick = config.Parameters.factorNbPick
logTime "areaOpen" (fun () -> ImgTools.areaOpen filteredGreen 2000)
- config.RBCSize <- Granulometry.findRadius filteredGreen (10, 100) 0.5 |> float
+ config.RBCRadius <- Granulometry.findRadius filteredGreen (10, 100) 0.5 |> float
let filteredGreenFloat = filteredGreen.Convert<Gray, float32>() // Is it neccessary?
let kmediansResults = logTime "Finding foreground (k-medians)" (fun () -> KMedians.kmedians filteredGreenFloat 1.0)
- let parasites, filteredGreenWhitoutParasites, filteredGreenWhitoutStain = ParasitesMarker2.find filteredGreen filteredGreenFloat kmediansResults config
- let filteredGreenWhitoutParasitesFloat = filteredGreenWhitoutParasites.Convert<Gray, float32>()
+ let parasites, filteredGreenWhitoutInfection, filteredGreenWhitoutStain = ParasitesMarker2.find filteredGreen filteredGreenFloat kmediansResults config
+ let filteredGreenWhitoutInfectionFloat = filteredGreenWhitoutInfection.Convert<Gray, float32>()
+ let filteredGreenWhitoutStainFloat = filteredGreenWhitoutStain.Convert<Gray, float32>()
use sobelKernel =
new ConvolutionKernelF(array2D [[ 1.0f; 0.0f; -1.0f ]
[ 2.0f; 0.0f; -2.0f ]
[ 1.0f; 0.0f; -1.0f ]], Point(0, 0))
- use xEdges = filteredGreenWhitoutParasitesFloat.Convolution(sobelKernel).Convert<Gray, float>()
- use yEdges = filteredGreenWhitoutParasitesFloat.Convolution(sobelKernel.Transpose()).Convert<Gray, float>()
+ use xEdges = filteredGreenWhitoutStainFloat.Convolution(sobelKernel).Convert<Gray, float>()
+ use yEdges = filteredGreenWhitoutStainFloat.Convolution(sobelKernel.Transpose()).Convert<Gray, float>()
let xEdgesData = xEdges.Data
let yEdgesData = yEdges.Data
let matchingEllipses = Ellipse.find edges xEdges yEdges config
matchingEllipses.Ellipses, matchingEllipses.PrunedEllipses )
- let cells = logTime "Classifier" (fun () -> Classifier.findCells ellipses parasites filteredGreenWhitoutParasites config)
+ let cells = logTime "Classifier" (fun () -> Classifier.findCells ellipses parasites filteredGreenWhitoutStain config)
// Output pictures if debug flag is set.
match config.Debug with
saveImg filteredGreenMaxima (buildFileName " - filtered - maxima.png")
saveImg filteredGreen (buildFileName " - filtered.png")
- saveImg filteredGreenWhitoutParasites (buildFileName " - filtered closed.png")
+ saveImg filteredGreenWhitoutStain (buildFileName " - filtered closed.png")
(*saveImg parasitesMarker (buildFileName " - parasites (area closing).png")
saveImg stainMarker (buildFileName " - stain (area closing).png")*)
type Result = {
darkStain: Image<Gray, byte>
- stain: Image<Gray, byte>
- infection: Image<Gray, byte> }
+ infection: Image<Gray, byte>
+ stain: Image<Gray, byte> }
// Create three binary markers :
// * 'Dark stain' corresponds to the colored pixel, it's independent of the size of the areas.
let maxLocation = ref <| [| Point() |]
diff.MinMax(min, max, minLocation, maxLocation)
- diff._ThresholdBinary((!max).[0] * threshold |> Gray, Gray(255.))
+ let valueThreshold = if (!max).[0] * threshold < 0.1 * (median_bg - median_fg) then 0.1 * (median_bg - median_fg) else (!max).[0] * threshold
+
+ diff._ThresholdBinary(Gray(valueThreshold), Gray(255.))
diff, closed
- let parasitesMarker, filteredGreenWithoutParasites = marker (int config.ParasiteArea) config.Parameters.infectionLevel
+ let infectionMarker, filteredGreenWithoutInfection = marker (int config.InfectionArea) config.Parameters.infectionLevel
let stainMarker, filteredGreenWithoutStain = marker (int config.StainArea) config.Parameters.stainLevel
{ darkStain = darkStain
- stain = parasitesMarker
- infection = stainMarker },
- filteredGreenWithoutParasites,
+ infection = infectionMarker
+ stain = stainMarker },
+ filteredGreenWithoutInfection,
filteredGreenWithoutStain
darkStainLevel = 0.4 // Lower -> more sensitive.
- stainArea = 0.02
- stainLevel = 0.2
-
- infectionArea = 0.06
+ infectionArea = 0.015
infectionLevel = 0.2
- infectionPixelsRequired = 1
+ parasitePixelsRequired = 1
+
+ stainArea = 0.06
+ stainLevel = 0.2
maxDarkStainRatio = 0.1
type Line (a: float, b: float) =
member this.A = a
member this.B = b
+ member this.Valid = not (Double.IsInfinity this.A)
[<Struct>]
type PointD (x: float, y: float) =