f64b1c3318d687d6dc314fe98f4dbdd125446d6f
5 open System.Collections.Generic
15 // Normalize image values between 0uy and 255uy.
16 let normalizeAndConvert (img
: Image<Gray, 'TDepth>) : Image<Gray, byte> =
17 let min = ref [| 0.0 |]
18 let minLocation = ref <| [| Point() |]
19 let max = ref [| 0.0 |]
20 let maxLocation = ref <| [| Point() |]
21 img.MinMax(min, max, minLocation, maxLocation)
22 ((img.Convert<Gray, float32>() - (!min).[0]) / ((!max).[0] - (!min).[0]) * 255.0).Convert<Gray, byte>()
25 let saveImg (img: Image<'TColor, 'TDepth>) (filepath: string) =
29 let saveMat (mat: Matrix<'TDepth>) (filepath
: string) =
30 use img = new Image<Gray, 'TDeph>(mat.Size)
35 let suppressMConnections (img: Matrix<byte>) =
38 for i in 1 .. h - 2 do
39 for j in 1 .. w - 2 do
40 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)
43 for i in 1 .. h - 2 do
44 for j in 1 .. w - 2 do
45 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)
50 let findEdges (img: Image<Gray, float32>) : Matrix<byte> * Image<Gray, float32> * Image<Gray, float32> =
55 new ConvolutionKernelF(array2D [[ 1.0f; 0.0f; -1.0f ]
57 [ 1.0f; 0.0f; -1.0f ]], Point(1, 1))
59 let xGradient = img.Convolution(sobelKernel)
60 let yGradient = img.Convolution(sobelKernel.Transpose())
62 let xGradientData = xGradient.Data
63 let yGradientData = yGradient.Data
64 for r in 0 .. h - 1 do
65 xGradientData.[r, 0, 0] <- 0.f
66 xGradientData.[r, w - 1, 0] <- 0.f
67 yGradientData.[r, 0, 0] <- 0.f
68 yGradientData.[r, w - 1, 0] <- 0.f
70 for c in 0 .. w - 1 do
71 xGradientData.[0, c, 0] <- 0.f
72 xGradientData.[h - 1, c, 0] <- 0.f
73 yGradientData.[0, c, 0] <- 0.f
74 yGradientData.[h - 1, c, 0] <- 0.f
76 use magnitudes = new Matrix<float32>(xGradient.Size)
77 use angles = new Matrix<float32>(xGradient.Size)
78 CvInvoke.CartToPolar(xGradient, yGradient, magnitudes, angles) // Compute the magnitudes (without angles).
80 let thresholdHigh, thresholdLow =
81 let sensibilityHigh = 0.1f
82 let sensibilityLow = 0.1f
83 use magnitudesByte = magnitudes.Convert<byte>()
84 let threshold = float32 <| CvInvoke.Threshold(magnitudesByte, magnitudesByte, 0.0, 1.0, CvEnum.ThresholdType.Otsu ||| CvEnum.ThresholdType.Binary)
85 threshold + (sensibilityHigh * threshold), threshold - (sensibilityLow * threshold)
87 // Non-maximum suppression.
88 use nms = new Matrix<byte>(xGradient.Size)
90 let nmsData = nms.Data
91 let anglesData = angles.Data
92 let magnitudesData = magnitudes.Data
93 let xGradientData = xGradient.Data
94 let yGradientData = yGradient.Data
96 let PI = float32 Math.PI
98 for i in 0 .. h - 1 do
100 nmsData.[i, w - 1] <- 0uy
102 for j in 0 .. w - 1 do
103 nmsData.[0, j] <- 0uy
104 nmsData.[h - 1, j] <- 0uy
106 for i in 1 .. h - 2 do
107 for j in 1 .. w - 2 do
108 let vx = xGradientData.[i, j, 0]
109 let vy = yGradientData.[i, j, 0]
110 if vx <> 0.f || vy <> 0.f
112 let angle = anglesData.[i, j]
114 let vx', vy' = abs vx, abs vy
115 let ratio2 = if vx' > vy' then vy' / vx' else vx' / vy'
116 let ratio1 = 1.f - ratio2
118 let mNeigbors (sign: int) : float32 =
120 then ratio1 * magnitudesData.[i, j + sign] + ratio2 * magnitudesData.[i + sign, j + sign]
121 elif angle < PI / 2.f
122 then ratio2 * magnitudesData.[i + sign, j + sign] + ratio1 * magnitudesData.[i + sign, j]
123 elif angle < 3.f * PI / 4.f
124 then ratio1 * magnitudesData.[i + sign, j] + ratio2 * magnitudesData.[i + sign, j - sign]
126 then ratio2 * magnitudesData.[i + sign, j - sign] + ratio1 * magnitudesData.[i, j - sign]
127 elif angle < 5.f * PI / 4.f
128 then ratio1 * magnitudesData.[i, j - sign] + ratio2 * magnitudesData.[i - sign, j - sign]
129 elif angle < 3.f * PI / 2.f
130 then ratio2 * magnitudesData.[i - sign, j - sign] + ratio1 * magnitudesData.[i - sign, j]
131 elif angle < 7.f * PI / 4.f
132 then ratio1 * magnitudesData.[i - sign, j] + ratio2 * magnitudesData.[i - sign, j + sign]
133 else ratio2 * magnitudesData.[i - sign, j + sign] + ratio1 * magnitudesData.[i, j + sign]
135 let m = magnitudesData.[i, j]
136 if m >= thresholdLow && m > mNeigbors 1 && m > mNeigbors -1
138 nmsData.[i, j] <- 1uy
140 // suppressMConnections nms // It's
not helpful
for the rest of the process (ellipse
detection).
142 let edges = new Matrix<byte
>(xGradient.Size)
143 let edgesData = edges.Data
145 // Hysteresis thresholding.
146 let toVisit = Stack<Point>()
147 for i
in 0 .. h - 1 do
148 for j
in 0 .. w - 1 do
149 if nmsData.[i
, j
] = 1uy && magnitudesData.[i
, j
] >= thresholdHigh
151 nmsData.[i
, j
] <- 0uy
152 toVisit.Push(Point(j
, i
))
153 while toVisit.Count > 0 do
154 let p = toVisit.Pop()
155 edgesData.[p.Y, p.X] <- 1uy
158 if i
' <> 0 || j' <> 0
162 if ni >= 0 && ni < h && nj >= 0 && nj < w && nmsData.[ni, nj] = 1uy
164 nmsData.[ni, nj] <- 0uy
165 toVisit.Push(Point(nj, ni))
167 edges, xGradient, yGradient
170 let gaussianFilter (img : Image<'TColor, 'TDepth>) (standardDeviation
: float) : Image<'TColor, 'TDepth> =
171 let size = 2 * int (ceil
(4.0 * standardDeviation
)) + 1
172 img.SmoothGaussian(size, size, standardDeviation
, standardDeviation
)
175 type Points = HashSet<Point>
177 let drawPoints (img: Image<Gray, 'TDepth>) (points: Points) (intensity: 'TDepth) =
179 img.Data.[p.Y, p.X, 0] <- intensity
185 let findExtremum (img: Image<Gray, 'TDepth>) (extremumType: ExtremumType) : IEnumerable<Points> =
188 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
190 let imgData = img.Data
191 let suppress: bool[,] = Array2D.zeroCreate h w
193 let result = List<List<Point>>()
195 let flood (start: Point) : List<List<Point>> =
196 let sameLevelToCheck = Stack<Point>()
197 let betterLevelToCheck = Stack<Point>()
198 betterLevelToCheck.Push(start)
200 let result' = List<List<Point>>()
202 while betterLevelToCheck.Count > 0 do
203 let p = betterLevelToCheck.Pop()
204 if not suppress.[p.Y, p.X]
206 suppress.[p.Y, p.X] <- true
207 sameLevelToCheck.Push(p)
208 let current = List<Point>()
210 let mutable betterExists = false
212 while sameLevelToCheck.Count > 0 do
213 let p' = sameLevelToCheck.Pop()
214 let currentLevel = imgData.[p'.Y, p'.X, 0]
215 current.Add(p') |> ignore
219 if ni >= 0 && ni < h && nj >= 0 && nj < w
221 let level = imgData.[ni, nj, 0]
222 let notSuppressed = not suppress.[ni, nj]
224 if level = currentLevel && notSuppressed
226 suppress.[ni, nj] <- true
227 sameLevelToCheck.Push(Point(nj, ni))
228 elif
if extremumType
= ExtremumType.Maxima then level > currentLevel else level < currentLevel
233 betterLevelToCheck.Push(Point(nj, ni))
240 for i
in 0 .. h - 1 do
241 for j in 0 .. w - 1 do
242 let maxima = flood (Point(j, i
))
245 result.AddRange(maxima)
247 result.Select(fun l
-> Points(l
))
250 let findMaxima (img: Image<Gray, 'TDepth>) : IEnumerable<Points> =
251 findExtremum img ExtremumType.Maxima
254 let findMinima (img: Image<Gray, 'TDepth>) : IEnumerable<Points> =
255 findExtremum img ExtremumType.Minima
258 type PriorityQueue () =
260 let q: Points[] = Array.init
size (fun i
-> Points())
261 let mutable highest = -1 // Value of the first elements of 'q'.
262 let mutable lowest = size
264 member this
.NextMax () : byte
* Point =
267 invalidOp
"Queue is empty"
271 l.Remove(next) |> ignore
272 let value = byte
highest
276 highest <- highest - 1
277 while highest > lowest && q.[highest].Count = 0 do
278 highest <- highest - 1
286 member this
.NextMin () : byte
* Point =
289 invalidOp
"Queue is empty"
291 let l = q.[lowest + 1]
293 l.Remove(next) |> ignore
294 let value = byte
(lowest + 1)
299 while lowest < highest && q.[lowest + 1].Count = 0 do
314 member this
.Add (value: byte
) (p: Point) =
324 q.[vi].Add(p) |> ignore
326 member this
.Remove (value: byte
) (p: Point) =
328 if q.[vi].Remove(p) && q.[vi].Count = 0
332 highest <- highest - 1
333 while highest > lowest && q.[highest].Count = 0 do
334 highest <- highest - 1
338 while lowest < highest && q.[lowest + 1].Count = 0 do
341 if highest = lowest // The queue is now empty.
346 member this
.IsEmpty =
349 member this
.Clear () =
350 while highest > lowest do
352 highest <- highest - 1
357 type private AreaState =
362 type private AreaOperation =
367 type private Area (elements
: Points) =
368 member this
.Elements = elements
369 member val Intensity = None with get
, set
370 member val State = AreaState.Unprocessed with get
, set
372 let private areaOperation
(img: Image<Gray, byte
>) (area
: int) (op
: AreaOperation) =
375 let imgData = img.Data
376 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
378 let areas = List<Area>((if op
= AreaOperation.Opening then findMaxima img else findMinima img) |> Seq.map
Area)
380 let pixels: Area[,] = Array2D.create
h w null
382 for e
in m.Elements do
383 pixels.[e
.Y, e
.X] <- m
385 let queue = PriorityQueue()
387 let addEdgeToQueue (elements
: Points) =
392 let p' = Point(nj, ni)
393 if ni >= 0 && ni < h && nj >= 0 && nj < w && not (elements.Contains(p'))
395 queue.Add (imgData.[ni, nj, 0]) p'
397 // Reverse order is quicker.
398 for i in areas.Count - 1 .. -1 .. 0 do
400 if m.Elements.Count <= area && m.State <> AreaState.Removed
403 addEdgeToQueue m.Elements
405 let mutable intensity = if op = AreaOperation.Opening then queue.Max else queue.Min
406 let nextElements = Points()
408 let mutable stop = false
410 let intensity', p = if op
= AreaOperation.Opening then queue.NextMax () else queue.NextMin ()
411 let mutable merged = false
413 if intensity' = intensity // The intensity doesn't
change.
415 if m.Elements.Count + nextElements.Count + 1 > area
417 m.State <- AreaState.Validated
418 m.Intensity <- Some intensity
421 nextElements.Add(p) |> ignore
423 elif
if op
= AreaOperation.Opening then intensity' < intensity else intensity' > intensity
425 m.Elements.UnionWith(nextElements)
426 for e
in nextElements do
427 pixels.[e
.Y, e
.X] <- m
429 if m.Elements.Count = area
431 m.State <- AreaState.Validated
432 m.Intensity <- Some (intensity')
435 intensity <- intensity'
437 nextElements.Add(p) |> ignore
440 let m' = pixels.[p.Y, p.X]
443 if m'.Elements.Count + m.Elements.Count <= area
445 m'.State <- AreaState.Removed
446 for e
in m'.Elements do
447 pixels.[e.Y, e.X] <- m
448 queue.Remove imgData.[e.Y, e.X, 0] e
449 addEdgeToQueue m'.Elements
450 m.Elements.UnionWith(m'.Elements)
451 let intensityMax = if op = AreaOperation.Opening then queue.Max else queue.Min
452 if intensityMax <> intensity
454 intensity <- intensityMax
460 m.State <- AreaState.Validated
461 m.Intensity <- Some (intensity)
464 if not stop && not merged
469 let p' = Point(nj, ni)
470 if ni < 0 || ni >= h || nj < 0 || nj >= w
472 m.State <- AreaState.Validated
473 m.Intensity <- Some (intensity)
475 elif
not (m.Elements.Contains(p')) && not (nextElements.Contains(p'))
477 queue.Add (imgData.[ni, nj, 0]) p'
481 if m.Elements.Count + nextElements.Count <= area
483 m.State <- AreaState.Validated
484 m.Intensity <- Some intensity'
485 m.Elements.UnionWith(nextElements)
489 if m.State = AreaState.Validated
491 match m.Intensity with
493 for p in m.Elements do
494 imgData.[p.Y, p.X, 0] <- i
499 let areaOpen (img: Image<Gray, byte
>) (area
: int) =
500 areaOperation
img area
AreaOperation.Opening
502 let areaClose (img: Image<Gray, byte
>) (area
: int) =
503 areaOperation
img area
AreaOperation.Closing
506 type Island (cmp
: IComparer<float32
>) =
507 member val Shore = Heap.Heap<float32
, Point>(cmp
) with get
508 member val Level = 0.f
with get
, set
509 member val Surface = 0 with get
, set
512 let private areaOperationF
(img: Image<Gray, float32
>) (area
: int) (op
: AreaOperation) =
516 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
518 let comparer = if op
= AreaOperation.Opening
519 then { new IComparer<float32
> with member this
.Compare(v1
, v2
) = v1
.CompareTo(v2
) }
520 else { new IComparer<float32
> with member this
.Compare(v1
, v2
) = v2
.CompareTo(v1
) }
522 let ownership: Island[,] = Array2D.create
h w null
524 // Initialize islands with their shore.
525 let islands = List<Island>()
526 let extremum = img |> if op
= AreaOperation.Opening then findMaxima else findMinima
530 Island(comparer, Level = earth.[p.Y, p.X, 0], Surface = e
.Count)
532 let shorePoints = Points()
534 ownership.[p.Y, p.X] <- island
538 let neighbor = Point(nj, ni)
539 if ni >= 0 && ni < h && nj >= 0 && nj < w && ownership.[ni, nj] = null && not (shorePoints.Contains(neighbor))
541 shorePoints.Add(neighbor) |> ignore
542 island.Shore.Add earth.[ni, nj, 0] neighbor
544 for island in islands do
545 let mutable stop = island.Shore.IsEmpty
547 // 'true' if 'p' is owned or adjacent to 'island'.
548 let ownedOrAdjacent (p: Point) : bool =
549 ownership.[p.Y, p.X] = island ||
550 (p.Y > 0 && ownership.[p.Y - 1, p.X] = island) ||
551 (p.Y < h - 1 && ownership.[p.Y + 1, p.X] = island) ||
552 (p.X > 0 && ownership.[p.Y, p.X - 1] = island) ||
553 (p.X < w - 1 && ownership.[p.Y, p.X + 1] = island)
555 while not stop && island.Surface < area
do
556 let level, next = island.Shore.Max
557 let other = ownership.[next.Y, next.X]
558 if other = island // During merging, some points on the shore may be owned by the island itself -> ignored.
560 island.Shore.RemoveNext ()
563 then // We touching another island.
564 if island.Surface + other.Surface >= area
567 else // We can merge 'other' into 'surface'.
568 island.Surface <- island.Surface + other.Surface
569 island.Level <- if comparer.Compare(island.Level, other.Level) > 0 then island.Level else other.Level
570 for l, p in other.Shore do
571 let mutable currentY = p.Y + 1
572 while currentY < h && ownership.[currentY, p.X] = other do
573 ownership.[currentY, p.X] <- island
574 currentY <- currentY + 1
578 elif
comparer.Compare(level, island.Level) > 0
582 island.Shore.RemoveNext ()
586 if ni < 0 || ni >= h || nj < 0 || nj >= w
588 island.Surface <- Int32.MaxValue
591 let neighbor = Point(nj, ni)
592 if not <| ownedOrAdjacent neighbor
594 island.Shore.Add earth.[ni, nj, 0] neighbor
597 ownership.[next.Y, next.X] <- island
598 island.Level <- level
599 island.Surface <- island.Surface + 1
601 for i
in 0 .. h - 1 do
602 for j in 0 .. w - 1 do
603 let island = ownership.[i
, j]
606 earth.[i
, j, 0] <- island.Level
610 let areaOpenF (img: Image<Gray, float32
>) (area
: int) =
611 areaOperationF
img area
AreaOperation.Opening
613 let areaCloseF (img: Image<Gray, float32
>) (area
: int) =
614 areaOperationF
img area
AreaOperation.Closing
616 // A simpler algorithm than 'areaOpen' but slower.
617 let areaOpen2 (img: Image<Gray, byte
>) (area
: int) =
620 let imgData = img.Data
621 let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |]
623 let histogram = Array.zeroCreate
256
624 for i
in 0 .. h - 1 do
625 for j in 0 .. w - 1 do
626 let v = imgData.[i
, j, 0] |> int
627 histogram.[v] <- histogram.[v] + 1
629 let flooded : bool[,] = Array2D.zeroCreate
h w
631 let pointsChecked = HashSet<Point>()
632 let pointsToCheck = Stack<Point>()
634 for level in 255 .. -1 .. 0 do
635 let mutable n = histogram.[level]
638 for i
in 0 .. h - 1 do
639 for j in 0 .. w - 1 do
640 if not flooded.[i
, j] && imgData.[i
, j, 0] = byte
level
642 let mutable maxNeighborValue = 0uy
643 pointsChecked.Clear()
644 pointsToCheck.Clear()
645 pointsToCheck.Push(Point(j, i
))
647 while pointsToCheck.Count > 0 do
648 let next = pointsToCheck.Pop()
649 pointsChecked.Add(next) |> ignore
650 flooded.[next.Y, next.X] <- true
653 let p = Point(next.X + nx
, next.Y + ny
)
654 if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h
656 let v = imgData.[p.Y, p.X, 0]
659 if not (pointsChecked.Contains(p))
661 pointsToCheck.Push(p)
662 elif
v > maxNeighborValue
664 maxNeighborValue <- v
666 if int maxNeighborValue < level && pointsChecked.Count <= area
668 for p in pointsChecked do
669 imgData.[p.Y, p.X, 0] <- maxNeighborValue
672 // Zhang and Suen algorithm.
673 // Modify 'mat' in place.
674 let thin (mat
: Matrix<byte
>) =
677 let mutable data1 = mat
.Data
678 let mutable data2 = Array2D.copy
data1
680 let mutable pixelChanged = true
681 let mutable oddIteration = true
683 while pixelChanged do
684 pixelChanged <- false
687 if data1.[i
, j] = 1uy
689 let p2 = if i
= 0 then 0uy else data1.[i
-1, j]
690 let p3 = if i
= 0 || j = w-1 then 0uy else data1.[i
-1, j+1]
691 let p4 = if j = w-1 then 0uy else data1.[i
, j+1]
692 let p5 = if i
= h-1 || j = w-1 then 0uy else data1.[i
+1, j+1]
693 let p6 = if i
= h-1 then 0uy else data1.[i
+1, j]
694 let p7 = if i
= h-1 || j = 0 then 0uy else data1.[i
+1, j-1]
695 let p8 = if j = 0 then 0uy else data1.[i
, j-1]
696 let p9 = if i
= 0 || j = 0 then 0uy else data1.[i
-1, j-1]
698 let sumNeighbors = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9
699 if sumNeighbors >= 2uy && sumNeighbors <= 6uy &&
700 (if p2 = 0uy && p3 = 1uy then 1 else 0) +
701 (if p3 = 0uy && p4 = 1uy then 1 else 0) +
702 (if p4 = 0uy && p5 = 1uy then 1 else 0) +
703 (if p5 = 0uy && p6 = 1uy then 1 else 0) +
704 (if p6 = 0uy && p7 = 1uy then 1 else 0) +
705 (if p7 = 0uy && p8 = 1uy then 1 else 0) +
706 (if p8 = 0uy && p9 = 1uy then 1 else 0) +
707 (if p9 = 0uy && p2 = 1uy then 1 else 0) = 1 &&
709 then p2 * p4 * p6 = 0uy && p4 * p6 * p8 = 0uy
710 else p2 * p4 * p8 = 0uy && p2 * p6 * p8 = 0uy
717 oddIteration <- not oddIteration
723 // Remove all 8-connected pixels with an area equal or greater than 'areaSize'.
724 // Modify 'mat' in place.
725 let removeArea (mat
: Matrix<byte
>) (areaSize
: int) =
736 use mat' = new Matrix<byte>(mat.Size)
742 let data' = mat'.Data
746 if data'.[i, j] = 1uy
748 let neighborhood = List<Point>()
749 let neighborsToCheck = Stack<Point>()
750 neighborsToCheck.Push(Point(j, i))
753 while neighborsToCheck.Count > 0 do
754 let n = neighborsToCheck.Pop()
756 for (ni, nj) in neighbors do
759 if pi >= 0 && pi < h && pj >= 0 && pj < w && data'.[pi, pj] = 1uy
761 neighborsToCheck.Push(Point(pj, pi))
762 data'.[pi, pj] <- 0uy
763 if neighborhood.Count <= areaSize
765 for n in neighborhood do
766 data.[n.Y, n.X] <- 0uy
768 let connectedComponents (img: Image<Gray, byte
>) (startPoints
: List<Point>) : List<Point> =
772 let pointChecked = Points()
773 let pointToCheck = Stack<Point>(startPoints
);
777 while pointToCheck.Count > 0 do
778 let next = pointToCheck.Pop()
779 pointChecked.Add(next) |> ignore
782 if ny
<> 0 && nx
<> 0
784 let p = Point(next.X + nx
, next.Y + ny
)
785 if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h && data.[p.Y, p.X, 0] > 0uy && not (pointChecked.Contains p)
789 List<Point>(pointChecked)
792 let drawLine (img: Image<'TColor, 'TDepth>) (color
: 'TColor) (x0: int) (y0: int) (x1: int) (y1: int) (thickness: int) =
793 img.Draw(LineSegment2D(Point(x0, y0), Point(x1, y1)), color, thickness);
796 let drawLineF (img: Image<'TColor, 'TDepth>) (color: 'TColor) (x0
: float) (y0
: float) (x1
: float) (y1
: float) (thickness
: int) =
797 img.Draw(LineSegment2DF(PointF(float32 x0
, float32 y0
), PointF(float32 x1
, float32 y1
)), color
, thickness
, CvEnum.LineType.AntiAlias);
800 let drawEllipse (img: Image<'TColor, 'TDepth>) (e
: Types.Ellipse) (color
: 'TColor) (alpha: float) =
804 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)
806 let windowPosX = e.Cx - e.A - 5.f
807 let gapX = windowPosX - (float32 (int windowPosX))
809 let windowPosY = e.Cy - e.A - 5.f
810 let gapY = windowPosY - (float32 (int windowPosY))
812 let roi = Rectangle(int windowPosX, int windowPosY, 2.f * (e.A + 5.f) |> int, 2.f * (e.A + 5.f) |> int)
815 if roi = img.ROI // We do not display ellipses touching the edges (FIXME)
817 use i = new Image<'TColor, 'TDepth>(img.ROI.Size)
818 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)
819 CvInvoke.AddWeighted(img, 1.0, i, alpha, 0.0, img)
820 img.ROI <- Rectangle.Empty
823 let drawEllipses (img: Image<'TColor, 'TDepth>) (ellipses: Types.Ellipse list) (color: 'TColor) (alpha
: float) =
824 List.iter
(fun e
-> drawEllipse img e
color alpha
) ellipses
827 let rngCell = System.Random()
828 let drawCell (img: Image<Bgr, byte
>) (drawCellContent
: bool) (c
: Types.Cell) =
831 let colorB = rngCell.Next(20, 70)
832 let colorG = rngCell.Next(20, 70)
833 let colorR = rngCell.Next(20, 70)
835 for y
in 0 .. c
.elements
.Height - 1 do
836 for x
in 0 .. c
.elements
.Width - 1 do
837 if c
.elements
.[y
, x
] > 0uy
839 let dx, dy
= c
.center
.X - c
.elements
.Width / 2, c
.center
.Y - c
.elements
.Height / 2
840 let b = img.Data.[y
+ dy
, x
+ dx, 0] |> int
841 let g = img.Data.[y
+ dy
, x
+ dx, 1] |> int
842 let r = img.Data.[y
+ dy
, x
+ dx, 2] |> int
843 img.Data.[y
+ dy
, x
+ dx, 0] <- if b + colorB > 255 then 255uy else byte (b + colorB)
844 img.Data.[y
+ dy
, x
+ dx, 1] <- if g + colorG > 255 then 255uy else byte (g + colorG)
845 img.Data.[y
+ dy
, x
+ dx, 2] <- if r + colorR > 255 then 255uy else byte (r + colorR)
847 let crossColor, crossColor2
=
848 match c
.cellClass
with
849 | Types.HealthyRBC -> Bgr(255., 0., 0.), Bgr(255., 255., 255.)
850 | Types.InfectedRBC -> Bgr(0., 0., 255.), Bgr(120., 120., 255.)
851 | Types.Peculiar -> Bgr(0., 0., 0.), Bgr(80., 80., 80.)
853 drawLine img crossColor2
(c
.center
.X - 3) c
.center
.Y (c
.center
.X + 3) c
.center
.Y 2
854 drawLine img crossColor2 c
.center
.X (c
.center
.Y - 3) c
.center
.X (c
.center
.Y + 3) 2
856 drawLine img crossColor (c
.center
.X - 3) c
.center
.Y (c
.center
.X + 3) c
.center
.Y 1
857 drawLine img crossColor c
.center
.X (c
.center
.Y - 3) c
.center
.X (c
.center
.Y + 3) 1
860 let drawCells (img: Image<Bgr, byte>) (drawCellContent
: bool) (cells
: Types.Cell list) =
861 List.iter
(fun c
-> drawCell img drawCellContent
c) cells