@@ -261,16 +261,21 @@ def calculate_ranking_boundaries(
261261 NDArray[np.float64]
262262 A 1-D array containing the lower IOU boundary for classifying pairs as true-positive across chunks.
263263 """
264- # groundtruths defined as (datum_id, groundtruth_id, groundtruth_label_id)
265- gts = ranked_pairs [:, (0 , 1 , 3 )].astype (np .int64 )
264+ ids = ranked_pairs [:, (0 , 1 , 2 , 3 , 4 )].astype (np .int64 )
265+ gts = ids [:, (0 , 1 , 3 )]
266+ gt_labels = ids [:, 3 ]
267+ pd_labels = ids [:, 4 ]
266268 ious = ranked_pairs [:, 5 ]
267269
268- iou_boundary = np .ones_like (ious ) * 2 # impossible bound
270+ # set default boundary to 2.0 as it will be used to check lower boundary in range [0-1].
271+ iou_boundary = np .ones_like (ious ) * 2
269272
273+ mask_matching_labels = gt_labels == pd_labels
270274 mask_valid_gts = gts [:, 1 ] >= 0
271275 unique_gts = np .unique (gts [mask_valid_gts ], axis = 0 )
272276 for gt in unique_gts :
273277 mask_gt = (gts == gt ).all (axis = 1 )
278+ mask_gt &= mask_matching_labels
274279 if mask_gt .sum () <= 1 :
275280 iou_boundary [mask_gt ] = 0.0
276281 continue
@@ -444,10 +449,6 @@ def compute_counts(
444449 minlength = n_labels ,
445450 )
446451
447- # create true-positive mask score threshold
448- mask_tps = mask_tp_outer
449- true_positives_mask = mask_tps & mask_iou_prev
450-
451452 # count running tp and total for AP
452453 for pd_label in unique_pd_labels :
453454 mask_pd_label = pd_labels == pd_label
@@ -463,7 +464,7 @@ def compute_counts(
463464 running_counts [iou_idx , pd_label , 0 ] += total_count
464465
465466 # running true-positive count
466- mask_tp_for_counting = mask_pd_label & true_positives_mask
467+ mask_tp_for_counting = mask_pd_label & mask_tp_outer
467468 tp_count = mask_tp_for_counting .sum ()
468469 running_tp_count [iou_idx , mask_tp_for_counting ] = np .arange (
469470 running_counts [iou_idx , pd_label , 1 ] + 1 ,
@@ -488,17 +489,43 @@ def compute_counts(
488489 )
489490 recall_index = np .floor (recall * 100.0 ).astype (np .int32 )
490491
491- # bin precision-recall curve
492+ # sort precision in descending order
493+ precision_indices = np .argsort (- precision , axis = 1 )
494+
495+ # populate precision-recall curve
492496 for iou_idx in range (n_ious ):
493- pr_curve [iou_idx , pd_labels , recall_index [iou_idx ], 0 ] = np .maximum (
494- pr_curve [iou_idx , pd_labels , recall_index [iou_idx ], 0 ],
495- precision [iou_idx ],
497+ labeled_recall = np .hstack (
498+ [
499+ pd_labels .reshape (- 1 , 1 ),
500+ recall_index [iou_idx , :].reshape (- 1 , 1 ),
501+ ]
502+ )
503+
504+ # extract maximum score per (label, recall) bin
505+ # arrays are already ordered by descending score
506+ lr_pairs , recall_indices = np .unique (
507+ labeled_recall , return_index = True , axis = 0
508+ )
509+ li = lr_pairs [:, 0 ]
510+ ri = lr_pairs [:, 1 ]
511+ pr_curve [iou_idx , li , ri , 1 ] = np .maximum (
512+ pr_curve [iou_idx , li , ri , 1 ],
513+ scores [recall_indices ],
514+ )
515+
516+ # extract maximum precision per (label, recall) bin
517+ # reorder arrays into descending precision order
518+ indices = precision_indices [iou_idx ]
519+ sorted_precision = precision [iou_idx , indices ]
520+ sorted_labeled_recall = labeled_recall [indices ]
521+ lr_pairs , recall_indices = np .unique (
522+ sorted_labeled_recall , return_index = True , axis = 0
496523 )
497- pr_curve [
498- iou_idx , pd_labels [:: - 1 ], recall_index [ iou_idx ][:: - 1 ], 1
499- ] = np .maximum (
500- pr_curve [iou_idx , pd_labels [:: - 1 ], recall_index [ iou_idx ][:: - 1 ], 1 ],
501- scores [:: - 1 ],
524+ li = lr_pairs [:, 0 ]
525+ ri = lr_pairs [:, 1 ]
526+ pr_curve [ iou_idx , li , ri , 0 ] = np .maximum (
527+ pr_curve [iou_idx , li , ri , 0 ],
528+ sorted_precision [ recall_indices ],
502529 )
503530
504531 return counts
0 commit comments