module ParasitemiaCore.ParasitesMarker open System.Drawing open System.Linq open Emgu.CV open Emgu.CV.Structure open Utils open Histogram open Otsu open Morpho open ImgTools type Result = { darkStain : Image // Colored pixel, it's independent of the size of the areas. It corresponds to white cells, schizontes, gametocytes, throphozoites. nucleus : Image // Parasite nucleus. It may contain some debris. It shouldn't contain thrombocytes or larger elements. parasite : Image // The whole parasites. } let find (img : Image) (config : Config.Config) : Result * Image * Image = 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 = histogramImg imgWithoutNucleus 300 otsu hist imgWithoutNucleus.Cmp (float mean_fg - config.Parameters.darkStainLevel * float (mean_bg - mean_fg), CvEnum.CmpType.LessThan) let marker (img : Image) (closed : Image) (level : float) : Image = let diff = img.Copy () diff._Mul level CvInvoke.Subtract (closed, diff, diff) diff._ThresholdBinary (Gray 0.0, Gray 255.) diff.Convert () // Nucleus. let nucleusMarker = marker img imgWithoutNucleus (1. / config.Parameters.infectionSensitivity) // Cytoplasm. let imgWithoutParasite = img.CopyBlank () let kernelSize = let size = roundInt config.RBCRadius.CytoplasmSize if size % 2 = 0 then size + 1 else size use kernel = if kernelSize <= 3 then 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 (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 parasite = parasiteMarker }, imgWithoutParasite, imgWithoutNucleus