// geodesic dilation from markers public static int[,] Reconstruction(int[,] markers, int[,] mask) { int[,] result = new int[markers.GetLength(0), markers.GetLength(1)]; DMaxHeap <Pixel> heap = new DMaxHeap <Pixel>(5); // add markers to heap for (int x = 0; x < markers.GetLength(0); x++) { for (int y = 0; y < markers.GetLength(1); y++) { if (markers[x, y] > 0 && mask[x, y] > 0) { Pixel p = new Pixel(x, y, Math.Min(markers[x, y], mask[x, y])); heap.Add(p); } result[x, y] = 0; } } // process heap in order of value while (heap.Count > 0) { Pixel p = heap.Extract(); if (p.Value <= result[p.X, p.Y]) // not a higher value than already determined { continue; } result[p.X, p.Y] = p.Value; // add neighbours for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { int pxx = p.X + i, pxy = p.Y + j; if (pxx < 0 || pxx >= markers.GetLength(0) || pxy < 0 || pxy >= markers.GetLength(1)) // no pixel { continue; } int val = Math.Min(mask[pxx, pxy], p.Value); if (val == result[pxx, pxy]) // no loops of pixels adding each other { continue; } heap.Add(new Pixel(pxx, pxy, val)); } } } return(result); }
// separate objects that are touching public static bool[,] Watershed(int[,] image, decimal threshold) { bool[,] shed = new bool[image.GetLength(0), image.GetLength(1)]; // watershed mask int[,] dt = DistanceTransform(image); // apply distance transform int[,] dt2 = new int[dt.GetLength(0), dt.GetLength(1)]; // find global maximum int max = int.MinValue; for (int i = 0; i < dt.GetLength(0); i++) { for (int j = 0; j < dt.GetLength(1); j++) { max = Math.Max(max, dt[i, j]); } } MPixel[,] pixels = new MPixel[dt.GetLength(0), dt.GetLength(1)]; // pixel object for each position for (int i = 0; i < dt.GetLength(0); i++) { for (int j = 0; j < dt.GetLength(1); j++) { dt2[i, j] = dt[i, j] <= threshold * max ? 0 : dt[i, j]; // thresholded distance transform by parameter } } List <Tuple <int, int> > maxima = GetLocalMaxima(dt2); // find maxima of thresholded dt DMaxHeap <MPixel> heap = new DMaxHeap <MPixel>(); int label = 1; foreach (Tuple <int, int> t in maxima) // start at maxima { MPixel p = new MPixel(t.Item1, t.Item2, label++, dt[t.Item1, t.Item2]); heap.Add(p); pixels[p.X, p.Y] = p; } while (heap.Count > 0) { MPixel p = heap.Extract(); int[] labels = new int[4]; // labels of neighbours if (p.X > 0 && pixels[p.X - 1, p.Y] != null) { labels[0] = pixels[p.X - 1, p.Y].Label; } if (p.Y > 0 && pixels[p.X, p.Y - 1] != null) { labels[1] = pixels[p.X, p.Y - 1].Label; } if (p.X < pixels.GetLength(0) - 1 && pixels[p.X + 1, p.Y] != null) { labels[2] = pixels[p.X + 1, p.Y].Label; } if (p.Y < pixels.GetLength(1) - 1 && pixels[p.X, p.Y + 1] != null) { labels[3] = pixels[p.X, p.Y + 1].Label; } Array.Sort(labels); int i = 0; while (i < 4 && labels[i] == 0) // first neighbour label { i++; } bool same = true; for (; i < 3; i++) { if (labels[i] != labels[i + 1]) // neighbours with different labels { same = false; } } if (same && p.Label == 0) // all neighbour labels the same and current pixel's label not yet set { p.Label = labels[3]; // give the same label as neighbours } // add all unprocessed neighbours MPixel newPixel; if (p.X > 0 && pixels[p.X - 1, p.Y] == null) { newPixel = new MPixel(p.X - 1, p.Y, 0, dt[p.X - 1, p.Y]); heap.Add(newPixel); pixels[p.X - 1, p.Y] = newPixel; } if (p.Y > 0 && pixels[p.X, p.Y - 1] == null) { newPixel = new MPixel(p.X, p.Y - 1, 0, dt[p.X, p.Y - 1]); heap.Add(newPixel); pixels[p.X, p.Y - 1] = newPixel; } if (p.X < pixels.GetLength(0) - 1 && pixels[p.X + 1, p.Y] == null) { newPixel = new MPixel(p.X + 1, p.Y, 0, dt[p.X + 1, p.Y]); heap.Add(newPixel); pixels[p.X + 1, p.Y] = newPixel; } if (p.Y < pixels.GetLength(1) - 1 && pixels[p.X, p.Y + 1] == null) { newPixel = new MPixel(p.X, p.Y + 1, 0, dt[p.X, p.Y + 1]); heap.Add(newPixel); pixels[p.X, p.Y + 1] = newPixel; } } // create mask from labels for (int x = 0; x < shed.GetLength(0); x++) { for (int y = 0; y < shed.GetLength(1); y++) { shed[x, y] = pixels[x, y] == null || pixels[x, y].Label != 0; } } return(shed); }