-
Notifications
You must be signed in to change notification settings - Fork 2k
Fix/mAP #1834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Fix/mAP #1834
Changes from all commits
317cbd3
232916a
36a2171
157ed3a
15cd445
688d987
bba43ec
790baa6
19c4e44
8f72e49
52980a6
cff9745
fd31351
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1325,3 +1325,77 @@ def spread_out_boxes( | |
xyxy_padded[:, [2, 3]] += force_vectors | ||
|
||
return pad_boxes(xyxy_padded, px=-1) | ||
|
||
|
||
def _jaccard(box_a: List[float], box_b: List[float], is_crowd: bool) -> float: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I meant was to enable batch processing of the boxes, so that we wouldn't need double for g_idx, g in enumerate(gt):
for d_idx, d in enumerate(dt): in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, see my explanation in the next conversation. I addressed this issue there. |
||
""" | ||
Calculate the Jaccard index (intersection over union) between two bounding boxes. | ||
If a gt object is marked as "iscrowd", a dt is allowed to match any subregion | ||
of the gt. Choosing gt' in the crowd gt that best matches the dt can be done using | ||
gt'=intersect(dt,gt). Since by definition union(gt',dt)=dt, computing | ||
iou(gt,dt,iscrowd) = iou(gt',dt) = area(intersect(gt,dt)) / area(dt) | ||
|
||
Args: | ||
box_a (List[float]): Box coordinates in the format [x, y, width, height]. | ||
box_b (List[float]): Box coordinates in the format [x, y, width, height]. | ||
iscrowd (bool): Flag indicating if the second box is a crowd region or not. | ||
|
||
Returns: | ||
float: Jaccard index between the two bounding boxes. | ||
""" | ||
# Smallest number to avoid division by zero | ||
EPS = np.spacing(1) | ||
|
||
xa, ya, x2a, y2a = box_a[0], box_a[1], box_a[0] + box_a[2], box_a[1] + box_a[3] | ||
xb, yb, x2b, y2b = box_b[0], box_b[1], box_b[0] + box_b[2], box_b[1] + box_b[3] | ||
|
||
# Innermost left x | ||
xi = max(xa, xb) | ||
# Innermost right x | ||
x2i = min(x2a, x2b) | ||
# Same for y | ||
yi = max(ya, yb) | ||
y2i = min(y2a, y2b) | ||
|
||
# Calculate areas | ||
Aa = max(x2a - xa, 0.0) * max(y2a - ya, 0.0) | ||
Ab = max(x2b - xb, 0.0) * max(y2b - yb, 0.0) | ||
Ai = max(x2i - xi, 0.0) * max(y2i - yi, 0.0) | ||
|
||
if is_crowd: | ||
return Ai / (Aa + EPS) | ||
|
||
return Ai / (Aa + Ab - Ai + EPS) | ||
|
||
|
||
def iou_with_jaccard( | ||
boxes_true: List[List[float]], | ||
boxes_detection: List[List[float]], | ||
is_crowd: List[bool], | ||
) -> np.ndarray: | ||
""" | ||
Calculate the intersection over union (IoU) between detection bounding boxes (dt) | ||
and ground-truth bounding boxes (gt). | ||
Reference: https://github.com/rafaelpadilla/review_object_detection_metrics | ||
|
||
Args: | ||
boxes_true (List[List[float]]): List of ground-truth bounding boxes in the \ | ||
format [x, y, width, height]. | ||
boxes_detection (List[List[float]]): List of detection bounding boxes in the \ | ||
format [x, y, width, height]. | ||
is_crowd (List[bool]): List indicating if each ground-truth bounding box \ | ||
is a crowd region or not. | ||
|
||
Returns: | ||
np.ndarray: Array of IoU values of shape (len(dt), len(gt)). | ||
""" | ||
assert len(is_crowd) == len(boxes_true), ( | ||
"iou(iscrowd=) must have the same length as boxes_true" | ||
) | ||
if len(boxes_detection) == 0 or len(boxes_true) == 0: | ||
return np.array([]) | ||
ious = np.zeros((len(boxes_detection), len(boxes_true)), dtype=np.float64) | ||
for g_idx, g in enumerate(boxes_true): | ||
for d_idx, d in enumerate(boxes_detection): | ||
ious[d_idx, g_idx] = _jaccard(d, g, is_crowd[g_idx]) | ||
return ious |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is not used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're correct — this function is not currently invoked in the main pipeline. However, it is essential for evaluating certain models whose class ID schemes differ from those used in the COCO dataset.
Specifically, some models use sequential class IDs (e.g., 0 to 79 for 80 classes), whereas COCO's official annotations intentionally skip some IDs. You can see a detailed breakdown of these skipped IDs in this spread sheet.
To address this mismatch, this function is super useful. A practical example of this mapping is used in
this colab notebook, where
get_coco_class_index_mapping
is applied to reproduce results consistent with the roboflow/model-leaderboardThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since in your examples you always reverse the dictionary right after
get_coco_class_index_mapping
, maybe it would be easier to just return the reversed mapping directly?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️ I have updated the code to return the reversed mapping. Thanks for the suggestion!