X-Git-Url: http://git.euphorik.ch/?p=master-thesis.git;a=blobdiff_plain;f=Parasitemia%2FParasitemiaCore%2FImgTools.fs;h=cc15af7f43df9c17744b8ce381570f07a23a29a3;hp=abc8714aa7e9ed2601332e506409c9046e6def2a;hb=170abb893b9e6babbf93276e39e8c984cedbc68f;hpb=aee6cf83f81c6378c458f75bbfaf7c9fa5521135 diff --git a/Parasitemia/ParasitemiaCore/ImgTools.fs b/Parasitemia/ParasitemiaCore/ImgTools.fs index abc8714..cc15af7 100644 --- a/Parasitemia/ParasitemiaCore/ImgTools.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools.fs @@ -10,6 +10,7 @@ open Emgu.CV.Structure open Heap open Const +open Types open Utils // Normalize image values between 0uy and 255uy. @@ -145,7 +146,10 @@ let otsu (hist: Histogram) : float32 * float32 * float32 = toFloat level, toFloat mean1, toFloat mean2 -let suppressMConnections (img: Matrix) = +/// +/// Remove M-adjacent pixels. It may be used after thinning. +/// +let suppressMAdjacency (img: Matrix) = let w = img.Width let h = img.Height for i in 1 .. h - 2 do @@ -159,6 +163,11 @@ let suppressMConnections (img: Matrix) = then img.[i, j] <- 0uy +/// +/// Find edges of an image by using the Canny approach. +/// The thresholds are automatically defined with otsu on gradient magnitudes. +/// +/// let findEdges (img: Image) : Matrix * Image * Image = let w = img.Width let h = img.Height @@ -187,7 +196,7 @@ let findEdges (img: Image) : Matrix * Image use magnitudes = new Matrix(xGradient.Size) use angles = new Matrix(xGradient.Size) - CvInvoke.CartToPolar(xGradient, yGradient, magnitudes, angles) // Compute the magnitudes (without angles). + CvInvoke.CartToPolar(xGradient, yGradient, magnitudes, angles) // Compute the magnitudes and angles. let thresholdHigh, thresholdLow = let sensibilityHigh = 0.1f @@ -205,8 +214,6 @@ let findEdges (img: Image) : Matrix * Image let xGradientData = xGradient.Data let yGradientData = yGradient.Data - let PI = float32 Math.PI - for i in 0 .. h - 1 do nmsData.[i, 0] <- 0uy nmsData.[i, w - 1] <- 0uy @@ -282,8 +289,6 @@ let gaussianFilter (img : Image<'TColor, 'TDepth>) (standardDeviation : float) : let size = 2 * int (ceil (4.0 * standardDeviation)) + 1 img.SmoothGaussian(size, size, standardDeviation, standardDeviation) -type Points = HashSet - let drawPoints (img: Image) (points: Points) (intensity: 'TDepth) = for p in points do img.Data.[p.Y, p.X, 0] <- intensity @@ -601,12 +606,73 @@ let private areaOperation (img: Image) (area: int) (op: AreaOperatio | _ -> () () +/// +/// Area opening on byte image. +/// let areaOpen (img: Image) (area: int) = areaOperation img area AreaOperation.Opening +/// +/// Area closing on byte image. +/// let areaClose (img: Image) (area: int) = areaOperation img area AreaOperation.Closing +// A simpler algorithm than 'areaOpen' on byte image but slower. +let areaOpen2 (img: Image) (area: int) = + let w = img.Width + let h = img.Height + let imgData = img.Data + let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |] + + let histogram = Array.zeroCreate 256 + for i in 0 .. h - 1 do + for j in 0 .. w - 1 do + let v = imgData.[i, j, 0] |> int + histogram.[v] <- histogram.[v] + 1 + + let flooded : bool[,] = Array2D.zeroCreate h w + + let pointsChecked = HashSet() + let pointsToCheck = Stack() + + for level in 255 .. -1 .. 0 do + let mutable n = histogram.[level] + if n > 0 + then + for i in 0 .. h - 1 do + for j in 0 .. w - 1 do + if not flooded.[i, j] && imgData.[i, j, 0] = byte level + then + let mutable maxNeighborValue = 0uy + pointsChecked.Clear() + pointsToCheck.Clear() + pointsToCheck.Push(Point(j, i)) + + while pointsToCheck.Count > 0 do + let next = pointsToCheck.Pop() + pointsChecked.Add(next) |> ignore + flooded.[next.Y, next.X] <- true + + for nx, ny in se do + let p = Point(next.X + nx, next.Y + ny) + if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h + then + let v = imgData.[p.Y, p.X, 0] + if v = byte level + then + if not (pointsChecked.Contains(p)) + then + pointsToCheck.Push(p) + elif v > maxNeighborValue + then + maxNeighborValue <- v + + if int maxNeighborValue < level && pointsChecked.Count <= area + then + for p in pointsChecked do + imgData.[p.Y, p.X, 0] <- maxNeighborValue + [] type Island (cmp: IComparer) = member val Shore = Heap.Heap(cmp) with get @@ -720,75 +786,36 @@ let private areaOperationF (img: Image) (areas: (int * 'a) list) | _ -> () () +/// +/// Area opening on float image. +/// let areaOpenF (img: Image) (area: int) = areaOperationF img [ area, () ] None AreaOperation.Opening +/// +/// Area closing on float image. +/// let areaCloseF (img: Image) (area: int) = areaOperationF img [ area, () ] None AreaOperation.Closing +/// +/// Area closing on float image with different areas. Given areas must be sorted increasingly. +/// For each area the function 'f' is called with the associated area value of type 'a and the volume difference +/// Between the previous and the current closing. +/// let areaOpenFWithFun (img: Image) (areas: (int * 'a) list) (f: 'a -> float32 -> unit) = areaOperationF img areas (Some f) AreaOperation.Opening +/// +/// Same as 'areaOpenFWithFun' for closing operation. +/// let areaCloseFWithFun (img: Image) (areas: (int * 'a) list) (f: 'a -> float32 -> unit) = areaOperationF img areas (Some f) AreaOperation.Closing -// A simpler algorithm than 'areaOpen' but slower. -let areaOpen2 (img: Image) (area: int) = - let w = img.Width - let h = img.Height - let imgData = img.Data - let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |] - - let histogram = Array.zeroCreate 256 - for i in 0 .. h - 1 do - for j in 0 .. w - 1 do - let v = imgData.[i, j, 0] |> int - histogram.[v] <- histogram.[v] + 1 - - let flooded : bool[,] = Array2D.zeroCreate h w - - let pointsChecked = HashSet() - let pointsToCheck = Stack() - - for level in 255 .. -1 .. 0 do - let mutable n = histogram.[level] - if n > 0 - then - for i in 0 .. h - 1 do - for j in 0 .. w - 1 do - if not flooded.[i, j] && imgData.[i, j, 0] = byte level - then - let mutable maxNeighborValue = 0uy - pointsChecked.Clear() - pointsToCheck.Clear() - pointsToCheck.Push(Point(j, i)) - - while pointsToCheck.Count > 0 do - let next = pointsToCheck.Pop() - pointsChecked.Add(next) |> ignore - flooded.[next.Y, next.X] <- true - - for nx, ny in se do - let p = Point(next.X + nx, next.Y + ny) - if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h - then - let v = imgData.[p.Y, p.X, 0] - if v = byte level - then - if not (pointsChecked.Contains(p)) - then - pointsToCheck.Push(p) - elif v > maxNeighborValue - then - maxNeighborValue <- v - - if int maxNeighborValue < level && pointsChecked.Count <= area - then - for p in pointsChecked do - imgData.[p.Y, p.X, 0] <- maxNeighborValue - -// Zhang and Suen algorithm. -// Modify 'mat' in place. +/// +/// Zhang and Suen thinning algorithm. +/// Modify 'mat' in place. +/// let thin (mat: Matrix) = let w = mat.Width let h = mat.Height @@ -837,8 +864,10 @@ let thin (mat: Matrix) = data1 <- data2 data2 <- tmp -// Remove all 8-connected pixels with an area equal or greater than 'areaSize'. -// Modify 'mat' in place. +/// +/// Remove all 8-connected pixels with an area equal or greater than 'areaSize'. +/// Modify 'mat' in place. +/// let removeArea (mat: Matrix) (areaSize: int) = let neighbors = [| (-1, 0) // p2 @@ -882,7 +911,7 @@ let removeArea (mat: Matrix) (areaSize: int) = for n in neighborhood do data.[n.Y, n.X] <- 0uy -let connectedComponents (img: Image) (startPoints: List) : List = +let connectedComponents (img: Image) (startPoints: List) : Points = let w = img.Width let h = img.Height @@ -903,7 +932,7 @@ let connectedComponents (img: Image) (startPoints: List) : Li then pointToCheck.Push(p) - List(pointChecked) + pointChecked let drawLine (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0: int) (y0: int) (x1: int) (y1: int) (thickness: int) = img.Draw(LineSegment2D(Point(x0, y0), Point(x1, y1)), color, thickness); @@ -911,10 +940,10 @@ let drawLine (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0: int) (y0: int) let drawLineF (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0: float) (y0: float) (x1: float) (y1: float) (thickness: int) = img.Draw(LineSegment2DF(PointF(float32 x0, float32 y0), PointF(float32 x1, float32 y1)), color, thickness, CvEnum.LineType.AntiAlias); -let drawEllipse (img: Image<'TColor, 'TDepth>) (e: Types.Ellipse) (color: 'TColor) (alpha: float) = +let drawEllipse (img: Image<'TColor, 'TDepth>) (e: Ellipse) (color: 'TColor) (alpha: float) = if alpha >= 1.0 then - img.Draw(Ellipse(PointF(float32 e.Cx, float32 e.Cy), SizeF(2.f * e.B, 2.f * e.A), e.Alpha / PI * 180.f), color, 1, CvEnum.LineType.AntiAlias) + img.Draw(Emgu.CV.Structure.Ellipse(PointF(float32 e.Cx, float32 e.Cy), SizeF(2.f * e.B, 2.f * e.A), e.Alpha / PI * 180.f), color, 1, CvEnum.LineType.AntiAlias) else let windowPosX = e.Cx - e.A - 5.f let gapX = windowPosX - (float32 (int windowPosX)) @@ -928,15 +957,15 @@ let drawEllipse (img: Image<'TColor, 'TDepth>) (e: Types.Ellipse) (color: 'TColo if roi = img.ROI // We do not display ellipses touching the edges (FIXME) then use i = new Image<'TColor, 'TDepth>(img.ROI.Size) - i.Draw(Ellipse(PointF(float32 <| (e.A + 5.f + gapX) , float32 <| (e.A + 5.f + gapY)), SizeF(2.f * e.B, 2.f * e.A), e.Alpha / PI * 180.f), color, 1, CvEnum.LineType.AntiAlias) + i.Draw(Emgu.CV.Structure.Ellipse(PointF(float32 <| (e.A + 5.f + gapX) , float32 <| (e.A + 5.f + gapY)), SizeF(2.f * e.B, 2.f * e.A), e.Alpha / PI * 180.f), color, 1, CvEnum.LineType.AntiAlias) CvInvoke.AddWeighted(img, 1.0, i, alpha, 0.0, img) img.ROI <- Rectangle.Empty -let drawEllipses (img: Image<'TColor, 'TDepth>) (ellipses: Types.Ellipse list) (color: 'TColor) (alpha: float) = +let drawEllipses (img: Image<'TColor, 'TDepth>) (ellipses: Ellipse list) (color: 'TColor) (alpha: float) = List.iter (fun e -> drawEllipse img e color alpha) ellipses let rngCell = System.Random() -let drawCell (img: Image) (drawCellContent: bool) (c: Types.Cell) = +let drawCell (img: Image) (drawCellContent: bool) (c: Cell) = if drawCellContent then let colorB = rngCell.Next(20, 70) @@ -957,9 +986,9 @@ let drawCell (img: Image) (drawCellContent: bool) (c: Types.Cell) = let crossColor, crossColor2 = match c.cellClass with - | Types.HealthyRBC -> Bgr(255., 0., 0.), Bgr(255., 255., 255.) - | Types.InfectedRBC -> Bgr(0., 0., 255.), Bgr(120., 120., 255.) - | Types.Peculiar -> Bgr(0., 0., 0.), Bgr(80., 80., 80.) + | HealthyRBC -> Bgr(255., 0., 0.), Bgr(255., 255., 255.) + | InfectedRBC -> Bgr(0., 0., 255.), Bgr(120., 120., 255.) + | Peculiar -> Bgr(0., 0., 0.), Bgr(80., 80., 80.) drawLine img crossColor2 (c.center.X - 3) c.center.Y (c.center.X + 3) c.center.Y 2 drawLine img crossColor2 c.center.X (c.center.Y - 3) c.center.X (c.center.Y + 3) 2 @@ -968,5 +997,5 @@ let drawCell (img: Image) (drawCellContent: bool) (c: Types.Cell) = drawLine img crossColor c.center.X (c.center.Y - 3) c.center.X (c.center.Y + 3) 1 -let drawCells (img: Image) (drawCellContent: bool) (cells: Types.Cell list) = +let drawCells (img: Image) (drawCellContent: bool) (cells: Cell list) = List.iter (fun c -> drawCell img drawCellContent c) cells \ No newline at end of file