Improve the thinning process performance.
[master-thesis.git] / Parasitemia / Parasitemia / ImgTools.fs
index cc09405..f7e5a4a 100644 (file)
@@ -9,82 +9,79 @@ open Emgu.CV.Structure
 
 open Utils
 
+// Normalize image values between 0uy and 255uy.
+let normalizeAndConvert (img: Image<Gray, float32>) : Image<Gray, byte> =
+    let min = ref [| 0.0 |]
+    let minLocation = ref <| [| Point() |]
+    let max = ref [| 0.0 |]
+    let maxLocation = ref <| [| Point() |]
+    img.MinMax(min, max, minLocation, maxLocation)
+    ((img - (!min).[0]) / ((!max).[0] - (!min).[0]) * 255.0).Convert<Gray, byte>()
+
 let gaussianFilter (img : Image<'TColor, 'TDepth>) (standardDeviation : float) : Image<'TColor, 'TDepth> =
     let size = 2 * int (ceil (4.0 * standardDeviation)) + 1
     img.SmoothGaussian(size, size, standardDeviation, standardDeviation)
 
-// Zhang and Suen algorithm. 
+// Zhang and Suen algorithm.
 // Modify 'mat' in place.
-let thin (mat: Matrix<byte>) =    
-    let neighbors = [| 
-        (-1,  0) // p2
-        (-1,  1) // p3
-        ( 0,  1) // p4
-        ( 1,  1) // p5
-        ( 1,  0) // p6
-        ( 1, -1) // p7
-        ( 0, -1) // p8
-        (-1, -1) |] // p9
-
+let thin (mat: Matrix<byte>) =
     let w = mat.Width
     let h = mat.Height
     let mutable data1 = mat.Data
-    let mutable data2 = Array2D.zeroCreate<byte> h w
-
-    // Return the list of neighbor values.
-    let neighborsValues (p1i, p1j) = 
-        Array.map (fun (ni, nj) -> 
-            let pi = p1i + ni
-            let pj = p1j + nj
-            if pi < 0 || pi >= h || pj < 0 || pj >= w then 0uy else data1.[pi, pj]
-            ) neighbors
-
-    // Return the number of 01 pattern in 'values' in a circular way.
-    let pattern01 (values: byte[]) = 
-        let mutable nb = 0
-        let mutable lastValue = 255uy
-        for v in values do
-            if lastValue = 0uy && v = 1uy
-            then
-                nb <- nb + 1
-            lastValue <- v
-        if lastValue = 0uy && values.[0] = 1uy
-        then
-            nb <- nb + 1
-        nb
-         
+    let mutable data2 = Array2D.copy data1
+
     let mutable pixelChanged = true
     let mutable oddIteration = true
+
     while pixelChanged do
         pixelChanged <- false
         for i in 0..h-1 do
             for j in 0..w-1 do
                 if data1.[i, j] = 1uy
                 then
-                    let values = neighborsValues (i, j)
-                    let s = Array.reduce (+) values
-                    if s >= 2uy && s <= 6uy &&
-                        pattern01 values = 1 &&
-                        (not oddIteration || (values.[0] * values.[2] * values.[4] = 0uy && values.[2] * values.[4] * values.[6] = 0uy)) && // Odd iteration.
-                        (oddIteration || (values.[0] * values.[2] * values.[6] = 0uy && values.[0] * values.[4] * values.[6] = 0uy)) // Even iterations.
+                    let p2 = if i = 0 then 0uy else data1.[i-1, j]
+                    let p3 = if i = 0 || j = w-1 then 0uy else data1.[i-1, j+1]
+                    let p4 = if j = w-1 then 0uy else data1.[i, j+1]
+                    let p5 = if i = h-1 || j = w-1 then 0uy else data1.[i+1, j+1]
+                    let p6 = if i = h-1 then 0uy else data1.[i+1, j]
+                    let p7 = if i = h-1 || j = 0 then 0uy else data1.[i+1, j-1]
+                    let p8 = if j = 0 then 0uy else data1.[i, j-1]
+                    let p9 = if i = 0 || j = 0 then 0uy else data1.[i-1, j-1]
+
+                    let sumNeighbors = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9
+                    if sumNeighbors >= 2uy && sumNeighbors <= 6uy &&
+                        (if p2 = 0uy && p3 = 1uy then 1 else 0) +
+                        (if p3 = 0uy && p4 = 1uy then 1 else 0) +
+                        (if p4 = 0uy && p5 = 1uy then 1 else 0) +
+                        (if p5 = 0uy && p6 = 1uy then 1 else 0) +
+                        (if p6 = 0uy && p7 = 1uy then 1 else 0) +
+                        (if p7 = 0uy && p8 = 1uy then 1 else 0) +
+                        (if p8 = 0uy && p9 = 1uy then 1 else 0) +
+                        (if p9 = 0uy && p2 = 1uy then 1 else 0) = 1 &&
+                        if oddIteration
+                        then p2 * p4 * p6 = 0uy && p4 * p6 * p8 = 0uy
+                        else p2 * p4 * p8 = 0uy && p2 * p6 * p8 = 0uy
                     then
                         data2.[i, j] <- 0uy
                         pixelChanged <- true
-                    else
-                        data2.[i, j] <- 1uy
                 else
                     data2.[i, j] <- 0uy
 
-        oddIteration <- not oddIteration                  
+        oddIteration <- not oddIteration
         let tmp = data1
         data1 <- data2
         data2 <- tmp
-        
+
+
+let pop (l: List<'a>) : 'a =
+    let n = l.[l.Count - 1]
+    l.RemoveAt(l.Count - 1)
+    n
 
 // Remove all 8-connected pixels with an area equal or greater than 'areaSize'.
 // Modify 'mat' in place.
 let removeArea (mat: Matrix<byte>) (areaSize: int) =
-    let neighbors = [| 
+    let neighbors = [|
         (-1,  0) // p2
         (-1,  1) // p3
         ( 0,  1) // p4
@@ -93,29 +90,24 @@ let removeArea (mat: Matrix<byte>) (areaSize: int) =
         ( 1, -1) // p7
         ( 0, -1) // p8
         (-1, -1) |] // p9
-    
+
     let mat' = new Matrix<byte>(mat.Size)
     let w = mat'.Width
     let h = mat'.Height
-    mat.CopyTo(mat')   
-     
+    mat.CopyTo(mat')
+
     let data = mat.Data
     let data' = mat'.Data
 
     for i in 0..h-1 do
         for j in 0..w-1 do
-            if data'.[i, j] = 1uy 
+            if data'.[i, j] = 1uy
             then
                 let neighborhood = List<(int*int)>()
                 let neighborsToCheck = List<(int*int)>()
-                neighborsToCheck.Add((i, j))          
+                neighborsToCheck.Add((i, j))
                 data'.[i, j] <- 0uy
 
-                let pop (l: List<'a>) : 'a = 
-                    let n = l.[l.Count - 1]
-                    l.RemoveAt(l.Count - 1)
-                    n
-
                 while neighborsToCheck.Count > 0 do
                     let (ci, cj) = pop neighborsToCheck
                     neighborhood.Add((ci, cj))
@@ -131,47 +123,103 @@ let removeArea (mat: Matrix<byte>) (areaSize: int) =
                     for (ni, nj) in neighborhood do
                         data.[ni, nj] <- 0uy
 
+let connectedComponents (img: Image<Gray, byte>) (startPoints: List<Point>) : List<Point> =
+    let w = img.Width
+    let h = img.Height
 
-let saveImg (img: Image<'TColor, 'TDepth>) (name: string) = 
-    img.Save("output/" + name)
-    
+    let pointChecked = HashSet<Point>()
+    let pointToCheck = List<Point>(startPoints);
 
-let saveMat (mat: Matrix<'TDepth>) (name: string) = 
+    let data = img.Data
+
+    while pointToCheck.Count > 0 do
+        let next = pop pointToCheck
+        pointChecked.Add(next) |> ignore
+        for ny in -1 .. 1 do
+            for nx in -1 .. 1 do
+                if ny <> 0 && nx <> 0
+                then
+                    let p = Point(next.X + nx, next.Y + ny)
+                    if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h && data.[p.Y, p.X, 0] > 0uy && not (pointChecked.Contains p)
+                    then
+                        pointToCheck.Add(p)
+
+    List<Point>(pointChecked)
+
+
+let saveImg (img: Image<'TColor, 'TDepth>) (filepath: string) =
+    img.Save(filepath)
+
+
+let saveMat (mat: Matrix<'TDepth>) (filepath: string) =
     use img = new Image<Gray, 'TDeph>(mat.Size)
     mat.CopyTo(img)
-    saveImg img name
-    
-(*let drawEllipse (img: Image<'TColor, 'TDepth>) (e: Types.Ellipse) (color: 'TColor) =
-    let e' = Ellipse(PointF(float32 e.cx, float32 e.cy), SizeF(2.0f * float32 e.a, 2.0f * float32 e.b), float32 e.alpha)
-    img.Draw(e', color)*)
+    saveImg img filepath
+
+let drawLine (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0: int) (y0: int) (x1: int) (y1: int) =
+    img.Draw(LineSegment2D(Point(x0, y0), Point(x1, y1)), color, 1);
 
-let drawLine (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0: float) (y0: float) (x1: float) (y1: float) =
+let drawLineF (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0: float) (y0: float) (x1: float) (y1: float) =
     let x0, y0, x1, y1 = roundInt(x0), roundInt(y0), roundInt(x1), roundInt(y1)
+    drawLine img color x0 y0 x1 y1
 
-    img.Draw(LineSegment2D(Point(x0, y0), Point(x1, y1)), color, 1);
+let drawEllipse (img: Image<'TColor, 'TDepth>) (e: Types.Ellipse) (color: 'TColor) =
+    let cosAlpha = cos e.Alpha
+    let sinAlpha = sin e.Alpha
 
-let drawEllipse (img: Image<'TColor, 'TDepth>) (e: Types.Ellipse) (color: 'TColor) =    
-    let cosAlpha = cos e.alpha
-    let sinAlpha = sin e.alpha
-        
     let mutable x0 = 0.0
     let mutable y0 = 0.0
     let mutable first_iteration = true
-        
+
     let n = 40
     let thetaIncrement = 2.0 * Math.PI / (float n)
-        
+
     for theta in 0.0 .. thetaIncrement .. 2.0 * Math.PI do
         let cosTheta = cos theta
         let sinTheta = sin theta
-        let x = e.cx + cosAlpha * e.a * cosTheta - sinAlpha * e.b * sinTheta
-        let y = e.cy + sinAlpha * e.a * cosTheta + cosAlpha * e.b * sinTheta
+        let x = e.Cx + cosAlpha * e.A * cosTheta - sinAlpha * e.B * sinTheta
+        let y = e.Cy + sinAlpha * e.A * cosTheta + cosAlpha * e.B * sinTheta
 
         if not first_iteration
         then
-            drawLine img color x0 y0 x y
+            drawLineF img color x0 y0 x y
         else
             first_iteration <- false
 
         x0 <- x
-        y0 <- y
\ No newline at end of file
+        y0 <- y
+
+let drawEllipses (img: Image<'TColor, 'TDepth>) (ellipses: Types.Ellipse list) (color: 'TColor) =
+    List.iter (fun e -> drawEllipse img e color) ellipses
+
+
+let rngCell =  System.Random()
+let drawCell (img: Image<Bgr, byte>) (drawCellContent: bool) (c: Types.Cell) =
+    if drawCellContent
+    then
+        let colorB = rngCell.Next(20, 70)
+        let colorG = rngCell.Next(20, 70)
+        let colorR = rngCell.Next(20, 70)
+
+        for y in 0 .. c.elements.Height - 1 do
+            for x in 0 .. c.elements.Width - 1 do
+                if c.elements.[y, x] > 0uy
+                then
+                    let dx, dy = c.center.X - c.elements.Width / 2, c.center.Y - c.elements.Height / 2
+                    let b = img.Data.[y + dy, x + dx, 0] |> int
+                    let g = img.Data.[y + dy, x + dx, 1] |> int
+                    let r = img.Data.[y + dy, x + dx, 2] |> int
+                    img.Data.[y + dy, x + dx, 0] <- if b + colorB > 255 then 255uy else byte (b + colorB)
+                    img.Data.[y + dy, x + dx, 1] <- if g + colorG > 255 then 255uy else byte (g + colorG)
+                    img.Data.[y + dy, x + dx, 2] <- if r + colorR > 255 then 255uy else byte (r + colorR)
+
+    let crossColor = match c.cellClass with
+                     | Types.HealthyRBC -> Bgr(255.0, 0.0, 0.0)
+                     | Types.InfectedRBC -> Bgr(0.0, 0.0, 255.0)
+                     | Types.Peculiar -> Bgr(0.0, 0.0, 0.0)
+
+    drawLine img crossColor (c.center.X - 3) c.center.Y (c.center.X + 3) c.center.Y
+    drawLine img crossColor c.center.X (c.center.Y - 3) c.center.X (c.center.Y + 3)
+
+let drawCells (img: Image<Bgr, byte>) (drawCellContent: bool) (cells: Types.Cell list) =
+    List.iter (fun c -> drawCell img drawCellContent c) cells
\ No newline at end of file