Skip to content

Commit

Permalink
reduce runtime of jaccard_iou by more than 50 percent (#1223)
Browse files Browse the repository at this point in the history
* reduce runtime of jaccard_iou
  • Loading branch information
benisraeldan committed Apr 10, 2022
1 parent c9ce8b9 commit 8108684
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 31 deletions.
2 changes: 1 addition & 1 deletion deepchecks/vision/checks/performance/confusion_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def update_object_detection(self, predictions, labels):
continue

list_of_ious = (
(label_index, detected_index, jaccard_iou(detected, label))
(label_index, detected_index, jaccard_iou(detected.cpu().numpy(), label.cpu().numpy()))
for label_index, label in enumerate(image_labels)
for detected_index, detected in enumerate(detections_passed_threshold)
)
Expand Down
44 changes: 27 additions & 17 deletions deepchecks/vision/metrics_utils/iou_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@
import torch


def jaccard_iou(dt, gt):
def jaccard_iou(dt: np.array, gt: np.array):
"""Calculate the jaccard IoU.
See https://en.wikipedia.org/wiki/Jaccard_index
Parameters
----------
dt: np.array
Single Detection in the shape of [x, y, width, height, confidence, class]
gt: np.array
Single Ground Truth in the shape of [class, x, y, width, height]
"""
x_dt, y_dt, w_dt, h_dt = dt[:4]
x_gt, y_gt, w_gt, h_gt = gt[1:]
Expand All @@ -27,18 +34,20 @@ def jaccard_iou(dt, gt):
x2_gt, y2_gt = x_gt + w_gt, y_gt + h_gt

# innermost left x
xi = max(x_dt, x_gt)
xi = x_dt if x_dt > x_gt else x_gt
# innermost right x
x2i = min(x2_dt, x2_gt)
x2i = x2_dt if x2_dt < x2_gt else x2_gt
# same for y
yi = max(y_dt, y_gt)
y2i = min(y2_dt, y2_gt)
yi = y_dt if y_dt > y_gt else y_gt
y2i = y2_dt if y2_dt < y2_gt else y2_gt

# calculate areas
dt_area = float(w_dt * h_dt)
gt_area = float(w_gt * h_gt)
intersection = float(max(x2i - xi, 0)) * float(max(y2i - yi, 0))
return float(intersection / (dt_area + gt_area - intersection))
dt_area = w_dt * h_dt
gt_area = w_gt * h_gt
iwidth = x2i - xi if x2i > xi else 0
ihight = y2i - yi if y2i > yi else 0
intersection = iwidth * ihight
return intersection / (dt_area + gt_area - intersection)


def compute_pairwise_ious(detected, ground_truth, iou_func):
Expand All @@ -50,22 +59,23 @@ def compute_pairwise_ious(detected, ground_truth, iou_func):
return ious


def group_class_detection_label(detected, ground_truth, detection_classes, ground_truth_classes):
def group_class_detection_label(detected, ground_truth):
"""Group bounding detection and labels by class."""
class_bounding_boxes = defaultdict(lambda: {"detected": [], "ground_truth": []})

for single_detection, class_id in zip(detected, detection_classes):
class_bounding_boxes[class_id]["detected"].append(single_detection)
for single_ground_truth, class_id in zip(ground_truth, ground_truth_classes):
class_bounding_boxes[class_id]["ground_truth"].append(single_ground_truth)
for single_detection in detected:
class_id = untorchify(single_detection[5])
class_bounding_boxes[class_id]["detected"].append(single_detection.cpu().numpy())
for single_ground_truth in ground_truth:
class_id = untorchify(single_ground_truth[0])
class_bounding_boxes[class_id]["ground_truth"].append(single_ground_truth.cpu().numpy())

return class_bounding_boxes


def compute_bounding_box_class_ious(detected, ground_truth):
"""Compute ious between bounding boxes of the same class."""
detection_classes = [untorchify(d[5]) for d in detected]
ground_truth_classes = [untorchify(g[0]) for g in ground_truth]
bb_info = group_class_detection_label(detected, ground_truth, detection_classes, ground_truth_classes)
bb_info = group_class_detection_label(detected, ground_truth)

# Calculating pairwise IoUs per class
return {class_id: compute_pairwise_ious(info["detected"], info["ground_truth"], jaccard_iou)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
# ----------------------------------------------------------------------------
#
"""Module for average precision for object detection."""
from collections import defaultdict
from typing import List

import numpy as np

from deepchecks.vision.metrics_utils.detection_precision_recall import AveragePrecision
from deepchecks.vision.metrics_utils.iou_utils import compute_pairwise_ious, jaccard_iou, untorchify
from deepchecks.vision.metrics_utils.iou_utils import compute_pairwise_ious, jaccard_iou, group_class_detection_label


class ObjectDetectionAveragePrecision(AveragePrecision):
Expand All @@ -26,16 +25,7 @@ def get_labels_areas(self, labels) -> List[int]:

def group_class_detection_label(self, detections, labels) -> dict:
"""Group detection and labels in dict of format {class_id: {'detected' [...], 'ground_truth': [...] }}."""
class_bounding_boxes = defaultdict(lambda: {"detected": [], "ground_truth": []})

for single_detection in detections:
class_id = untorchify(single_detection[5])
class_bounding_boxes[class_id]["detected"].append(single_detection)
for single_ground_truth in labels:
class_id = untorchify(single_ground_truth[0])
class_bounding_boxes[class_id]["ground_truth"].append(single_ground_truth)

return class_bounding_boxes
return group_class_detection_label(detections, labels)

def get_confidences(self, detections) -> List[float]:
"""Get detections object of single image and should return confidence for each detection."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_detection(coco_train_visiondata, coco_test_visiondata, mock_trained_yol
mock_trained_yolov5_object_detection,
device=device)
# Assert
assert_that(len(result.value['feature_segments']), equal_to(3))
assert_that(len(result.value['feature_segments']), equal_to(4))
assert_that(result.value['feature_segments']['Mean Green Relative Intensity']['segment1']['n_samples'],
equal_to(21))

Expand Down

0 comments on commit 8108684

Please sign in to comment.