-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
[NMS] - add segmentation models support #847
Conversation
…ded withnms conditional depends if contains mask
Hi @AdonaiVera 👋🏻 ! I left a few comments on the code, but for the moment, these are secondary issues. The main problem I see is speed. Using your Colab example, I measured that it takes 13 seconds to calculate the In addition, RAM consumption jumps from 4GB to 12-13GB just because you count the IoU mask. A quick query in ChatGPT suggested the code below, but I didn't check if the calculation result was the same or if the code was faster. I leave it here simply as an idea. import numpy as np
def mask_iou_batch(masks_true: np.ndarray, masks_detection: np.ndarray) -> np.ndarray:
"""
Compute Intersection over Union (IoU) of two sets of masks -
`masks_true` and `masks_detection`.
Args:
masks_true (np.ndarray): 3D `np.ndarray` representing ground-truth masks.
masks_detection (np.ndarray): 3D `np.ndarray` representing detection masks.
Returns:
np.ndarray: Pairwise IoU of masks from `masks_true` and `masks_detection`.
"""
# No need to cast to bool here if the masks are already boolean
# Calculate intersection and union areas
intersection_area = np.logical_and(masks_true[:, None], masks_detection).sum(axis=(2, 3))
masks_true_area = masks_true.sum(axis=(1, 2))[:, None]
masks_detection_area = masks_detection.sum(axis=(1, 2))
# Calculate union area using the inclusion-exclusion principle
union_area = masks_true_area + masks_detection_area - intersection_area
# Avoid division by zero and calculate IoU
iou = np.divide(intersection_area, union_area, out=np.zeros_like(intersection_area, dtype=float), where=union_area != 0)
return iou The second idea I have is to resize the masks. Maybe we don't need to count IoU at full resolution. I'm curious, if we did rescaling on the input to UpdateI couldn't help myself and decided to see how the IoU mask speed would change if we used rescaling and calculated IoU on smaller masks. For your image, the execution time drops from 13 seconds to 0.8 seconds. Rescaling the masks alone takes 0.01 seconds. Here is my vectorized rescaling code. I also included it in our test Colab. def resize_masks(masks: np.ndarray, max_dimension: int = 640) -> np.ndarray:
"""
Resize all masks in the array to have a maximum dimension of max_dimension,
maintaining aspect ratio.
Args:
masks (np.ndarray): 3D array of binary masks with shape (N, H, W).
max_dimension (int): The maximum dimension for the resized masks.
Returns:
np.ndarray: Array of resized masks.
"""
max_height = np.max(masks.shape[1])
max_width = np.max(masks.shape[2])
scale = min(max_dimension / max_height, max_dimension / max_width)
new_height = int(scale * max_height)
new_width = int(scale * max_width)
# Create a grid of coordinates in the resized shape
x = np.linspace(0, max_width - 1, new_width).astype(int)
y = np.linspace(0, max_height - 1, new_height).astype(int)
xv, yv = np.meshgrid(x, y)
# Use nearest-neighbor interpolation to resize all masks
resized_masks = masks[:, yv, xv]
# Reshape to remove the extra dimension
resized_masks = resized_masks.reshape(masks.shape[0], new_height, new_width)
return resized_masks Update 2I also checked how the IoU mask values change when we scale the mask. The graph below shows the absolute value of the difference between mask IoU calculated on full-size masks and on scaled masks. The max value of the IoU difference is |
Hi @SkalskiP 👋 I made the changes to the algorithm based on your feedback. I executed the test on my local machine and found that the execution time for the Then, I implemented your suggestion of using the inclusion-exclusion principle to improve the After that, I applied your resize function to the input images before applying the Thank you for your improvements, and I believe that these changes have significantly improved the algorithm's speed. I also incorporated the other small changes that you suggested and pushed the changes. I think the idea of resizing the images worked really well because, from the graph you provided, the absolute value of the difference between the mask IoU calculated on full-size masks and on scaled masks is not significant, and the processing time improved a lot. I executed all the unit test and the algorithm is working fine 👍 PDT: I left the print time message to keep making more tests if needed. Thank you for your valuable feedback. 🚀 Thank you 🥷 |
…ion_nms # Conflicts: # supervision/detection/utils.py
Hi @AdonaiVera 👋🏻 ! I made a few small changes in the second part of keep = np.ones(rows, dtype=bool)
for i in range(rows):
if keep[i]:
condition = (ious[i] > iou_threshold) & (categories[i] == categories)
keep[i + 1 :] = np.where(condition[i + 1 :], False, keep[i + 1 :])
return keep[sort_index.argsort()] I also added some additional unit tests. In general, we could make it even faster. But taking it from 13 to 0.4 seconds is still a good start. I'm merging! Thanks a lot for your help! 🔥 |
Amazing, thank you @SkalskiP 🚀
I'll update a PR soon 💪 |
@AdonaiVera, thanks a lot! 🙏🏻 You know where to find me! |
Description
This PR introduces Non-Maximum Suppression (NMS) algorithm focused on segmentation, enhancing our object detection capabilities, particularly in segmentation tasks. We have renamed the traditional
non_max_suppression
function tobox_non_max_suppression
for better clarity regarding its application to bounding boxes. Furthermore, we've integrated a conditional mechanism within thewith_nms
function that utilizes segmentation masks for NMS when such masks are present in the predictions. This optimization leverages the spatial context provided by segmentation masks to improve the suppression process.This enhancement is part of a task segmented into two parts for more focused development and review. This PR addresses the first part, as discussed in the related issue here. Splitting the task ensures thorough implementation and testing of each component.
In addition, this PR encompasses comprehensive unit tests for the new NMS functionality and a demo that demonstrates the algorithm's application and effectiveness in real-world scenarios.
Type of change
How has this change been tested
I created a demo to showcase this functionality here
Docs