private SortedSet <int> MergeNeighbors(int i, int j) { PixelCluster a = clusters[i]; PixelCluster b = clusters[j]; SortedSet <int> merged = Simplify(a.neighbors); merged.UnionWith(Simplify(b.neighbors)); merged.Remove(a.id); merged.Remove(b.id); return(merged); }
//follow the parent pointer until we reach the root private int Find(int id) { PixelCluster p = clusters[id]; if (p.id == p.parentId) { return(p.id); } int parentId = p.parentId; while (parentId != clusters[parentId].parentId) { parentId = clusters[parentId].parentId; } //compress the path p.parentId = parentId; return(parentId); }
//Cluster the final clusters into color space public void ClusterColorSpace() { double maxDist = 20 * 20; int minRegions = 5; SortedSet <int> activeClusterIds = new SortedSet <int>(rootIds); String logFile = "colorlog.txt"; StreamWriter log = File.AppendText(logFile); log.WriteLine("\n\tCluster ColorSpace Run " + DateTime.Now.ToString()); log.Flush(); //the smaller id comes first in the dictionary for pairwise distances PriorityQueue <Tuple <int, int>, double> pq = new PriorityQueue <Tuple <int, int>, double>(); int counter = activeClusterIds.Last() + 1; int[] ids = activeClusterIds.ToArray <int>(); //Calculate the initial distances for (int i = 0; i < ids.Count(); i++) { for (int j = i + 1; j < ids.Count(); j++) { //log.WriteLine(ids[i] + ", " + ids[j] + " dist " + -1 * clusters[ids[i]].lab.SqDist(clusters[ids[j]].lab)); //log.Flush(); //pq.Enqueue(new Tuple<int, int>(ids[i], ids[j]), -1 * clusters[ids[i]].lab.SqDist(clusters[ids[j]].lab)); PixelCluster a = clusters[ids[i]]; PixelCluster b = clusters[ids[j]]; double newDist = a.lab.SqDist(b.lab); //Add in Ward's variance (variation in Color Segmentation using Region Merging) //http://www.mathworks.com/help/toolbox/stats/linkage.html //newDist = newDist * Math.Sqrt(2 * a.count * b.count / (a.count + b.count)); pq.Enqueue(new Tuple <int, int>(ids[i], ids[j]), -1 * newDist); } } Stopwatch timer = new Stopwatch(); timer.Start(); while (activeClusterIds.Count > minRegions) { //Find the pair with the smallest distance KeyValuePair <Tuple <int, int>, double> result = BestPair(pq, activeClusterIds); Tuple <int, int> pair = result.Key; double bestDist = -1 * result.Value; Console.WriteLine("num clusters: " + activeClusterIds.Count()); if (bestDist > maxDist) { break; } PixelCluster a = clusters[pair.Item1]; PixelCluster b = clusters[pair.Item2]; //Create a new cluster with unique id, we don't care about the neighbors PixelCluster merged = new PixelCluster(); merged.id = counter++; merged.lab = (a.lab * a.count + b.lab * b.count) / (a.count + b.count); merged.count = a.count + b.count; merged.children = new int[] { a.id, b.id }; merged.parentId = merged.id; a.parentId = merged.id; b.parentId = merged.id; clusters.Add(merged.id, merged); //Update the active cluster set activeClusterIds.Remove(a.id); activeClusterIds.Remove(b.id); activeClusterIds.Add(merged.id); double totalCount = a.count + b.count; //Update the distances, based on old distances foreach (int i in activeClusterIds) { if (i == merged.id) { continue; } //TODO: Ward's method with minimum variance //For now, just use the dist between the centroids PixelCluster c = clusters[i]; double newDist = merged.lab.SqDist(c.lab); //Add in Ward's variance (variation in Color Segmentation using Region Merging) //http://www.mathworks.com/help/toolbox/stats/linkage.html //newDist = newDist * Math.Sqrt(2*a.count * b.count / (a.count + b.count)); if (c.id < merged.id) { pq.Enqueue(new Tuple <int, int>(c.id, merged.id), -1 * newDist); } else { pq.Enqueue(new Tuple <int, int>(merged.id, c.id), -1 * newDist); } } //Remove the old clusters foreach (int i in activeClusterIds) { if (i > a.id) { pq.Remove(new Tuple <int, int>(a.id, i)); } else { pq.Remove(new Tuple <int, int>(i, a.id)); } } foreach (int i in activeClusterIds) { if (i > b.id) { pq.Remove(new Tuple <int, int>(b.id, i)); } else { pq.Remove(new Tuple <int, int>(i, b.id)); } } //Repeat until termination criteria if (activeClusterIds.Count() % 1000 == 0) { //write to file log.WriteLine("Merge loop: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count + " merged # neighbors: " + merged.neighbors.Count); log.Flush(); timer.Restart(); } } rootIds = activeClusterIds; timer.Stop(); log.WriteLine("End: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count); log.WriteLine("End Time: " + DateTime.Now.ToString()); log.Flush(); log.Close(); }
/** * Now the Agglomerative Clustering. May want to pass in additional parameters, like the termination * criteria and parameters. * Assumes list of colors are in row-order **/ public void Cluster(List <CIELAB> colors, int width, int height) { double maxDist = 50 * 10; SortedSet <int> activeClusterIds = new SortedSet <int>(); String logFile = "log.txt"; StreamWriter log = File.AppendText(logFile); log.WriteLine("\n\tCluster Spatial Run " + DateTime.Now.ToString()); log.Flush(); //the smaller id comes first in the dictionary for pairwise distances PriorityQueue <Tuple <int, int>, double> pq = new PriorityQueue <Tuple <int, int>, double>(); clusters = new Dictionary <int, PixelCluster>(); int counter = 0; //Initialize the clusters in row-order for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { activeClusterIds.Add(counter); PixelCluster p = new PixelCluster(counter, colors[width * j + i]); counter++; //Initialize the 4-neighbors if (i > 0) { p.neighbors.Add(ToIndex(i - 1, j, width)); } if (j > 0) { p.neighbors.Add(ToIndex(i, j - 1, width)); } if (i < width - 1) { p.neighbors.Add(ToIndex(i + 1, j, width)); } if (j < height - 1) { p.neighbors.Add(ToIndex(i, j + 1, width)); } clusters.Add(p.id, p); } } foreach (int i in activeClusterIds) { //calculate distances to neighbors larger than current id SortedSet <int> neighbors = Simplify(clusters[i].neighbors); foreach (int j in neighbors) { if (i < j) { pq.Enqueue(new Tuple <int, int>(i, j), -1 * clusters[i].lab.SqDist(clusters[j].lab)); } } } Stopwatch timer = new Stopwatch(); timer.Start(); while (activeClusterIds.Count > 1) { //Find the pair with the smallest distance KeyValuePair <Tuple <int, int>, double> result = BestPair(pq, activeClusterIds); Tuple <int, int> pair = result.Key; double bestDist = -1 * result.Value; Console.WriteLine("num clusters: " + activeClusterIds.Count()); if (bestDist > maxDist) { break; } PixelCluster a = clusters[pair.Item1]; PixelCluster b = clusters[pair.Item2]; //Create a new cluster with unique id PixelCluster merged = new PixelCluster(); merged.id = counter++; merged.lab = (a.lab * a.count + b.lab * b.count) / (a.count + b.count); merged.count = a.count + b.count; merged.children = new int[] { a.id, b.id }; merged.neighbors = MergeNeighbors(a.id, b.id); merged.parentId = merged.id; a.parentId = merged.id; b.parentId = merged.id; clusters.Add(merged.id, merged); //Update the active cluster set activeClusterIds.Remove(a.id); activeClusterIds.Remove(b.id); activeClusterIds.Add(merged.id); double totalCount = a.count + b.count; //Update the distances, based on old distances foreach (int i in merged.neighbors) { //Debug.Assert(i != merged.id && activeClusterIds.Contains(i)); //TODO: Ward's method with minimum variance //For now, just use the dist between the centroids PixelCluster c = clusters[i]; double newDist = merged.lab.SqDist(c.lab); //Add in Ward's variance (variation in Color Segmentation using Region Merging) //http://www.mathworks.com/help/toolbox/stats/linkage.html newDist = newDist * Math.Sqrt(2 * a.count * b.count / (a.count + b.count)); if (c.id < merged.id) { pq.Enqueue(new Tuple <int, int>(c.id, merged.id), -1 * newDist); } else { pq.Enqueue(new Tuple <int, int>(merged.id, c.id), -1 * newDist); } } //Remove the old clusters foreach (int i in a.neighbors) { if (i > a.id) { pq.Remove(new Tuple <int, int>(a.id, i)); } else { pq.Remove(new Tuple <int, int>(i, a.id)); } } foreach (int i in b.neighbors) { if (i > b.id) { pq.Remove(new Tuple <int, int>(b.id, i)); } else { pq.Remove(new Tuple <int, int>(i, b.id)); } } //Repeat until termination criteria if (activeClusterIds.Count() % 1000 == 0) { //write to file log.WriteLine("Merge loop: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count + " merged # neighbors: " + merged.neighbors.Count); log.Flush(); timer.Restart(); } } rootIds = activeClusterIds; timer.Stop(); log.WriteLine("End: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count); log.WriteLine("End Time: " + DateTime.Now.ToString()); log.Flush(); log.Close(); }
public void ClusterColors(List <CIELAB> colors, int width, int height) { //Bin the colors int minRegions = 5; double maxDist = 10 * 10; SortedSet <int> activeClusterIds = new SortedSet <int>(); String logFile = "log-colorspace.txt"; StreamWriter log = File.AppendText(logFile); log.WriteLine("\n\tCluster Color Space " + DateTime.Now.ToString()); log.Flush(); //the smaller id comes first in the dictionary for pairwise distances PriorityQueue <Tuple <int, int>, double> pq = new PriorityQueue <Tuple <int, int>, double>(); clusters = new Dictionary <int, PixelCluster>(); int counter = 0; foreach (CIELAB color in colors) { //bin it into one of the clusters //index is a first, then b, then L int id = GetBinId(color); if (id > counter) { counter = id; } if (!clusters.ContainsKey(id)) { clusters.Add(id, new PixelCluster(id, color)); } else { clusters[id].lab = (clusters[id].lab * clusters[id].count + color) / (clusters[id].count + 1); clusters[id].count++; } } counter++; activeClusterIds = new SortedSet <int>(clusters.Keys); List <int> ids = activeClusterIds.ToList <int>(); for (int i = 0; i < ids.Count(); i++) { PixelCluster a = clusters[ids[i]]; //calculate distances to neighbors larger than current id for (int j = i + 1; j < ids.Count(); j++) { PixelCluster b = clusters[ids[j]]; double newDist = a.lab.SqDist(b.lab); //newDist = newDist * Math.Sqrt(2 * a.count * b.count / (a.count + b.count)); pq.Enqueue(new Tuple <int, int>(a.id, b.id), -1 * newDist); } } Stopwatch timer = new Stopwatch(); timer.Start(); while (activeClusterIds.Count > minRegions) { //Find the pair with the smallest distance KeyValuePair <Tuple <int, int>, double> result = BestPair(pq, activeClusterIds); Tuple <int, int> pair = result.Key; double bestDist = -1 * result.Value; Console.WriteLine("num clusters: " + activeClusterIds.Count()); if (bestDist > maxDist) { break; } PixelCluster a = clusters[pair.Item1]; PixelCluster b = clusters[pair.Item2]; //Create a new cluster with unique id, don't care about neighbors PixelCluster merged = new PixelCluster(); merged.id = counter++; merged.lab = (a.lab * a.count + b.lab * b.count) / (a.count + b.count); merged.count = a.count + b.count; merged.children = new int[] { a.id, b.id }; merged.parentId = merged.id; a.parentId = merged.id; b.parentId = merged.id; clusters.Add(merged.id, merged); //Update the active cluster set activeClusterIds.Remove(a.id); activeClusterIds.Remove(b.id); activeClusterIds.Add(merged.id); double totalCount = a.count + b.count; //Update the distances, based on old distances foreach (int i in activeClusterIds) { //Debug.Assert(i != merged.id && activeClusterIds.Contains(i)); //TODO: Ward's method with minimum variance //For now, just use the dist between the centroids if (i == merged.id) { continue; } PixelCluster c = clusters[i]; double newDist = merged.lab.SqDist(c.lab); //Add in Ward's variance (variation in Color Segmentation using Region Merging) //http://www.mathworks.com/help/toolbox/stats/linkage.html //newDist = newDist * Math.Sqrt(2 * a.count * b.count / (a.count + b.count)); if (c.id < merged.id) { pq.Enqueue(new Tuple <int, int>(c.id, merged.id), -1 * newDist); } else { pq.Enqueue(new Tuple <int, int>(merged.id, c.id), -1 * newDist); } } //Remove the old clusters foreach (int i in a.neighbors) { if (i > a.id) { pq.Remove(new Tuple <int, int>(a.id, i)); } else { pq.Remove(new Tuple <int, int>(i, a.id)); } } foreach (int i in b.neighbors) { if (i > b.id) { pq.Remove(new Tuple <int, int>(b.id, i)); } else { pq.Remove(new Tuple <int, int>(i, b.id)); } } //Repeat until termination criteria if (activeClusterIds.Count() % 1000 == 0) { //write to file log.WriteLine("Merge loop: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count + " merged # neighbors: " + merged.neighbors.Count); log.Flush(); timer.Restart(); } } rootIds = activeClusterIds; timer.Stop(); log.WriteLine("End: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count); log.WriteLine("End Time: " + DateTime.Now.ToString()); log.Flush(); log.Close(); }
/** * Now the Agglomerative Clustering. May want to pass in additional parameters, like the termination * criteria and parameters. * Assumes list of colors are in row-order **/ public void Cluster(List<CIELAB> colors, int width, int height) { double maxDist = 50*10; SortedSet<int> activeClusterIds = new SortedSet<int>(); String logFile = "log.txt"; StreamWriter log = File.AppendText(logFile); log.WriteLine("\n\tCluster Spatial Run " + DateTime.Now.ToString()); log.Flush(); //the smaller id comes first in the dictionary for pairwise distances PriorityQueue<Tuple<int, int>, double> pq = new PriorityQueue<Tuple<int, int>, double>(); clusters = new Dictionary<int, PixelCluster>(); int counter = 0; //Initialize the clusters in row-order for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { activeClusterIds.Add(counter); PixelCluster p = new PixelCluster(counter, colors[width * j + i]); counter++; //Initialize the 4-neighbors if (i > 0) p.neighbors.Add(ToIndex(i - 1, j, width)); if (j > 0) p.neighbors.Add(ToIndex(i, j - 1, width)); if (i < width - 1) p.neighbors.Add(ToIndex(i + 1, j, width)); if (j < height - 1) p.neighbors.Add(ToIndex(i, j + 1, width)); clusters.Add(p.id, p); } } foreach (int i in activeClusterIds) { //calculate distances to neighbors larger than current id SortedSet<int> neighbors = Simplify(clusters[i].neighbors); foreach (int j in neighbors) { if (i < j) { pq.Enqueue(new Tuple<int, int>(i, j), -1*clusters[i].lab.SqDist(clusters[j].lab)); } } } Stopwatch timer = new Stopwatch(); timer.Start(); while (activeClusterIds.Count > 1) { //Find the pair with the smallest distance KeyValuePair<Tuple<int, int>, double> result = BestPair(pq, activeClusterIds); Tuple<int, int> pair = result.Key; double bestDist = -1*result.Value; Console.WriteLine("num clusters: " + activeClusterIds.Count()); if (bestDist > maxDist) break; PixelCluster a = clusters[pair.Item1]; PixelCluster b = clusters[pair.Item2]; //Create a new cluster with unique id PixelCluster merged = new PixelCluster(); merged.id = counter++; merged.lab = (a.lab * a.count + b.lab * b.count) / (a.count + b.count); merged.count = a.count + b.count; merged.children = new int[] { a.id, b.id }; merged.neighbors = MergeNeighbors(a.id, b.id); merged.parentId = merged.id; a.parentId = merged.id; b.parentId = merged.id; clusters.Add(merged.id, merged); //Update the active cluster set activeClusterIds.Remove(a.id); activeClusterIds.Remove(b.id); activeClusterIds.Add(merged.id); double totalCount = a.count + b.count; //Update the distances, based on old distances foreach (int i in merged.neighbors) { //Debug.Assert(i != merged.id && activeClusterIds.Contains(i)); //TODO: Ward's method with minimum variance //For now, just use the dist between the centroids PixelCluster c = clusters[i]; double newDist = merged.lab.SqDist(c.lab); //Add in Ward's variance (variation in Color Segmentation using Region Merging) //http://www.mathworks.com/help/toolbox/stats/linkage.html newDist = newDist * Math.Sqrt(2 * a.count * b.count / (a.count + b.count)); if (c.id < merged.id) pq.Enqueue(new Tuple<int, int>(c.id, merged.id), -1 * newDist); else pq.Enqueue(new Tuple<int, int>(merged.id, c.id), -1 * newDist); } //Remove the old clusters foreach (int i in a.neighbors) { if (i > a.id) pq.Remove(new Tuple<int, int>(a.id, i)); else pq.Remove(new Tuple<int, int>(i, a.id)); } foreach (int i in b.neighbors) { if (i > b.id) pq.Remove(new Tuple<int, int>(b.id, i)); else pq.Remove(new Tuple<int, int>(i, b.id)); } //Repeat until termination criteria if (activeClusterIds.Count() % 1000 == 0) { //write to file log.WriteLine("Merge loop: " + timer.ElapsedMilliseconds/1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count + " merged # neighbors: " + merged.neighbors.Count ); log.Flush(); timer.Restart(); } } rootIds = activeClusterIds; timer.Stop(); log.WriteLine("End: " + timer.ElapsedMilliseconds/1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count); log.WriteLine("End Time: " + DateTime.Now.ToString()); log.Flush(); log.Close(); }
//Cluster the final clusters into color space public void ClusterColorSpace() { double maxDist = 20*20; int minRegions = 5; SortedSet<int> activeClusterIds = new SortedSet<int>(rootIds); String logFile = "colorlog.txt"; StreamWriter log = File.AppendText(logFile); log.WriteLine("\n\tCluster ColorSpace Run " + DateTime.Now.ToString()); log.Flush(); //the smaller id comes first in the dictionary for pairwise distances PriorityQueue<Tuple<int, int>, double> pq = new PriorityQueue<Tuple<int, int>, double>(); int counter = activeClusterIds.Last()+1; int[] ids = activeClusterIds.ToArray<int>(); //Calculate the initial distances for (int i = 0; i < ids.Count(); i++) { for (int j = i+1; j < ids.Count(); j++) { //log.WriteLine(ids[i] + ", " + ids[j] + " dist " + -1 * clusters[ids[i]].lab.SqDist(clusters[ids[j]].lab)); //log.Flush(); //pq.Enqueue(new Tuple<int, int>(ids[i], ids[j]), -1 * clusters[ids[i]].lab.SqDist(clusters[ids[j]].lab)); PixelCluster a = clusters[ids[i]]; PixelCluster b = clusters[ids[j]]; double newDist = a.lab.SqDist(b.lab); //Add in Ward's variance (variation in Color Segmentation using Region Merging) //http://www.mathworks.com/help/toolbox/stats/linkage.html //newDist = newDist * Math.Sqrt(2 * a.count * b.count / (a.count + b.count)); pq.Enqueue(new Tuple<int, int>(ids[i], ids[j]), -1 * newDist); } } Stopwatch timer = new Stopwatch(); timer.Start(); while (activeClusterIds.Count > minRegions) { //Find the pair with the smallest distance KeyValuePair<Tuple<int, int>, double> result = BestPair(pq, activeClusterIds); Tuple<int, int> pair = result.Key; double bestDist = -1 * result.Value; Console.WriteLine("num clusters: " + activeClusterIds.Count()); if (bestDist > maxDist) break; PixelCluster a = clusters[pair.Item1]; PixelCluster b = clusters[pair.Item2]; //Create a new cluster with unique id, we don't care about the neighbors PixelCluster merged = new PixelCluster(); merged.id = counter++; merged.lab = (a.lab * a.count + b.lab * b.count) / (a.count + b.count); merged.count = a.count + b.count; merged.children = new int[] { a.id, b.id }; merged.parentId = merged.id; a.parentId = merged.id; b.parentId = merged.id; clusters.Add(merged.id, merged); //Update the active cluster set activeClusterIds.Remove(a.id); activeClusterIds.Remove(b.id); activeClusterIds.Add(merged.id); double totalCount = a.count + b.count; //Update the distances, based on old distances foreach (int i in activeClusterIds) { if (i == merged.id) continue; //TODO: Ward's method with minimum variance //For now, just use the dist between the centroids PixelCluster c = clusters[i]; double newDist = merged.lab.SqDist(c.lab); //Add in Ward's variance (variation in Color Segmentation using Region Merging) //http://www.mathworks.com/help/toolbox/stats/linkage.html //newDist = newDist * Math.Sqrt(2*a.count * b.count / (a.count + b.count)); if (c.id < merged.id) pq.Enqueue(new Tuple<int, int>(c.id, merged.id), -1 * newDist); else pq.Enqueue(new Tuple<int, int>(merged.id, c.id), -1 * newDist); } //Remove the old clusters foreach (int i in activeClusterIds) { if (i > a.id) pq.Remove(new Tuple<int, int>(a.id, i)); else pq.Remove(new Tuple<int, int>(i, a.id)); } foreach (int i in activeClusterIds) { if (i > b.id) pq.Remove(new Tuple<int, int>(b.id, i)); else pq.Remove(new Tuple<int, int>(i, b.id)); } //Repeat until termination criteria if (activeClusterIds.Count() % 1000 == 0) { //write to file log.WriteLine("Merge loop: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count + " merged # neighbors: " + merged.neighbors.Count); log.Flush(); timer.Restart(); } } rootIds = activeClusterIds; timer.Stop(); log.WriteLine("End: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count); log.WriteLine("End Time: " + DateTime.Now.ToString()); log.Flush(); log.Close(); }
public void ClusterColors(List<CIELAB> colors, int width, int height) { //Bin the colors int minRegions = 5; double maxDist = 10*10; SortedSet<int> activeClusterIds = new SortedSet<int>(); String logFile = "log-colorspace.txt"; StreamWriter log = File.AppendText(logFile); log.WriteLine("\n\tCluster Color Space " + DateTime.Now.ToString()); log.Flush(); //the smaller id comes first in the dictionary for pairwise distances PriorityQueue<Tuple<int, int>, double> pq = new PriorityQueue<Tuple<int, int>, double>(); clusters = new Dictionary<int, PixelCluster>(); int counter = 0; foreach (CIELAB color in colors) { //bin it into one of the clusters //index is a first, then b, then L int id = GetBinId(color); if (id > counter) counter = id; if (!clusters.ContainsKey(id)) { clusters.Add(id, new PixelCluster(id, color)); } else { clusters[id].lab = (clusters[id].lab * clusters[id].count + color) / (clusters[id].count + 1); clusters[id].count++; } } counter++; activeClusterIds = new SortedSet<int>(clusters.Keys); List<int> ids = activeClusterIds.ToList<int>(); for (int i=0; i<ids.Count(); i++) { PixelCluster a = clusters[ids[i]]; //calculate distances to neighbors larger than current id for (int j=i+1; j<ids.Count(); j++) { PixelCluster b = clusters[ids[j]]; double newDist = a.lab.SqDist(b.lab); //newDist = newDist * Math.Sqrt(2 * a.count * b.count / (a.count + b.count)); pq.Enqueue(new Tuple<int, int>(a.id, b.id), -1*newDist); } } Stopwatch timer = new Stopwatch(); timer.Start(); while (activeClusterIds.Count > minRegions) { //Find the pair with the smallest distance KeyValuePair<Tuple<int, int>, double> result = BestPair(pq, activeClusterIds); Tuple<int, int> pair = result.Key; double bestDist = -1 * result.Value; Console.WriteLine("num clusters: " + activeClusterIds.Count()); if (bestDist > maxDist) break; PixelCluster a = clusters[pair.Item1]; PixelCluster b = clusters[pair.Item2]; //Create a new cluster with unique id, don't care about neighbors PixelCluster merged = new PixelCluster(); merged.id = counter++; merged.lab = (a.lab * a.count + b.lab * b.count) / (a.count + b.count); merged.count = a.count + b.count; merged.children = new int[] { a.id, b.id }; merged.parentId = merged.id; a.parentId = merged.id; b.parentId = merged.id; clusters.Add(merged.id, merged); //Update the active cluster set activeClusterIds.Remove(a.id); activeClusterIds.Remove(b.id); activeClusterIds.Add(merged.id); double totalCount = a.count + b.count; //Update the distances, based on old distances foreach (int i in activeClusterIds) { //Debug.Assert(i != merged.id && activeClusterIds.Contains(i)); //TODO: Ward's method with minimum variance //For now, just use the dist between the centroids if (i == merged.id) continue; PixelCluster c = clusters[i]; double newDist = merged.lab.SqDist(c.lab); //Add in Ward's variance (variation in Color Segmentation using Region Merging) //http://www.mathworks.com/help/toolbox/stats/linkage.html //newDist = newDist * Math.Sqrt(2 * a.count * b.count / (a.count + b.count)); if (c.id < merged.id) pq.Enqueue(new Tuple<int, int>(c.id, merged.id), -1 * newDist); else pq.Enqueue(new Tuple<int, int>(merged.id, c.id), -1 * newDist); } //Remove the old clusters foreach (int i in a.neighbors) { if (i > a.id) pq.Remove(new Tuple<int, int>(a.id, i)); else pq.Remove(new Tuple<int, int>(i, a.id)); } foreach (int i in b.neighbors) { if (i > b.id) pq.Remove(new Tuple<int, int>(b.id, i)); else pq.Remove(new Tuple<int, int>(i, b.id)); } //Repeat until termination criteria if (activeClusterIds.Count() % 1000 == 0) { //write to file log.WriteLine("Merge loop: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count + " merged # neighbors: " + merged.neighbors.Count); log.Flush(); timer.Restart(); } } rootIds = activeClusterIds; timer.Stop(); log.WriteLine("End: " + timer.ElapsedMilliseconds / 1000.0 + " # Clusters: " + activeClusterIds.Count() + " pqCount: " + pq.Count); log.WriteLine("End Time: " + DateTime.Now.ToString()); log.Flush(); log.Close(); }