cee21c77d5c60fa26c8e25f4c2afa34c15056543
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)
48 let findEdges (img: Image<Gray, float32
>) : Matrix<byte
> * Image<Gray, float> * Image<Gray, float> =
53 new ConvolutionKernelF(array2D
[[ 1.0f; 0.0f; -1.0f ]
55 [ 1.0f; 0.0f; -1.0f ]], Point(1, 1))
57 let xGradient = img.Convolution(sobelKernel).Convert<Gray, float>()
58 let yGradient = img.Convolution(sobelKernel.Transpose()).Convert<Gray, float>()
60 let xGradientData = xGradient.Data
61 let yGradientData = yGradient.Data
62 for r
in 0 .. h - 1 do
63 xGradientData.[r
, 0, 0] <- 0.0
64 xGradientData.[r
, w - 1, 0] <- 0.0
65 yGradientData.[r
, 0, 0] <- 0.0
66 yGradientData.[r
, w - 1, 0] <- 0.0
68 for c
in 0 .. w - 1 do
69 xGradientData.[0, c
, 0] <- 0.0
70 xGradientData.[h - 1, c
, 0] <- 0.0
71 yGradientData.[0, c
, 0] <- 0.0
72 yGradientData.[h - 1, c
, 0] <- 0.0
74 use magnitudes = new Matrix<float>(xGradient.Size)
75 use angles = new Matrix<float>(xGradient.Size)
76 CvInvoke.CartToPolar(xGradient, yGradient, magnitudes, angles) // Compute the magnitudes (without angles).
78 let thresholdHigh, thresholdLow
=
80 use magnitudesByte = magnitudes.Convert<byte
>()
81 let threshold = CvInvoke.Threshold(magnitudesByte, magnitudesByte, 0.0, 1.0, CvEnum.ThresholdType.Otsu ||| CvEnum.ThresholdType.Binary)
82 threshold + (sensibility * threshold), threshold - (sensibility * threshold)
84 // Non-maximum suppression.
85 use nms = new Matrix<byte
>(xGradient.Size)
87 for i
in 0 .. h - 1 do
88 nms.Data.[i
, 0] <- 0uy
89 nms.Data.[i
, w - 1] <- 0uy
91 for j
in 0 .. w - 1 do
92 nms.Data.[0, j
] <- 0uy
93 nms.Data.[h - 1, j
] <- 0uy
95 for i
in 1 .. h - 2 do
96 for j
in 1 .. w - 2 do
97 let vx = xGradient.Data.[i
, j
, 0]
98 let vy = yGradient.Data.[i
, j
, 0]
99 if vx <> 0. || vy <> 0.
101 let angle = angles.[i
, j
]
103 let vx', vy' = abs
vx, abs
vy
104 let ratio2 = if vx' > vy' then vy' / vx' else vx' / vy'
105 let ratio1 = 1. - ratio2
107 let mNeigbors (sign
: int) : float =
108 if angle < Math.PI / 4.
110 ratio1 * magnitudes.Data.[i
, j
+ sign
] + ratio2 * magnitudes.Data.[i
+ sign
, j
+ sign
]
111 elif
angle < Math.PI / 2.
113 ratio2 * magnitudes.Data.[i
+ sign
, j
+ sign
] + ratio1 * magnitudes.Data.[i
+ sign
, j
]
114 elif
angle < 3.0 * Math.PI / 4.
116 ratio1 * magnitudes.Data.[i
+ sign
, j
] + ratio2 * magnitudes.Data.[i
+ sign
, j
- sign
]
119 ratio2 * magnitudes.Data.[i
+ sign
, j
- sign
] + ratio1 * magnitudes.Data.[i
, j
- sign
]
120 elif
angle < 5. * Math.PI / 4.
122 ratio1 * magnitudes.Data.[i
, j
- sign
] + ratio2 * magnitudes.Data.[i
- sign
, j
- sign
]
123 elif
angle < 3. * Math.PI / 2.
125 ratio2 * magnitudes.Data.[i
- sign
, j
- sign
] + ratio1 * magnitudes.Data.[i
- sign
, j
]
126 elif
angle < 7. * Math.PI / 4.
128 ratio1 * magnitudes.Data.[i
- sign
, j
] + ratio2 * magnitudes.Data.[i
- sign
, j
+ sign
]
130 ratio2 * magnitudes.Data.[i
- sign
, j
+ sign
] + ratio1 * magnitudes.Data.[i
, j
+ sign
]
132 let m = magnitudes.Data.[i
, j
]
133 if m >= thresholdLow
&& m > mNeigbors 1 && m > mNeigbors -1
135 nms.Data.[i
, j
] <- 1uy
137 // suppressMConnections nms // It's not helpful for the rest of the process (ellipse detection).
139 let edges = new Matrix<byte
>(xGradient.Size)
141 // Histeresis thresholding.
142 let toVisit = Stack<Point>()
143 for i
in 0 .. h - 1 do
144 for j
in 0 .. w - 1 do
145 if nms.Data.[i
, j
] = 1uy && magnitudes.Data.[i
, j
] >= thresholdHigh
147 nms.Data.[i
, j
] <- 0uy
148 toVisit.Push(Point(j
, i
))
149 while toVisit.Count > 0 do
150 let p = toVisit.Pop()
151 edges.Data.[p.Y, p.X] <- 1uy
154 if i
' <> 0 || j' <> 0
158 if ni >= 0 && ni < h && nj >= 0 && nj < w && nms.Data.[ni, nj] = 1uy
160 nms.Data.[ni, nj] <- 0uy
161 toVisit.Push(Point(nj, ni))
163 edges, xGradient, yGradient
166 let gaussianFilter (img : Image<'TColor, 'TDepth>) (standardDeviation
: float) : Image<'TColor, 'TDepth> =
167 let size = 2 * int (ceil
(4.0 * standardDeviation
)) + 1
168 img.SmoothGaussian(size, size, standardDeviation
, standardDeviation
)
171 type Points = HashSet<Point>
173 let drawPoints (img: Image<Gray, 'TDepth>) (points: Points) (intensity: 'TDepth) =
175 img.Data.[p.Y, p.X, 0] <- intensity
181 let findExtremum (img: Image<Gray, 'TDepth>) (extremumType: ExtremumType) : IEnumerable<Points> =
184 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
186 let imgData = img.Data
187 let suppress: bool[,] = Array2D.zeroCreate h w
189 let result = List<List<Point>>()
191 let flood (start: Point) : List<List<Point>> =
192 let sameLevelToCheck = Stack<Point>()
193 let betterLevelToCheck = Stack<Point>()
194 betterLevelToCheck.Push(start)
196 let result' = List<List<Point>>()
198 while betterLevelToCheck.Count > 0 do
199 let p = betterLevelToCheck.Pop()
200 if not suppress.[p.Y, p.X]
202 suppress.[p.Y, p.X] <- true
203 sameLevelToCheck.Push(p)
204 let current = List<Point>()
206 let mutable betterExists = false
208 while sameLevelToCheck.Count > 0 do
209 let p' = sameLevelToCheck.Pop()
210 let currentLevel = imgData.[p'.Y, p'.X, 0]
211 current.Add(p') |> ignore
215 if ni >= 0 && ni < h && nj >= 0 && nj < w
217 let level = imgData.[ni, nj, 0]
218 let notSuppressed = not suppress.[ni, nj]
220 if level = currentLevel && notSuppressed
222 suppress.[ni, nj] <- true
223 sameLevelToCheck.Push(Point(nj, ni))
224 elif
if extremumType
= ExtremumType.Maxima then level > currentLevel else level < currentLevel
229 betterLevelToCheck.Push(Point(nj, ni))
236 for i
in 0 .. h - 1 do
237 for j in 0 .. w - 1 do
238 let maxima = flood (Point(j, i
))
241 result.AddRange(maxima)
243 result.Select(fun l
-> Points(l
))
246 let findMaxima (img: Image<Gray, 'TDepth>) : IEnumerable<Points> =
247 findExtremum img ExtremumType.Maxima
250 let findMinima (img: Image<Gray, 'TDepth>) : IEnumerable<Points> =
251 findExtremum img ExtremumType.Minima
254 type PriorityQueue () =
256 let q: Points[] = Array.init
size (fun i
-> Points())
257 let mutable highest = -1 // Value of the first elements of 'q'.
258 let mutable lowest = size
260 member this
.NextMax () : byte
* Point =
263 invalidOp
"Queue is empty"
267 l.Remove(next) |> ignore
268 let value = byte
highest
272 highest <- highest - 1
273 while highest > lowest && q.[highest].Count = 0 do
274 highest <- highest - 1
282 member this
.NextMin () : byte
* Point =
285 invalidOp
"Queue is empty"
287 let l = q.[lowest + 1]
289 l.Remove(next) |> ignore
290 let value = byte
(lowest + 1)
295 while lowest < highest && q.[lowest + 1].Count = 0 do
310 member this
.Add (value: byte
) (p: Point) =
320 q.[vi].Add(p) |> ignore
322 member this
.Remove (value: byte
) (p: Point) =
324 if q.[vi].Remove(p) && q.[vi].Count = 0
328 highest <- highest - 1
329 while highest > lowest && q.[highest].Count = 0 do
330 highest <- highest - 1
334 while lowest < highest && q.[lowest + 1].Count = 0 do
337 if highest = lowest // The queue is now empty.
342 member this
.IsEmpty =
345 member this
.Clear () =
346 while highest > lowest do
348 highest <- highest - 1
353 type private AreaState =
358 type private AreaOperation =
363 type private Area (elements
: Points) =
364 member this
.Elements = elements
365 member val Intensity = None with get
, set
366 member val State = AreaState.Unprocessed with get
, set
368 let private areaOperation
(img: Image<Gray, byte
>) (area
: int) (op
: AreaOperation) =
371 let imgData = img.Data
372 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
374 let areas = List<Area>((if op
= AreaOperation.Opening then findMaxima img else findMinima img) |> Seq.map
Area)
376 let pixels: Area[,] = Array2D.create
h w null
378 for e
in m.Elements do
379 pixels.[e
.Y, e
.X] <- m
381 let queue = PriorityQueue()
383 let addEdgeToQueue (elements
: Points) =
388 let p' = Point(nj, ni)
389 if ni >= 0 && ni < h && nj >= 0 && nj < w && not (elements.Contains(p'))
391 queue.Add (imgData.[ni, nj, 0]) p'
393 // Reverse order is quicker.
394 for i in areas.Count - 1 .. -1 .. 0 do
396 if m.Elements.Count <= area && m.State <> AreaState.Removed
399 addEdgeToQueue m.Elements
401 let mutable intensity = if op = AreaOperation.Opening then queue.Max else queue.Min
402 let nextElements = Points()
404 let mutable stop = false
406 let intensity', p = if op
= AreaOperation.Opening then queue.NextMax () else queue.NextMin ()
407 let mutable merged = false
409 if intensity' = intensity // The intensity doesn't
change.
411 if m.Elements.Count + nextElements.Count + 1 > area
413 m.State <- AreaState.Validated
414 m.Intensity <- Some intensity
417 nextElements.Add(p) |> ignore
419 elif
if op
= AreaOperation.Opening then intensity' < intensity else intensity' > intensity
421 m.Elements.UnionWith(nextElements)
422 for e
in nextElements do
423 pixels.[e
.Y, e
.X] <- m
425 if m.Elements.Count = area
427 m.State <- AreaState.Validated
428 m.Intensity <- Some (intensity')
431 intensity <- intensity'
433 nextElements.Add(p) |> ignore
436 let m' = pixels.[p.Y, p.X]
439 if m'.Elements.Count + m.Elements.Count <= area
441 m'.State <- AreaState.Removed
442 for e
in m'.Elements do
443 pixels.[e.Y, e.X] <- m
444 queue.Remove imgData.[e.Y, e.X, 0] e
445 addEdgeToQueue m'.Elements
446 m.Elements.UnionWith(m'.Elements)
447 let intensityMax = if op = AreaOperation.Opening then queue.Max else queue.Min
448 if intensityMax <> intensity
450 intensity <- intensityMax
456 m.State <- AreaState.Validated
457 m.Intensity <- Some (intensity)
460 if not stop && not merged
465 let p' = Point(nj, ni)
466 if ni < 0 || ni >= h || nj < 0 || nj >= w
468 m.State <- AreaState.Validated
469 m.Intensity <- Some (intensity)
471 elif
not (m.Elements.Contains(p')) && not (nextElements.Contains(p'))
473 queue.Add (imgData.[ni, nj, 0]) p'
477 if m.Elements.Count + nextElements.Count <= area
479 m.State <- AreaState.Validated
480 m.Intensity <- Some intensity'
481 m.Elements.UnionWith(nextElements)
485 if m.State = AreaState.Validated
487 match m.Intensity with
489 for p in m.Elements do
490 imgData.[p.Y, p.X, 0] <- i
495 let areaOpen (img: Image<Gray, byte
>) (area
: int) =
496 areaOperation
img area
AreaOperation.Opening
498 let areaClose (img: Image<Gray, byte
>) (area
: int) =
499 areaOperation
img area
AreaOperation.Closing
502 type Island (cmp
: IComparer<float32
>) =
503 member val Shore = Heap.Heap<float32
, Point>(cmp
) with get
504 member val Level = 0.f
with get
, set
505 member val Surface = 0 with get
, set
508 let private areaOperationF
(img: Image<Gray, float32
>) (area
: int) (op
: AreaOperation) =
512 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
514 let comparer = if op
= AreaOperation.Opening
515 then { new IComparer<float32
> with member this
.Compare(v1
, v2
) = v1
.CompareTo(v2
) }
516 else { new IComparer<float32
> with member this
.Compare(v1
, v2
) = v2
.CompareTo(v1
) }
518 let ownership: Island[,] = Array2D.create
h w null
520 // Initialize islands with their shore.
521 let islands = List<Island>()
522 let extremum = img |> if op
= AreaOperation.Opening then findMaxima else findMinima
526 Island(comparer, Level = earth.[p.Y, p.X, 0], Surface = e
.Count)
528 let shorePoints = Points()
530 ownership.[p.Y, p.X] <- island
534 let neighbor = Point(nj, ni)
535 if ni >= 0 && ni < h && nj >= 0 && nj < w && ownership.[ni, nj] = null && not (shorePoints.Contains(neighbor))
537 shorePoints.Add(neighbor) |> ignore
538 island.Shore.Add earth.[ni, nj, 0] neighbor
540 for island in islands do
541 let mutable stop = island.Shore.IsEmpty
543 // 'true' if 'p' is owned or adjacent to 'island'.
544 let ownedOrAdjacent (p: Point) : bool =
545 ownership.[p.Y, p.X] = island ||
546 (p.Y > 0 && ownership.[p.Y - 1, p.X] = island) ||
547 (p.Y < h - 1 && ownership.[p.Y + 1, p.X] = island) ||
548 (p.X > 0 && ownership.[p.Y, p.X - 1] = island) ||
549 (p.X < w - 1 && ownership.[p.Y, p.X + 1] = island)
551 while not stop && island.Surface < area
do
552 let level, next = island.Shore.Max
553 let other = ownership.[next.Y, next.X]
554 if other = island // During merging, some points on the shore may be owned by the island itself -> ignored.
556 island.Shore.RemoveNext ()
559 then // We touching another island.
560 if island.Surface + other.Surface >= area
563 else // We can merge 'other' into 'surface'.
564 island.Surface <- island.Surface + other.Surface
565 island.Level <- if comparer.Compare(island.Level, other.Level) > 0 then island.Level else other.Level
566 for l, p in other.Shore do
567 let mutable currentY = p.Y + 1
568 while currentY < h && ownership.[currentY, p.X] = other do
569 ownership.[currentY, p.X] <- island
570 currentY <- currentY + 1
574 elif
comparer.Compare(level, island.Level) > 0
578 island.Shore.RemoveNext ()
582 if ni < 0 || ni >= h || nj < 0 || nj >= w
584 island.Surface <- Int32.MaxValue
587 let neighbor = Point(nj, ni)
588 if not <| ownedOrAdjacent neighbor
590 island.Shore.Add earth.[ni, nj, 0] neighbor
593 ownership.[next.Y, next.X] <- island
594 island.Level <- level
595 island.Surface <- island.Surface + 1
597 for i
in 0 .. h - 1 do
598 for j in 0 .. w - 1 do
599 let island = ownership.[i
, j]
602 earth.[i
, j, 0] <- island.Level
606 let areaOpenF (img: Image<Gray, float32
>) (area
: int) =
607 areaOperationF
img area
AreaOperation.Opening
609 let areaCloseF (img: Image<Gray, float32
>) (area
: int) =
610 areaOperationF
img area
AreaOperation.Closing
612 // A simpler algorithm than 'areaOpen' but slower.
613 let areaOpen2 (img: Image<Gray, byte
>) (area
: int) =
616 let imgData = img.Data
617 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
619 let histogram = Array.zeroCreate
256
620 for i
in 0 .. h - 1 do
621 for j in 0 .. w - 1 do
622 let v = imgData.[i
, j, 0] |> int
623 histogram.[v] <- histogram.[v] + 1
625 let flooded : bool[,] = Array2D.zeroCreate
h w
627 let pointsChecked = HashSet<Point>()
628 let pointsToCheck = Stack<Point>()
630 for level in 255 .. -1 .. 0 do
631 let mutable n = histogram.[level]
634 for i
in 0 .. h - 1 do
635 for j in 0 .. w - 1 do
636 if not flooded.[i
, j] && imgData.[i
, j, 0] = byte
level
638 let mutable maxNeighborValue = 0uy
639 pointsChecked.Clear()
640 pointsToCheck.Clear()
641 pointsToCheck.Push(Point(j, i
))
643 while pointsToCheck.Count > 0 do
644 let next = pointsToCheck.Pop()
645 pointsChecked.Add(next) |> ignore
646 flooded.[next.Y, next.X] <- true
649 let p = Point(next.X + nx
, next.Y + ny
)
650 if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h
652 let v = imgData.[p.Y, p.X, 0]
655 if not (pointsChecked.Contains(p))
657 pointsToCheck.Push(p)
658 elif
v > maxNeighborValue
660 maxNeighborValue <- v
662 if int maxNeighborValue < level && pointsChecked.Count <= area
664 for p in pointsChecked do
665 imgData.[p.Y, p.X, 0] <- maxNeighborValue
668 // Zhang and Suen algorithm.
669 // Modify 'mat' in place.
670 let thin (mat
: Matrix<byte
>) =
673 let mutable data1 = mat
.Data
674 let mutable data2 = Array2D.copy
data1
676 let mutable pixelChanged = true
677 let mutable oddIteration = true
679 while pixelChanged do
680 pixelChanged <- false
683 if data1.[i
, j] = 1uy
685 let p2 = if i
= 0 then 0uy else data1.[i
-1, j]
686 let p3 = if i
= 0 || j = w-1 then 0uy else data1.[i
-1, j+1]
687 let p4 = if j = w-1 then 0uy else data1.[i
, j+1]
688 let p5 = if i
= h-1 || j = w-1 then 0uy else data1.[i
+1, j+1]
689 let p6 = if i
= h-1 then 0uy else data1.[i
+1, j]
690 let p7 = if i
= h-1 || j = 0 then 0uy else data1.[i
+1, j-1]
691 let p8 = if j = 0 then 0uy else data1.[i
, j-1]
692 let p9 = if i
= 0 || j = 0 then 0uy else data1.[i
-1, j-1]
694 let sumNeighbors = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9
695 if sumNeighbors >= 2uy && sumNeighbors <= 6uy &&
696 (if p2 = 0uy && p3 = 1uy then 1 else 0) +
697 (if p3 = 0uy && p4 = 1uy then 1 else 0) +
698 (if p4 = 0uy && p5 = 1uy then 1 else 0) +
699 (if p5 = 0uy && p6 = 1uy then 1 else 0) +
700 (if p6 = 0uy && p7 = 1uy then 1 else 0) +
701 (if p7 = 0uy && p8 = 1uy then 1 else 0) +
702 (if p8 = 0uy && p9 = 1uy then 1 else 0) +
703 (if p9 = 0uy && p2 = 1uy then 1 else 0) = 1 &&
705 then p2 * p4 * p6 = 0uy && p4 * p6 * p8 = 0uy
706 else p2 * p4 * p8 = 0uy && p2 * p6 * p8 = 0uy
713 oddIteration <- not oddIteration
719 // Remove all 8-connected pixels with an area equal or greater than 'areaSize'.
720 // Modify 'mat' in place.
721 let removeArea (mat
: Matrix<byte
>) (areaSize
: int) =
732 let mat' = new Matrix<byte>(mat.Size)
738 let data' = mat'.Data
742 if data'.[i, j] = 1uy
744 let neighborhood = List<Point>()
745 let neighborsToCheck = Stack<Point>()
746 neighborsToCheck.Push(Point(j, i))
749 while neighborsToCheck.Count > 0 do
750 let n = neighborsToCheck.Pop()
752 for (ni, nj) in neighbors do
755 if pi >= 0 && pi < h && pj >= 0 && pj < w && data'.[pi, pj] = 1uy
757 neighborsToCheck.Push(Point(pj, pi))
758 data'.[pi, pj] <- 0uy
759 if neighborhood.Count <= areaSize
761 for n in neighborhood do
762 data.[n.Y, n.X] <- 0uy
764 let connectedComponents (img: Image<Gray, byte
>) (startPoints
: List<Point>) : List<Point> =
768 let pointChecked = Points()
769 let pointToCheck = Stack<Point>(startPoints
);
773 while pointToCheck.Count > 0 do
774 let next = pointToCheck.Pop()
775 pointChecked.Add(next) |> ignore
778 if ny
<> 0 && nx
<> 0
780 let p = Point(next.X + nx
, next.Y + ny
)
781 if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h && data.[p.Y, p.X, 0] > 0uy && not (pointChecked.Contains p)
785 List<Point>(pointChecked)
788 let drawLine (img: Image<'TColor, 'TDepth>) (color
: 'TColor) (x0: int) (y0: int) (x1: int) (y1: int) (thickness: int) =
789 img.Draw(LineSegment2D(Point(x0, y0), Point(x1, y1)), color, thickness);
792 let drawLineF (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0
: float) (y0
: float) (x1
: float) (y1
: float) (thickness
: int) =
793 img.Draw(LineSegment2DF(PointF(float32 x0
, float32 y0
), PointF(float32 x1
, float32 y1
)), color
, thickness
, CvEnum.LineType.AntiAlias);
796 let drawEllipse (img: Image<'TColor, 'TDepth>) (e
: Types.Ellipse) (color
: 'TColor) (alpha: float) =
800 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)
802 let windowPosX = e.Cx - e.A - 5.0
803 let gapX = windowPosX - (float (int windowPosX))
805 let windowPosY = e.Cy - e.A - 5.0
806 let gapY = windowPosY - (float (int windowPosY))
808 let roi = Rectangle(int windowPosX, int windowPosY, 2. * (e.A + 5.0) |> int, 2.* (e.A + 5.0) |> int)
811 if roi = img.ROI // We do not display ellipses touching the edges (FIXME)
813 use i = new Image<'TColor, 'TDepth>(img.ROI.Size)
814 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)
815 CvInvoke.AddWeighted(img, 1.0, i, alpha, 0.0, img)
816 img.ROI <- Rectangle.Empty
819 let drawEllipses (img: Image<'TColor, 'TDepth>) (ellipses: Types.Ellipse list) (color: 'TColor) (alpha
: float) =
820 List.iter
(fun e -> drawEllipse img e color alpha
) ellipses
823 let rngCell = System.Random()
824 let drawCell (img: Image<Bgr, byte
>) (drawCellContent
: bool) (c
: Types.Cell) =
827 let colorB = rngCell.Next(20, 70)
828 let colorG = rngCell.Next(20, 70)
829 let colorR = rngCell.Next(20, 70)
831 for y
in 0 .. c
.elements
.Height - 1 do
832 for x
in 0 .. c
.elements
.Width - 1 do
833 if c
.elements
.[y
, x
] > 0uy
835 let dx, dy
= c
.center
.X - c
.elements
.Width / 2, c
.center
.Y - c
.elements
.Height / 2
836 let b = img.Data.[y
+ dy
, x
+ dx, 0] |> int
837 let g = img.Data.[y
+ dy
, x
+ dx, 1] |> int
838 let r = img.Data.[y
+ dy
, x
+ dx, 2] |> int
839 img.Data.[y
+ dy
, x
+ dx, 0] <- if b + colorB > 255 then 255uy else byte (b + colorB)
840 img.Data.[y
+ dy
, x
+ dx, 1] <- if g + colorG > 255 then 255uy else byte (g + colorG)
841 img.Data.[y
+ dy
, x
+ dx, 2] <- if r + colorR > 255 then 255uy else byte (r + colorR)
843 let crossColor, crossColor2
=
844 match c
.cellClass
with
845 | Types.HealthyRBC -> Bgr(255., 0., 0.), Bgr(255., 255., 255.)
846 | Types.InfectedRBC -> Bgr(0., 0., 255.), Bgr(120., 120., 255.)
847 | Types.Peculiar -> Bgr(0., 0., 0.), Bgr(80., 80., 80.)
849 drawLine img crossColor2
(c
.center
.X - 3) c
.center
.Y (c
.center
.X + 3) c
.center
.Y 2
850 drawLine img crossColor2 c
.center
.X (c
.center
.Y - 3) c
.center
.X (c
.center
.Y + 3) 2
852 drawLine img crossColor (c
.center
.X - 3) c
.center
.Y (c
.center
.X + 3) c
.center
.Y 1
853 drawLine img crossColor c
.center
.X (c
.center
.Y - 3) c
.center
.X (c
.center
.Y + 3) 1
856 let drawCells (img: Image<Bgr, byte>) (drawCellContent
: bool) (cells
: Types.Cell list) =
857 List.iter
(fun c
-> drawCell img drawCellContent
c) cells