public static List <BoundingBoxGroup> GreedyMeanHierarchicalMergeByPixelDeviation(List <BoundingBox> originalBoundingBoxes, List <string> originalIdentifiers, double deviationToleranceThresholdX, double deviationToleranceThresholdY) { 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.ComputeNormalizedMaxDeviationMetric(ret[i].mergedBoundingBox, deviationToleranceThresholdX, deviationToleranceThresholdY); double deviation2 = meanB.ComputeNormalizedMaxDeviationMetric(ret[j].mergedBoundingBox, deviationToleranceThresholdX, deviationToleranceThresholdY); double maxDeviation = Math.Max(deviation1, deviation2); if (maxDeviation <= 1) { 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); }
public static double ACCEPTANCE_NUMBER_OF_BOXES_THRESHOLD = TaskConstants.MULTI_OBJECT_LOCALIZATION_AND_LABLING_MTURK_OBJECT_COVERAGE_THRESHOLD_FOR_PAYMENT; //the person must have made at least 80% of the boxes public static bool IsAcceptable(SatyamAggregatedResultsTableEntry aggResultEntry, SatyamResultsTableEntry resultEntry, double DeviationPixelThreshold = TaskConstants.MULTI_OBJECT_LOCALIZATION_AND_LABLING_MTURK_DEVIATION_THRESHOLD_FOR_PAYMENT) { //most boxes should be within limits //most categories should be right SatyamAggregatedResult satyamAggResult = JSonUtils.ConvertJSonToObject <SatyamAggregatedResult>(aggResultEntry.ResultString); MultiObjectLocalizationAndLabelingAggregatedResult aggresult = JSonUtils.ConvertJSonToObject <MultiObjectLocalizationAndLabelingAggregatedResult>(satyamAggResult.AggregatedResultString); SatyamResult satyamResult = JSonUtils.ConvertJSonToObject <SatyamResult>(resultEntry.ResultString); MultiObjectLocalizationAndLabelingResult result = JSonUtils.ConvertJSonToObject <MultiObjectLocalizationAndLabelingResult>(satyamResult.TaskResult); if (result == null) { return(false); } //first check if the number of boxes are within limit int boxLimit = (int)Math.Ceiling((double)aggresult.boxesAndCategories.objects.Count * ACCEPTANCE_NUMBER_OF_BOXES_THRESHOLD); if (result.objects.Count < boxLimit) { return(false); } double minboxdimension_threshold_x = MIN_BOX_DIMENSION_FOR_CONSIDERATION * result.displayScaleReductionX; double minboxdimension_threshold_y = MIN_BOX_DIMENSION_FOR_CONSIDERATION * result.displayScaleReductionY; //We fist do a bipartitte matching to find the best assocaition for the boxes List <List <BoundingBox> > allboxes = new List <List <BoundingBox> >(); allboxes.Add(new List <BoundingBox>()); foreach (MultiObjectLocalizationAndLabelingResultSingleEntry entry in result.objects) { allboxes[0].Add(entry.boundingBox); } allboxes.Add(new List <BoundingBox>()); List <bool> tooSmallToIgnore = new List <bool>(); foreach (MultiObjectLocalizationAndLabelingResultSingleEntry entry in aggresult.boxesAndCategories.objects) { allboxes[1].Add(entry.boundingBox); if (entry.boundingBox.getWidth() < minboxdimension_threshold_x && entry.boundingBox.getHeight() < minboxdimension_threshold_y) { tooSmallToIgnore.Add(true); } else { tooSmallToIgnore.Add(false); } } List <MultipartiteWeightedMatch> boxAssociation = BoundingBoxAssociation.computeBoundingBoxAssociations(allboxes); //now find how many of the results match aggregated results int noAccepted = 0; double deviation_threshold_x = DeviationPixelThreshold * result.displayScaleReductionX; double deviation_threshold_y = DeviationPixelThreshold * result.displayScaleReductionY; int noIgnorable = 0; foreach (MultipartiteWeightedMatch match in boxAssociation) { if (match.elementList.ContainsKey(1)) // this contains an aggregated box { if (match.elementList.ContainsKey(0)) // a result box has been associated { BoundingBox aggregatedBoundingBox = allboxes[1][match.elementList[1]]; BoundingBox resultBoundingBox = allboxes[0][match.elementList[0]]; //double deviation = aggregatedBoundingBox.ComputeMaxDeviationMetric(resultBoundingBox); double deviation = aggregatedBoundingBox.ComputeNormalizedMaxDeviationMetric(resultBoundingBox, deviation_threshold_x, deviation_threshold_y); if (deviation <= 1) //deviation test passed { //now check category if (result.objects[match.elementList[0]].Category == aggresult.boxesAndCategories.objects[match.elementList[1]].Category) { //both category and bounding box tests have passed noAccepted++; } } else { if (tooSmallToIgnore[match.elementList[1]]) { noIgnorable++; } } } } } if (noAccepted >= boxLimit - noIgnorable) { return(true); } return(false); }