5 open System.Collections.Generic
12 // Normalize image values between 0uy and 255uy.
13 let normalizeAndConvert (img
: Image<Gray, float32
>) : Image<Gray, byte
> =
14 let min = ref [| 0.0
|]
15 let minLocation = ref <| [| Point() |]
16 let max = ref [| 0.0
|]
17 let maxLocation = ref <| [| Point() |]
18 img
.MinMax(min, max, minLocation, maxLocation)
19 ((img
- (!min).[0]) / ((!max).[0] - (!min).[0]) * 255.0).Convert<Gray, byte
>()
21 let gaussianFilter (img
: Image<'TColor, 'TDepth>) (standardDeviation
: float) : Image<'TColor, 'TDepth> =
22 let size = 2 * int (ceil
(4.0 * standardDeviation
)) + 1
23 img
.SmoothGaussian(size, size, standardDeviation
, standardDeviation
)
25 // Zhang and Suen algorithm.
26 // Modify 'mat' in place.
27 let thin (mat
: Matrix<byte
>) =
30 let mutable data1 = mat
.Data
31 let mutable data2 = Array2D.copy
data1
33 let mutable pixelChanged = true
34 let mutable oddIteration = true
42 let p2 = if i
= 0 then 0uy else data1.[i
-1, j
]
43 let p3 = if i
= 0 || j = w-1 then 0uy else data1.[i
-1, j+1]
44 let p4 = if j = w-1 then 0uy else data1.[i
, j+1]
45 let p5 = if i
= h-1 || j = w-1 then 0uy else data1.[i
+1, j+1]
46 let p6 = if i
= h-1 then 0uy else data1.[i
+1, j]
47 let p7 = if i
= h-1 || j = 0 then 0uy else data1.[i
+1, j-1]
48 let p8 = if j = 0 then 0uy else data1.[i
, j-1]
49 let p9 = if i
= 0 || j = 0 then 0uy else data1.[i
-1, j-1]
51 let sumNeighbors = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9
52 if sumNeighbors >= 2uy && sumNeighbors <= 6uy &&
53 (if p2 = 0uy && p3 = 1uy then 1 else 0) +
54 (if p3 = 0uy && p4 = 1uy then 1 else 0) +
55 (if p4 = 0uy && p5 = 1uy then 1 else 0) +
56 (if p5 = 0uy && p6 = 1uy then 1 else 0) +
57 (if p6 = 0uy && p7 = 1uy then 1 else 0) +
58 (if p7 = 0uy && p8 = 1uy then 1 else 0) +
59 (if p8 = 0uy && p9 = 1uy then 1 else 0) +
60 (if p9 = 0uy && p2 = 1uy then 1 else 0) = 1 &&
62 then p2 * p4 * p6 = 0uy && p4 * p6 * p8 = 0uy
63 else p2 * p4 * p8 = 0uy && p2 * p6 * p8 = 0uy
70 oddIteration <- not
oddIteration
76 let pop (l
: List<'a>) : 'a
=
77 let n = l
.[l
.Count - 1]
78 l
.RemoveAt(l
.Count - 1)
81 // Remove all 8-connected pixels with an area equal or greater than 'areaSize'.
82 // Modify 'mat' in place.
83 let removeArea (mat
: Matrix<byte
>) (areaSize
: int) =
94 let mat' = new Matrix<byte>(mat.Size)
100 let data' = mat'.Data
104 if data'.[i, j] = 1uy
106 let neighborhood = List<(int*int)>()
107 let neighborsToCheck = List<(int*int)>()
108 neighborsToCheck.Add((i, j))
111 while neighborsToCheck.Count > 0 do
112 let (ci
, cj
) = pop neighborsToCheck
113 neighborhood.Add((ci
, cj
))
114 for (ni
, nj
) in neighbors do
117 if pi >= 0 && pi < h && pj >= 0 && pj < w && data'.[pi, pj] = 1uy
119 neighborsToCheck.Add((pi, pj))
120 data'.[pi, pj] <- 0uy
121 if neighborhood.Count <= areaSize
123 for (ni
, nj
) in neighborhood do
126 let connectedComponents (img
: Image<Gray, byte
>) (startPoints
: List<Point>) : List<Point> =
130 let pointChecked = HashSet<Point>()
131 let pointToCheck = List<Point>(startPoints
);
135 while pointToCheck.Count > 0 do
136 let next = pop pointToCheck
137 pointChecked.Add(next) |> ignore
140 if ny
<> 0 && nx
<> 0
142 let p = Point(next.X + nx
, next.Y + ny
)
143 if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h && data.[p.Y, p.X, 0] > 0uy && not
(pointChecked.Contains p)
147 List<Point>(pointChecked)
150 let saveImg (img
: Image<'TColor, 'TDepth>) (filepath
: string) =
154 let saveMat (mat: Matrix<'TDepth>) (filepath: string) =
155 use img = new Image<Gray, 'TDeph>(mat.Size)
159 let drawLine (img: Image<'TColor, 'TDepth>) (color
: 'TColor) (x0: int) (y0: int) (x1: int) (y1: int) (thickness: int) =
160 img.Draw(LineSegment2D(Point(x0, y0), Point(x1, y1)), color, thickness);
162 let drawLineF (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0
: float) (y0
: float) (x1
: float) (y1
: float) (thickness
: int) =
163 img.Draw(LineSegment2DF(PointF(float32 x0
, float32 y0
), PointF(float32 x1
, float32 y1
)), color
, thickness
, CvEnum.LineType.AntiAlias);
165 let drawEllipse (img: Image<'TColor, 'TDepth>) (e
: Types.Ellipse) (color
: 'TColor) (alpha: float) =
169 img.Draw(Ellipse(PointF(float32 e.Cx, float32 e.Cy), SizeF(2. * e.B |> float32, 2. * e.A |> float32), float32 <| e.Alpha / Math.PI * 180.), color, 1, CvEnum.LineType.AntiAlias)
171 let windowPosX = e.Cx - e.A - 5.0
172 let gapX = windowPosX - (float (int windowPosX))
174 let windowPosY = e.Cy - e.A - 5.0
175 let gapY = windowPosY - (float (int windowPosY))
177 let roi = Rectangle(int windowPosX, int windowPosY, 2. * (e.A + 5.0) |> int, 2.* (e.A + 5.0) |> int)
180 if roi = img.ROI // We do not display ellipses touching the edges (FIXME)
182 use i = new Image<'TColor, 'TDepth>(img.ROI.Size)
183 i.Draw(Ellipse(PointF(float32 <| (e.A + 5. + gapX) , float32 <| (e.A + 5. + gapY)), SizeF(2. * e.B |> float32, 2. * e.A |> float32), float32 <| e.Alpha / Math.PI * 180.), color, 1, CvEnum.LineType.AntiAlias)
184 CvInvoke.AddWeighted(img, 1.0, i, alpha, 0.0, img)
185 img.ROI <- Rectangle.Empty
188 let drawEllipses (img: Image<'TColor, 'TDepth>) (ellipses: Types.Ellipse list) (color: 'TColor) (alpha
: float) =
189 List.iter
(fun e -> drawEllipse img e color alpha
) ellipses
192 let rngCell = System.Random()
193 let drawCell (img: Image<Bgr, byte
>) (drawCellContent
: bool) (c
: Types.Cell) =
196 let colorB = rngCell.Next(20, 70)
197 let colorG = rngCell.Next(20, 70)
198 let colorR = rngCell.Next(20, 70)
200 for y
in 0 .. c
.elements
.Height - 1 do
201 for x
in 0 .. c
.elements
.Width - 1 do
202 if c
.elements
.[y
, x
] > 0uy
204 let dx, dy
= c
.center
.X - c
.elements
.Width / 2, c
.center
.Y - c
.elements
.Height / 2
205 let b = img.Data.[y
+ dy
, x
+ dx, 0] |> int
206 let g = img.Data.[y
+ dy
, x
+ dx, 1] |> int
207 let r = img.Data.[y
+ dy
, x
+ dx, 2] |> int
208 img.Data.[y
+ dy
, x
+ dx, 0] <- if b + colorB > 255 then 255uy else byte (b + colorB)
209 img.Data.[y
+ dy
, x
+ dx, 1] <- if g + colorG > 255 then 255uy else byte (g + colorG)
210 img.Data.[y
+ dy
, x
+ dx, 2] <- if r + colorR > 255 then 255uy else byte (r + colorR)
212 let crossColor, crossColor2
=
213 match c
.cellClass
with
214 | Types.HealthyRBC -> Bgr(255., 0., 0.), Bgr(255., 255., 255.)
215 | Types.InfectedRBC -> Bgr(0., 0., 255.), Bgr(120., 120., 255.)
216 | Types.Peculiar -> Bgr(0., 0., 0.), Bgr(80., 80., 80.)
218 drawLine img crossColor2
(c
.center
.X - 3) c
.center
.Y (c
.center
.X + 3) c
.center
.Y 2
219 drawLine img crossColor2 c
.center
.X (c
.center
.Y - 3) c
.center
.X (c
.center
.Y + 3) 2
221 drawLine img crossColor (c
.center
.X - 3) c
.center
.Y (c
.center
.X + 3) c
.center
.Y 1
222 drawLine img crossColor c
.center
.X (c
.center
.Y - 3) c
.center
.X (c
.center
.Y + 3) 1
225 let drawCells (img: Image<Bgr, byte>) (drawCellContent
: bool) (cells
: Types.Cell list) =
226 List.iter
(fun c
-> drawCell img drawCellContent
c) cells