b9a31f8a5da2e9e1dd66adaaa30e59c9c741258c
5 open System.Collections.Generic
14 // Normalize image values between 0uy and 255uy.
15 let normalizeAndConvert (img
: Image<Gray, float32
>) : Image<Gray, byte
> =
16 let min = ref [| 0.0
|]
17 let minLocation = ref <| [| Point() |]
18 let max = ref [| 0.0
|]
19 let maxLocation = ref <| [| Point() |]
20 img
.MinMax(min, max, minLocation, maxLocation)
21 ((img
- (!min).[0]) / ((!max).[0] - (!min).[0]) * 255.0).Convert<Gray, byte
>()
24 let saveImg (img
: Image<'TColor, 'TDepth>) (filepath
: string) =
28 let saveMat (mat
: Matrix<'TDepth>) (filepath: string) =
29 use img = new Image<Gray, 'TDeph>(mat
.Size)
34 let suppressMConnections (img: Matrix<byte
>) =
37 for i
in 1 .. h - 2 do
38 for j
in 1 .. w - 2 do
39 if img.[i
, j
] > 0uy && img.Data.[i
+ 1, j
] > 0uy && (img.Data.[i
, j
- 1] > 0uy && img.Data.[i
- 1, j
+ 1] = 0uy || img.Data.[i
, j
+ 1] > 0uy && img.Data.[i
- 1, j
- 1] = 0uy)
42 for i
in 1 .. h - 2 do
43 for j
in 1 .. w - 2 do
44 if img.[i
, j
] > 0uy && img.Data.[i
- 1, j
] > 0uy && (img.Data.[i
, j
- 1] > 0uy && img.Data.[i
+ 1, j
+ 1] = 0uy || img.Data.[i
, j
+ 1] > 0uy && img.Data.[i
+ 1, j
- 1] = 0uy)
49 let findEdges (img: Image<Gray, float32
>) : Matrix<byte
> * Image<Gray, float> * Image<Gray, float> =
54 new ConvolutionKernelF(array2D
[[ 1.0f; 0.0f; -1.0f ]
56 [ 1.0f; 0.0f; -1.0f ]], Point(1, 1))
58 let xGradient = img.Convolution(sobelKernel).Convert<Gray, float>()
59 let yGradient = img.Convolution(sobelKernel.Transpose()).Convert<Gray, float>()
61 let xGradientData = xGradient.Data
62 let yGradientData = yGradient.Data
63 for r
in 0 .. h - 1 do
64 xGradientData.[r
, 0, 0] <- 0.0
65 xGradientData.[r
, w - 1, 0] <- 0.0
66 yGradientData.[r
, 0, 0] <- 0.0
67 yGradientData.[r
, w - 1, 0] <- 0.0
69 for c
in 0 .. w - 1 do
70 xGradientData.[0, c
, 0] <- 0.0
71 xGradientData.[h - 1, c
, 0] <- 0.0
72 yGradientData.[0, c
, 0] <- 0.0
73 yGradientData.[h - 1, c
, 0] <- 0.0
75 use magnitudes = new Matrix<float>(xGradient.Size)
76 use angles = new Matrix<float>(xGradient.Size)
77 CvInvoke.CartToPolar(xGradient, yGradient, magnitudes, angles) // Compute the magnitudes (without angles).
79 let thresholdHigh, thresholdLow
=
80 let sensibilityHigh = 0.1
81 let sensibilityLow = 0.1
82 use magnitudesByte = magnitudes.Convert<byte
>()
83 let threshold = CvInvoke.Threshold(magnitudesByte, magnitudesByte, 0.0, 1.0, CvEnum.ThresholdType.Otsu ||| CvEnum.ThresholdType.Binary)
84 threshold + (sensibilityHigh * threshold), threshold - (sensibilityLow * threshold)
86 // Non-maximum suppression.
87 use nms = new Matrix<byte
>(xGradient.Size)
89 for i
in 0 .. h - 1 do
90 nms.Data.[i
, 0] <- 0uy
91 nms.Data.[i
, w - 1] <- 0uy
93 for j
in 0 .. w - 1 do
94 nms.Data.[0, j
] <- 0uy
95 nms.Data.[h - 1, j
] <- 0uy
97 for i
in 1 .. h - 2 do
98 for j
in 1 .. w - 2 do
99 let vx = xGradient.Data.[i
, j
, 0]
100 let vy = yGradient.Data.[i
, j
, 0]
101 if vx <> 0. || vy <> 0.
103 let angle = angles.[i
, j
]
105 let vx', vy' = abs
vx, abs
vy
106 let ratio2 = if vx' > vy' then vy' / vx' else vx' / vy'
107 let ratio1 = 1. - ratio2
109 let mNeigbors (sign
: int) : float =
110 if angle < Math.PI / 4.
111 then ratio1 * magnitudes.Data.[i
, j
+ sign
] + ratio2 * magnitudes.Data.[i
+ sign
, j
+ sign
]
112 elif
angle < Math.PI / 2.
113 then ratio2 * magnitudes.Data.[i
+ sign
, j
+ sign
] + ratio1 * magnitudes.Data.[i
+ sign
, j
]
114 elif
angle < 3.0 * Math.PI / 4.
115 then ratio1 * magnitudes.Data.[i
+ sign
, j
] + ratio2 * magnitudes.Data.[i
+ sign
, j
- sign
]
117 then ratio2 * magnitudes.Data.[i
+ sign
, j
- sign
] + ratio1 * magnitudes.Data.[i
, j
- sign
]
118 elif
angle < 5. * Math.PI / 4.
119 then ratio1 * magnitudes.Data.[i
, j
- sign
] + ratio2 * magnitudes.Data.[i
- sign
, j
- sign
]
120 elif
angle < 3. * Math.PI / 2.
121 then ratio2 * magnitudes.Data.[i
- sign
, j
- sign
] + ratio1 * magnitudes.Data.[i
- sign
, j
]
122 elif
angle < 7. * Math.PI / 4.
123 then ratio1 * magnitudes.Data.[i
- sign
, j
] + ratio2 * magnitudes.Data.[i
- sign
, j
+ sign
]
124 else ratio2 * magnitudes.Data.[i
- sign
, j
+ sign
] + ratio1 * magnitudes.Data.[i
, j
+ sign
]
126 let m = magnitudes.Data.[i
, j
]
127 if m >= thresholdLow
&& m > mNeigbors 1 && m > mNeigbors -1
129 nms.Data.[i
, j
] <- 1uy
131 // suppressMConnections nms // It's not helpful for the rest of the process (ellipse detection).
133 let edges = new Matrix<byte
>(xGradient.Size)
135 // Histeresis thresholding.
136 let toVisit = Stack<Point>()
137 for i
in 0 .. h - 1 do
138 for j
in 0 .. w - 1 do
139 if nms.Data.[i
, j
] = 1uy && magnitudes.Data.[i
, j
] >= thresholdHigh
141 nms.Data.[i
, j
] <- 0uy
142 toVisit.Push(Point(j
, i
))
143 while toVisit.Count > 0 do
144 let p = toVisit.Pop()
145 edges.Data.[p.Y, p.X] <- 1uy
148 if i
' <> 0 || j' <> 0
152 if ni >= 0 && ni < h && nj >= 0 && nj < w && nms.Data.[ni, nj] = 1uy
154 nms.Data.[ni, nj] <- 0uy
155 toVisit.Push(Point(nj, ni))
157 edges, xGradient, yGradient
160 let gaussianFilter (img : Image<'TColor, 'TDepth>) (standardDeviation
: float) : Image<'TColor, 'TDepth> =
161 let size = 2 * int (ceil
(4.0 * standardDeviation
)) + 1
162 img.SmoothGaussian(size, size, standardDeviation
, standardDeviation
)
165 type Points = HashSet<Point>
167 let drawPoints (img: Image<Gray, 'TDepth>) (points: Points) (intensity: 'TDepth) =
169 img.Data.[p.Y, p.X, 0] <- intensity
175 let findExtremum (img: Image<Gray, 'TDepth>) (extremumType: ExtremumType) : IEnumerable<Points> =
178 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
180 let imgData = img.Data
181 let suppress: bool[,] = Array2D.zeroCreate h w
183 let result = List<List<Point>>()
185 let flood (start: Point) : List<List<Point>> =
186 let sameLevelToCheck = Stack<Point>()
187 let betterLevelToCheck = Stack<Point>()
188 betterLevelToCheck.Push(start)
190 let result' = List<List<Point>>()
192 while betterLevelToCheck.Count > 0 do
193 let p = betterLevelToCheck.Pop()
194 if not suppress.[p.Y, p.X]
196 suppress.[p.Y, p.X] <- true
197 sameLevelToCheck.Push(p)
198 let current = List<Point>()
200 let mutable betterExists = false
202 while sameLevelToCheck.Count > 0 do
203 let p' = sameLevelToCheck.Pop()
204 let currentLevel = imgData.[p'.Y, p'.X, 0]
205 current.Add(p') |> ignore
209 if ni >= 0 && ni < h && nj >= 0 && nj < w
211 let level = imgData.[ni, nj, 0]
212 let notSuppressed = not suppress.[ni, nj]
214 if level = currentLevel && notSuppressed
216 suppress.[ni, nj] <- true
217 sameLevelToCheck.Push(Point(nj, ni))
218 elif
if extremumType
= ExtremumType.Maxima then level > currentLevel else level < currentLevel
223 betterLevelToCheck.Push(Point(nj, ni))
230 for i
in 0 .. h - 1 do
231 for j in 0 .. w - 1 do
232 let maxima = flood (Point(j, i
))
235 result.AddRange(maxima)
237 result.Select(fun l
-> Points(l
))
240 let findMaxima (img: Image<Gray, 'TDepth>) : IEnumerable<Points> =
241 findExtremum img ExtremumType.Maxima
244 let findMinima (img: Image<Gray, 'TDepth>) : IEnumerable<Points> =
245 findExtremum img ExtremumType.Minima
248 type PriorityQueue () =
250 let q: Points[] = Array.init
size (fun i
-> Points())
251 let mutable highest = -1 // Value of the first elements of 'q'.
252 let mutable lowest = size
254 member this
.NextMax () : byte
* Point =
257 invalidOp
"Queue is empty"
261 l.Remove(next) |> ignore
262 let value = byte
highest
266 highest <- highest - 1
267 while highest > lowest && q.[highest].Count = 0 do
268 highest <- highest - 1
276 member this
.NextMin () : byte
* Point =
279 invalidOp
"Queue is empty"
281 let l = q.[lowest + 1]
283 l.Remove(next) |> ignore
284 let value = byte
(lowest + 1)
289 while lowest < highest && q.[lowest + 1].Count = 0 do
304 member this
.Add (value: byte
) (p: Point) =
314 q.[vi].Add(p) |> ignore
316 member this
.Remove (value: byte
) (p: Point) =
318 if q.[vi].Remove(p) && q.[vi].Count = 0
322 highest <- highest - 1
323 while highest > lowest && q.[highest].Count = 0 do
324 highest <- highest - 1
328 while lowest < highest && q.[lowest + 1].Count = 0 do
331 if highest = lowest // The queue is now empty.
336 member this
.IsEmpty =
339 member this
.Clear () =
340 while highest > lowest do
342 highest <- highest - 1
347 type private AreaState =
352 type private AreaOperation =
357 type private Area (elements
: Points) =
358 member this
.Elements = elements
359 member val Intensity = None with get
, set
360 member val State = AreaState.Unprocessed with get
, set
362 let private areaOperation
(img: Image<Gray, byte
>) (area
: int) (op
: AreaOperation) =
365 let imgData = img.Data
366 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
368 let areas = List<Area>((if op
= AreaOperation.Opening then findMaxima img else findMinima img) |> Seq.map
Area)
370 let pixels: Area[,] = Array2D.create
h w null
372 for e
in m.Elements do
373 pixels.[e
.Y, e
.X] <- m
375 let queue = PriorityQueue()
377 let addEdgeToQueue (elements
: Points) =
382 let p' = Point(nj, ni)
383 if ni >= 0 && ni < h && nj >= 0 && nj < w && not (elements.Contains(p'))
385 queue.Add (imgData.[ni, nj, 0]) p'
387 // Reverse order is quicker.
388 for i in areas.Count - 1 .. -1 .. 0 do
390 if m.Elements.Count <= area && m.State <> AreaState.Removed
393 addEdgeToQueue m.Elements
395 let mutable intensity = if op = AreaOperation.Opening then queue.Max else queue.Min
396 let nextElements = Points()
398 let mutable stop = false
400 let intensity', p = if op
= AreaOperation.Opening then queue.NextMax () else queue.NextMin ()
401 let mutable merged = false
403 if intensity' = intensity // The intensity doesn't
change.
405 if m.Elements.Count + nextElements.Count + 1 > area
407 m.State <- AreaState.Validated
408 m.Intensity <- Some intensity
411 nextElements.Add(p) |> ignore
413 elif
if op
= AreaOperation.Opening then intensity' < intensity else intensity' > intensity
415 m.Elements.UnionWith(nextElements)
416 for e
in nextElements do
417 pixels.[e
.Y, e
.X] <- m
419 if m.Elements.Count = area
421 m.State <- AreaState.Validated
422 m.Intensity <- Some (intensity')
425 intensity <- intensity'
427 nextElements.Add(p) |> ignore
430 let m' = pixels.[p.Y, p.X]
433 if m'.Elements.Count + m.Elements.Count <= area
435 m'.State <- AreaState.Removed
436 for e
in m'.Elements do
437 pixels.[e.Y, e.X] <- m
438 queue.Remove imgData.[e.Y, e.X, 0] e
439 addEdgeToQueue m'.Elements
440 m.Elements.UnionWith(m'.Elements)
441 let intensityMax = if op = AreaOperation.Opening then queue.Max else queue.Min
442 if intensityMax <> intensity
444 intensity <- intensityMax
450 m.State <- AreaState.Validated
451 m.Intensity <- Some (intensity)
454 if not stop && not merged
459 let p' = Point(nj, ni)
460 if ni < 0 || ni >= h || nj < 0 || nj >= w
462 m.State <- AreaState.Validated
463 m.Intensity <- Some (intensity)
465 elif
not (m.Elements.Contains(p')) && not (nextElements.Contains(p'))
467 queue.Add (imgData.[ni, nj, 0]) p'
471 if m.Elements.Count + nextElements.Count <= area
473 m.State <- AreaState.Validated
474 m.Intensity <- Some intensity'
475 m.Elements.UnionWith(nextElements)
479 if m.State = AreaState.Validated
481 match m.Intensity with
483 for p in m.Elements do
484 imgData.[p.Y, p.X, 0] <- i
489 let areaOpen (img: Image<Gray, byte
>) (area
: int) =
490 areaOperation
img area
AreaOperation.Opening
492 let areaClose (img: Image<Gray, byte
>) (area
: int) =
493 areaOperation
img area
AreaOperation.Closing
496 type Island (cmp
: IComparer<float32
>) =
497 member val Shore = Heap.Heap<float32
, Point>(cmp
) with get
498 member val Level = 0.f
with get
, set
499 member val Surface = 0 with get
, set
502 let private areaOperationF
(img: Image<Gray, float32
>) (area
: int) (op
: AreaOperation) =
506 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
508 let comparer = if op
= AreaOperation.Opening
509 then { new IComparer<float32
> with member this
.Compare(v1
, v2
) = v1
.CompareTo(v2
) }
510 else { new IComparer<float32
> with member this
.Compare(v1
, v2
) = v2
.CompareTo(v1
) }
512 let ownership: Island[,] = Array2D.create
h w null
514 // Initialize islands with their shore.
515 let islands = List<Island>()
516 let extremum = img |> if op
= AreaOperation.Opening then findMaxima else findMinima
520 Island(comparer, Level = earth.[p.Y, p.X, 0], Surface = e
.Count)
522 let shorePoints = Points()
524 ownership.[p.Y, p.X] <- island
528 let neighbor = Point(nj, ni)
529 if ni >= 0 && ni < h && nj >= 0 && nj < w && ownership.[ni, nj] = null && not (shorePoints.Contains(neighbor))
531 shorePoints.Add(neighbor) |> ignore
532 island.Shore.Add earth.[ni, nj, 0] neighbor
534 for island in islands do
535 let mutable stop = island.Shore.IsEmpty
537 // 'true' if 'p' is owned or adjacent to 'island'.
538 let ownedOrAdjacent (p: Point) : bool =
539 ownership.[p.Y, p.X] = island ||
540 (p.Y > 0 && ownership.[p.Y - 1, p.X] = island) ||
541 (p.Y < h - 1 && ownership.[p.Y + 1, p.X] = island) ||
542 (p.X > 0 && ownership.[p.Y, p.X - 1] = island) ||
543 (p.X < w - 1 && ownership.[p.Y, p.X + 1] = island)
545 while not stop && island.Surface < area
do
546 let level, next = island.Shore.Max
547 let other = ownership.[next.Y, next.X]
548 if other = island // During merging, some points on the shore may be owned by the island itself -> ignored.
550 island.Shore.RemoveNext ()
553 then // We touching another island.
554 if island.Surface + other.Surface >= area
557 else // We can merge 'other' into 'surface'.
558 island.Surface <- island.Surface + other.Surface
559 island.Level <- if comparer.Compare(island.Level, other.Level) > 0 then island.Level else other.Level
560 for l, p in other.Shore do
561 let mutable currentY = p.Y + 1
562 while currentY < h && ownership.[currentY, p.X] = other do
563 ownership.[currentY, p.X] <- island
564 currentY <- currentY + 1
568 elif
comparer.Compare(level, island.Level) > 0
572 island.Shore.RemoveNext ()
576 if ni < 0 || ni >= h || nj < 0 || nj >= w
578 island.Surface <- Int32.MaxValue
581 let neighbor = Point(nj, ni)
582 if not <| ownedOrAdjacent neighbor
584 island.Shore.Add earth.[ni, nj, 0] neighbor
587 ownership.[next.Y, next.X] <- island
588 island.Level <- level
589 island.Surface <- island.Surface + 1
591 for i
in 0 .. h - 1 do
592 for j in 0 .. w - 1 do
593 let island = ownership.[i
, j]
596 earth.[i
, j, 0] <- island.Level
600 let areaOpenF (img: Image<Gray, float32
>) (area
: int) =
601 areaOperationF
img area
AreaOperation.Opening
603 let areaCloseF (img: Image<Gray, float32
>) (area
: int) =
604 areaOperationF
img area
AreaOperation.Closing
606 // A simpler algorithm than 'areaOpen' but slower.
607 let areaOpen2 (img: Image<Gray, byte
>) (area
: int) =
610 let imgData = img.Data
611 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
613 let histogram = Array.zeroCreate
256
614 for i
in 0 .. h - 1 do
615 for j in 0 .. w - 1 do
616 let v = imgData.[i
, j, 0] |> int
617 histogram.[v] <- histogram.[v] + 1
619 let flooded : bool[,] = Array2D.zeroCreate
h w
621 let pointsChecked = HashSet<Point>()
622 let pointsToCheck = Stack<Point>()
624 for level in 255 .. -1 .. 0 do
625 let mutable n = histogram.[level]
628 for i
in 0 .. h - 1 do
629 for j in 0 .. w - 1 do
630 if not flooded.[i
, j] && imgData.[i
, j, 0] = byte
level
632 let mutable maxNeighborValue = 0uy
633 pointsChecked.Clear()
634 pointsToCheck.Clear()
635 pointsToCheck.Push(Point(j, i
))
637 while pointsToCheck.Count > 0 do
638 let next = pointsToCheck.Pop()
639 pointsChecked.Add(next) |> ignore
640 flooded.[next.Y, next.X] <- true
643 let p = Point(next.X + nx
, next.Y + ny
)
644 if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h
646 let v = imgData.[p.Y, p.X, 0]
649 if not (pointsChecked.Contains(p))
651 pointsToCheck.Push(p)
652 elif
v > maxNeighborValue
654 maxNeighborValue <- v
656 if int maxNeighborValue < level && pointsChecked.Count <= area
658 for p in pointsChecked do
659 imgData.[p.Y, p.X, 0] <- maxNeighborValue
662 // Zhang and Suen algorithm.
663 // Modify 'mat' in place.
664 let thin (mat
: Matrix<byte
>) =
667 let mutable data1 = mat
.Data
668 let mutable data2 = Array2D.copy
data1
670 let mutable pixelChanged = true
671 let mutable oddIteration = true
673 while pixelChanged do
674 pixelChanged <- false
677 if data1.[i
, j] = 1uy
679 let p2 = if i
= 0 then 0uy else data1.[i
-1, j]
680 let p3 = if i
= 0 || j = w-1 then 0uy else data1.[i
-1, j+1]
681 let p4 = if j = w-1 then 0uy else data1.[i
, j+1]
682 let p5 = if i
= h-1 || j = w-1 then 0uy else data1.[i
+1, j+1]
683 let p6 = if i
= h-1 then 0uy else data1.[i
+1, j]
684 let p7 = if i
= h-1 || j = 0 then 0uy else data1.[i
+1, j-1]
685 let p8 = if j = 0 then 0uy else data1.[i
, j-1]
686 let p9 = if i
= 0 || j = 0 then 0uy else data1.[i
-1, j-1]
688 let sumNeighbors = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9
689 if sumNeighbors >= 2uy && sumNeighbors <= 6uy &&
690 (if p2 = 0uy && p3 = 1uy then 1 else 0) +
691 (if p3 = 0uy && p4 = 1uy then 1 else 0) +
692 (if p4 = 0uy && p5 = 1uy then 1 else 0) +
693 (if p5 = 0uy && p6 = 1uy then 1 else 0) +
694 (if p6 = 0uy && p7 = 1uy then 1 else 0) +
695 (if p7 = 0uy && p8 = 1uy then 1 else 0) +
696 (if p8 = 0uy && p9 = 1uy then 1 else 0) +
697 (if p9 = 0uy && p2 = 1uy then 1 else 0) = 1 &&
699 then p2 * p4 * p6 = 0uy && p4 * p6 * p8 = 0uy
700 else p2 * p4 * p8 = 0uy && p2 * p6 * p8 = 0uy
707 oddIteration <- not oddIteration
713 // Remove all 8-connected pixels with an area equal or greater than 'areaSize'.
714 // Modify 'mat' in place.
715 let removeArea (mat
: Matrix<byte
>) (areaSize
: int) =
726 let mat' = new Matrix<byte>(mat.Size)
732 let data' = mat'.Data
736 if data'.[i, j] = 1uy
738 let neighborhood = List<Point>()
739 let neighborsToCheck = Stack<Point>()
740 neighborsToCheck.Push(Point(j, i))
743 while neighborsToCheck.Count > 0 do
744 let n = neighborsToCheck.Pop()
746 for (ni, nj) in neighbors do
749 if pi >= 0 && pi < h && pj >= 0 && pj < w && data'.[pi, pj] = 1uy
751 neighborsToCheck.Push(Point(pj, pi))
752 data'.[pi, pj] <- 0uy
753 if neighborhood.Count <= areaSize
755 for n in neighborhood do
756 data.[n.Y, n.X] <- 0uy
758 let connectedComponents (img: Image<Gray, byte
>) (startPoints
: List<Point>) : List<Point> =
762 let pointChecked = Points()
763 let pointToCheck = Stack<Point>(startPoints
);
767 while pointToCheck.Count > 0 do
768 let next = pointToCheck.Pop()
769 pointChecked.Add(next) |> ignore
772 if ny
<> 0 && nx
<> 0
774 let p = Point(next.X + nx
, next.Y + ny
)
775 if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h && data.[p.Y, p.X, 0] > 0uy && not (pointChecked.Contains p)
779 List<Point>(pointChecked)
782 let drawLine (img: Image<'TColor, 'TDepth>) (color
: 'TColor) (x0: int) (y0: int) (x1: int) (y1: int) (thickness: int) =
783 img.Draw(LineSegment2D(Point(x0, y0), Point(x1, y1)), color, thickness);
786 let drawLineF (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0
: float) (y0
: float) (x1
: float) (y1
: float) (thickness
: int) =
787 img.Draw(LineSegment2DF(PointF(float32 x0
, float32 y0
), PointF(float32 x1
, float32 y1
)), color
, thickness
, CvEnum.LineType.AntiAlias);
790 let drawEllipse (img: Image<'TColor, 'TDepth>) (e
: Types.Ellipse) (color
: 'TColor) (alpha: float) =
794 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)
796 let windowPosX = e.Cx - e.A - 5.0
797 let gapX = windowPosX - (float (int windowPosX))
799 let windowPosY = e.Cy - e.A - 5.0
800 let gapY = windowPosY - (float (int windowPosY))
802 let roi = Rectangle(int windowPosX, int windowPosY, 2. * (e.A + 5.0) |> int, 2.* (e.A + 5.0) |> int)
805 if roi = img.ROI // We do not display ellipses touching the edges (FIXME)
807 use i = new Image<'TColor, 'TDepth>(img.ROI.Size)
808 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)
809 CvInvoke.AddWeighted(img, 1.0, i, alpha, 0.0, img)
810 img.ROI <- Rectangle.Empty
813 let drawEllipses (img: Image<'TColor, 'TDepth>) (ellipses: Types.Ellipse list) (color: 'TColor) (alpha
: float) =
814 List.iter
(fun e -> drawEllipse img e color alpha
) ellipses
817 let rngCell = System.Random()
818 let drawCell (img: Image<Bgr, byte
>) (drawCellContent
: bool) (c
: Types.Cell) =
821 let colorB = rngCell.Next(20, 70)
822 let colorG = rngCell.Next(20, 70)
823 let colorR = rngCell.Next(20, 70)
825 for y
in 0 .. c
.elements
.Height - 1 do
826 for x
in 0 .. c
.elements
.Width - 1 do
827 if c
.elements
.[y
, x
] > 0uy
829 let dx, dy
= c
.center
.X - c
.elements
.Width / 2, c
.center
.Y - c
.elements
.Height / 2
830 let b = img.Data.[y
+ dy
, x
+ dx, 0] |> int
831 let g = img.Data.[y
+ dy
, x
+ dx, 1] |> int
832 let r = img.Data.[y
+ dy
, x
+ dx, 2] |> int
833 img.Data.[y
+ dy
, x
+ dx, 0] <- if b + colorB > 255 then 255uy else byte (b + colorB)
834 img.Data.[y
+ dy
, x
+ dx, 1] <- if g + colorG > 255 then 255uy else byte (g + colorG)
835 img.Data.[y
+ dy
, x
+ dx, 2] <- if r + colorR > 255 then 255uy else byte (r + colorR)
837 let crossColor, crossColor2
=
838 match c
.cellClass
with
839 | Types.HealthyRBC -> Bgr(255., 0., 0.), Bgr(255., 255., 255.)
840 | Types.InfectedRBC -> Bgr(0., 0., 255.), Bgr(120., 120., 255.)
841 | Types.Peculiar -> Bgr(0., 0., 0.), Bgr(80., 80., 80.)
843 drawLine img crossColor2
(c
.center
.X - 3) c
.center
.Y (c
.center
.X + 3) c
.center
.Y 2
844 drawLine img crossColor2 c
.center
.X (c
.center
.Y - 3) c
.center
.X (c
.center
.Y + 3) 2
846 drawLine img crossColor (c
.center
.X - 3) c
.center
.Y (c
.center
.X + 3) c
.center
.Y 1
847 drawLine img crossColor c
.center
.X (c
.center
.Y - 3) c
.center
.X (c
.center
.Y + 3) 1
850 let drawCells (img: Image<Bgr, byte>) (drawCellContent
: bool) (cells
: Types.Cell list) =
851 List.iter
(fun c
-> drawCell img drawCellContent
c) cells