public static List <BoundingBoxGroup> GreedyMeanHierarchicalMergeByPixelDeviation(List <BoundingBox> originalBoundingBoxes, List <string> originalIdentifiers, double deviationToleranceThreshold) { List <BoundingBoxGroup> ret = new List <BoundingBoxGroup>(); //initialize by creating one group per bounding box for (int i = 0; i < originalBoundingBoxes.Count; i++) { BoundingBox b = originalBoundingBoxes[i]; BoundingBoxGroup bgroup = new BoundingBoxGroup(); bgroup.mergedBoundingBox = new BoundingBox(b.tlx, b.tly, b.brx, b.bry); bgroup.boundingBoxList.Add(b); bgroup.identifiers.Add(originalIdentifiers[i]); ret.Add(bgroup); } bool changed = false; do { changed = false; //find the closest mergable bouding box group pair and merge by averaging int index1 = -1; int index2 = -1; //double bestSimilarity = 0; double bestDeviation = 10000000000; BoundingBox bestMergedBoundingBox = null; for (int i = 0; i < ret.Count - 1; i++) { for (int j = i + 1; j < ret.Count; j++) { //compute the merged bouding box BoundingBox meanB = computeMeanBoudingBoxFromTwoGroups(ret[i].boundingBoxList, ret[j].boundingBoxList); double deviation1 = meanB.ComputeMaxDeviationMetric(ret[i].mergedBoundingBox); double deviation2 = meanB.ComputeMaxDeviationMetric(ret[j].mergedBoundingBox); double maxDeviation = Math.Max(deviation1, deviation2); if (maxDeviation <= deviationToleranceThreshold) { if (maxDeviation < bestDeviation) { bestDeviation = maxDeviation; index1 = i; index2 = j; bestMergedBoundingBox = meanB; changed = true; } } } } if (changed) //there is something worth merging { //merge 2 into 1 for (int i = 0; i < ret[index2].boundingBoxList.Count; i++) { ret[index1].boundingBoxList.Add(ret[index2].boundingBoxList[i]); ret[index1].identifiers.Add(ret[index2].identifiers[i]); } ret[index1].mergedBoundingBox = bestMergedBoundingBox; //remove 2 ret.RemoveAt(index2); } if (ret.Count == 1) { break; } } while (changed); return(ret); }