/// <summary> /// This performs the union operation on the set which contains the values passed /// This Union is union by size for smaller trees /// Performs path compression upon performing this union becuase it must find the root /// </summary> /// <seealso cref="https://www.youtube.com/watch?v=gcmjC-OcWpI"/> /// <param name="p_labels">up to 4 values whose sets need to be merged</param> /// <returns>The new root of the unioned values</returns> public static ComponentLabel Union(int[] p_labels) { ComponentLabel bigger, smaller; bigger = null; for (int i = 0; i < p_labels.Length - 1; ++i) { for (int j = i + 1; j < p_labels.Length; ++j) { //Get the root of each value smaller = Find(p_labels[j]); bigger = Find(p_labels[i]); if (bigger.m_id != smaller.m_id) { if ((bigger.m_size == smaller.m_size && smaller.m_id < bigger.m_id) || bigger.m_size < smaller.m_size) { //I want to merge smaller sets to larger ones and higher numbers to lesser numbers ComponentLabel temp = smaller; smaller = bigger; bigger = temp; } smaller.m_parent = bigger; bigger.m_size += smaller.m_size; } } } return(bigger); }
/// <summary> /// recursively search the parent structure of the label until it cannot go any further /// </summary> /// <param name="p_label">instantiated label</param> /// <returns>the root of the set that contains this label</returns> public static ComponentLabel Find(ComponentLabel p_label) { if (p_label.m_parent == null) { return(p_label); } else { return(Find(p_label.m_parent)); } }
/// <summary> /// This uses the two pass method to search for blobs /// </summary> /// <seealso cref="https://en.wikipedia.org/wiki/Connected-component_labeling"/> /// <param name="p_data"></param> /// <param name="p_buffer"></param> /// <returns>All blobs found from search</returns> static public Dictionary <int, List <Point> > findBlobs(ref BitmapData p_data, ref byte[] p_buffer) { Dictionary <int, List <Point> > blobs = new Dictionary <int, List <Point> >(); Dictionary <int, int> labelMap = new Dictionary <int, int>(); //Create and populate a binary map int[,] binMap = new int[p_data.Height, p_data.Width]; for (int i = 0; i < p_buffer.Length; i += 4) { if (p_buffer[i] != 0) { binMap[i / p_data.Stride, (i % p_data.Stride) / 4] = 1; } } //curLabel is not instantiated here because the first pixel will never have a point to the left or above it. // Therefore it will be instantiated before I use it. ComponentLabel curLabel = null; #region First Pass HashSet <int> searchSpace = new HashSet <int>(); int min; //iterate through binmap and find foreground pixels for (int y = 1; y < p_data.Height; ++y) { for (int x = 1; x < p_data.Width - 1; ++x) { if (binMap[y, x] == 1) { for (int i = 0; i < 4; ++i) { searchSpace.Add(binMap[y + (i / 3) - 1, x + (i % 3) - 1]); } searchSpace.Remove(0); //remove background pixels searchSpace.Remove(1); //remove first row and last column foreground pixels switch (searchSpace.Count) { case 0: curLabel = ComponentLabel.getInstance(); break; case 1: if (curLabel.Id != searchSpace.First()) { curLabel = ComponentLabel.getInstance(searchSpace.First()); } break; case 4: case 3: case 2: //This is gaurenteed to be two different values from left and top right curLabel = ComponentLabel.Union(searchSpace.ToArray()); break; default: throw new Exception("4 distinct values in blob detection"); } searchSpace.Clear(); binMap[y, x] = curLabel.Id; } } } #endregion #region Second Pass if (curLabel != null) { int root, label; label = curLabel.Id; root = ComponentLabel.Find(label).Id; for (int y = 1; y < p_data.Height; ++y) { for (int x = 1; x < p_data.Width - 1; ++x) { if (binMap[y, x] != 0) { //Store the labels and roots into a map for constant lookup if (binMap[y, x] != label) { label = binMap[y, x]; if (!labelMap.ContainsKey(label)) { labelMap[label] = ComponentLabel.Find(label).Id; } root = labelMap[label]; } //if the blob for this root doesn't exist create it if (!blobs.ContainsKey(root)) { blobs[root] = new List <Point>(); } blobs[root].Add(new Point(x, y)); } } } } #endregion ComponentLabel.dispose(); return(blobs); }