@@ -231,6 +231,96 @@ def box_area(box):
231231 return ious
232232
233233
234+ def box_iou_batch_alt (
235+ boxes_true : np .ndarray ,
236+ boxes_detection : np .ndarray ,
237+ overlap_metric : OverlapMetric = OverlapMetric .IOU ,
238+ ) -> np .ndarray :
239+ """
240+ Compute Intersection over Union (IoU) of two sets of bounding boxes -
241+ `boxes_true` and `boxes_detection`. Both sets
242+ of boxes are expected to be in `(x_min, y_min, x_max, y_max)` format.
243+
244+ Note:
245+ Use `box_iou` when computing IoU between two individual boxes.
246+ For comparing multiple boxes (arrays of boxes), use `box_iou_batch` for better
247+ performance.
248+
249+ Args:
250+ boxes_true (np.ndarray): 2D `np.ndarray` representing ground-truth boxes.
251+ `shape = (N, 4)` where `N` is number of true objects.
252+ boxes_detection (np.ndarray): 2D `np.ndarray` representing detection boxes.
253+ `shape = (M, 4)` where `M` is number of detected objects.
254+ overlap_metric (OverlapMetric): Metric used to compute the degree of overlap
255+ between pairs of boxes (e.g., IoU, IoS).
256+
257+ Returns:
258+ np.ndarray: Pairwise IoU of boxes from `boxes_true` and `boxes_detection`.
259+ `shape = (N, M)` where `N` is number of true objects and
260+ `M` is number of detected objects.
261+
262+ Examples:
263+ ```python
264+ import numpy as np
265+ import supervision as sv
266+
267+ boxes_true = np.array([
268+ [100, 100, 200, 200],
269+ [300, 300, 400, 400]
270+ ])
271+ boxes_detection = np.array([
272+ [150, 150, 250, 250],
273+ [320, 320, 420, 420]
274+ ])
275+
276+ sv.box_iou_batch(boxes_true=boxes_true, boxes_detection=boxes_detection)
277+ # array([
278+ # [0.14285714, 0. ],
279+ # [0. , 0.47058824]
280+ # ])
281+ ```
282+
283+ """
284+
285+ tx1 , ty1 , tx2 , ty2 = boxes_true .T
286+ dx1 , dy1 , dx2 , dy2 = boxes_detection .T
287+ N , M = boxes_true .shape [0 ], boxes_detection .shape [0 ]
288+
289+ top_left_x = np .empty ((N , M ), dtype = np .float32 )
290+ bottom_right_x = np .empty_like (top_left_x )
291+ top_left_y = np .empty_like (top_left_x )
292+ bottom_right_y = np .empty_like (top_left_x )
293+
294+ np .maximum (tx1 [:, None ], dx1 [None , :], out = top_left_x )
295+ np .minimum (tx2 [:, None ], dx2 [None , :], out = bottom_right_x )
296+ np .maximum (ty1 [:, None ], dy1 [None , :], out = top_left_y )
297+ np .minimum (ty2 [:, None ], dy2 [None , :], out = bottom_right_y )
298+
299+ np .subtract (bottom_right_x , top_left_x , out = bottom_right_x ) # W
300+ np .subtract (bottom_right_y , top_left_y , out = bottom_right_y ) # H
301+ np .clip (bottom_right_x , 0.0 , None , out = bottom_right_x )
302+ np .clip (bottom_right_y , 0.0 , None , out = bottom_right_y )
303+
304+ area_inter = bottom_right_x * bottom_right_y
305+
306+ area_true = (tx2 - tx1 ) * (ty2 - ty1 )
307+ area_detection = (dx2 - dx1 ) * (dy2 - dy1 )
308+
309+ if overlap_metric == OverlapMetric .IOU :
310+ denom = area_true [:, None ] + area_detection [None , :] - area_inter
311+ elif overlap_metric == OverlapMetric .IOS :
312+ denom = np .minimum (area_true [:, None ], area_detection [None , :])
313+ else :
314+ raise ValueError (
315+ f"overlap_metric { overlap_metric } is not supported, "
316+ "only 'IOU' and 'IOS' are supported"
317+ )
318+
319+ out = np .zeros_like (area_inter , dtype = np .float32 )
320+ np .divide (area_inter , denom , out = out , where = denom > 0 )
321+ return out
322+
323+
234324def _jaccard (box_a : list [float ], box_b : list [float ], is_crowd : bool ) -> float :
235325 """
236326 Calculate the Jaccard index (intersection over union) between two bounding boxes.
0 commit comments