let h = img.Height
for i in 1 .. h - 2 do
for j in 1 .. w - 2 do
- 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) then
+ 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)
+ then
img.[i, j] <- 0uy
for i in 1 .. h - 2 do
for j in 1 .. w - 2 do
- 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) then
+ 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)
+ then
img.[i, j] <- 0uy
let findEdges (img: Image<Gray, float32>) : Matrix<byte> * Image<Gray, float> * Image<Gray, float> =
else magnitudes.Data.[i - sign, j + sign]
let m = magnitudes.Data.[i, j]
- if m < mNeigbors 1 || m < mNeigbors -1 || m < thresholdLow then
+ if m < mNeigbors 1 || m < mNeigbors -1 || m < thresholdLow
+ then
nms.Data.[i, j] <- 0uy
// suppressMConnections nms // It's not usefull for the rest of the process (ellipse detection).
let toVisit = Stack<Point>()
for i in 0 .. h - 1 do
for j in 0 .. w - 1 do
- if nms.Data.[i, j] = 1uy && magnitudes.Data.[i, j] >= thresholdHigh then
+ if nms.Data.[i, j] = 1uy && magnitudes.Data.[i, j] >= thresholdHigh
+ then
nms.Data.[i, j] <- 0uy
toVisit.Push(Point(j, i))
while toVisit.Count > 0 do
edges.Data.[p.Y, p.X] <- 1uy
for i' in -1 .. 1 do
for j' in -1 .. 1 do
- if i' <> 0 || j' <> 0 then
+ if i' <> 0 || j' <> 0
+ then
let ni = p.Y + i'
let nj = p.X + j'
- if ni >= 0 && ni < h && nj >= 0 && nj < w && nms.Data.[ni, nj] = 1uy then
+ if ni >= 0 && ni < h && nj >= 0 && nj < w && nms.Data.[ni, nj] = 1uy
+ then
nms.Data.[ni, nj] <- 0uy
toVisit.Push(Point(nj, ni))
data2 <- tmp
-// FIXME: replace by a queue or stack.
-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) =
for j in 0..w-1 do
if data'.[i, j] = 1uy
then
- let neighborhood = List<(int*int)>()
- let neighborsToCheck = List<(int*int)>()
- neighborsToCheck.Add((i, j))
+ let neighborhood = List<Point>()
+ let neighborsToCheck = Stack<Point>()
+ neighborsToCheck.Push(Point(j, i))
data'.[i, j] <- 0uy
while neighborsToCheck.Count > 0 do
- let (ci, cj) = pop neighborsToCheck
- neighborhood.Add((ci, cj))
+ let n = neighborsToCheck.Pop()
+ neighborhood.Add(n)
for (ni, nj) in neighbors do
- let pi = ci + ni
- let pj = cj + nj
+ let pi = n.Y + ni
+ let pj = n.X + nj
if pi >= 0 && pi < h && pj >= 0 && pj < w && data'.[pi, pj] = 1uy
then
- neighborsToCheck.Add((pi, pj))
+ neighborsToCheck.Push(Point(pj, pi))
data'.[pi, pj] <- 0uy
if neighborhood.Count <= areaSize
then
- for (ni, nj) in neighborhood do
- data.[ni, nj] <- 0uy
+ for n in neighborhood do
+ data.[n.Y, n.X] <- 0uy
let connectedComponents (img: Image<Gray, byte>) (startPoints: List<Point>) : List<Point> =
let w = img.Width
let h = img.Height
let pointChecked = Points()
- let pointToCheck = List<Point>(startPoints);
+ let pointToCheck = Stack<Point>(startPoints);
let data = img.Data
while pointToCheck.Count > 0 do
- let next = pop pointToCheck
+ let next = pointToCheck.Pop()
pointChecked.Add(next) |> ignore
for ny in -1 .. 1 do
for nx in -1 .. 1 do
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)
+ pointToCheck.Push(p)
List<Point>(pointChecked)
--- /dev/null
+module KMeans
+
+open System.Collections.Generic
+open System.Drawing
+
+open Emgu.CV
+open Emgu.CV.Structure
+
+type Result = {
+ fg: Image<Gray, byte>
+ mean_bg: float
+ mean_fg: float
+ d_fg: Image<Gray, float32> } // Euclidean distances of the foreground to mean_fg.
+
+let kmeans (img: Image<Gray, float32>) (fgFactor: float) : Result =
+ let nbIteration = 3
+ let w = img.Width
+ let h = img.Height
+
+ let min = ref [| 0.0 |]
+ let minLocation = ref <| [| Point() |]
+ let max = ref [| 0.0 |]
+ let maxLocation = ref <| [| Point() |]
+ img.MinMax(min, max, minLocation, maxLocation)
+
+ let mutable mean_bg = (!max).[0] - ((!max).[0] - (!min).[0]) / 4.0
+ let mutable mean_fg = (!min).[0] + ((!max).[0] - (!min).[0]) / 4.0
+ use mutable d_bg = new Image<Gray, float32>(img.Size)
+ let mutable d_fg = new Image<Gray, float32>(img.Size)
+ let mutable fg = new Image<Gray, byte>(img.Size)
+
+ for i in 1 .. nbIteration do
+ d_bg <- img.AbsDiff(Gray(mean_bg))
+ d_fg <- img.AbsDiff(Gray(mean_fg))
+
+ CvInvoke.Compare(d_fg, d_bg, fg, CvEnum.CmpType.LessThan)
+
+ let mutable bg_total = 0.0
+ let mutable bg_nb = 0
+
+ let mutable fg_total = 0.0
+ let mutable fg_nb = 0
+
+ for i in 0 .. h - 1 do
+ for j in 0 .. w - 1 do
+ if fg.Data.[i, j, 0] > 0uy
+ then
+ fg_total <- fg_total + float img.Data.[i, j, 0]
+ fg_nb <- fg_nb + 1
+ else
+ bg_total <- bg_total + float img.Data.[i, j, 0]
+ bg_nb <- bg_nb + 1
+
+ mean_bg <- bg_total / float bg_nb
+ mean_fg <- fg_total / float fg_nb
+
+ { fg = fg; mean_bg = mean_bg; mean_fg = mean_fg; d_fg = d_fg }
\ No newline at end of file
ImgTools.areaClose filteredGreenWithoutStain (int config.StainArea)
// We use the filtered image to find the dark stain.
- let kmediansResults = logTime "Finding fg/bg (k-medians)" (fun () -> KMedians.kmedians (filteredGreenWithoutInfection.Convert<Gray, float32>()) 1.0) // FIXME: avoid converting again this in MainAnalysis
- let { KMedians.fg = fg; KMedians.median_bg = median_bg; KMedians.median_fg = median_fg; KMedians.d_fg = d_fg } = kmediansResults
- let darkStain = d_fg.Cmp(median_bg * config.Parameters.darkStainLevel, CvEnum.CmpType.GreaterThan)
- darkStain._And(filteredGreenWithoutInfection.Cmp(median_fg, CvEnum.CmpType.LessThan))
+
+ // With K-Means.
+ let kmeansResults = logTime "Finding fg/bg (k-means)" (fun () -> KMeans.kmeans (filteredGreenWithoutInfection.Convert<Gray, float32>()) 1.0)
+ let { KMeans.mean_bg = value_bg; KMeans.mean_fg = value_fg; KMeans.d_fg = d_fg } = kmeansResults
+
+ // With K-Medians.
+ (* let kmediansResults = logTime "Finding fg/bg (k-medians)" (fun () -> KMedians.kmedians (filteredGreenWithoutInfection.Convert<Gray, float32>()) 1.0) // FIXME: avoid converting this again in MainAnalysis
+ let { KMedians.median_bg = value_bg; KMedians.median_fg = value_fg; KMedians.d_fg = d_fg } = kmediansResults *)
+
+ let darkStain = d_fg.Cmp(value_bg * config.Parameters.darkStainLevel, CvEnum.CmpType.GreaterThan)
+ darkStain._And(filteredGreenWithoutInfection.Cmp(value_fg, CvEnum.CmpType.LessThan))
let marker (img: Image<Gray, byte>) (closed: Image<Gray, byte>) (level: float) : Image<Gray, byte> =
let diff = closed - (img * level)