Skip to content

⚡️ Speed up function get_bbox_size_category by 36% #42

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

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

codeflash-ai[bot]
Copy link

@codeflash-ai codeflash-ai bot commented Feb 3, 2025

📄 36% (0.36x) speedup for get_bbox_size_category in supervision/metrics/utils/object_size.py

⏱️ Runtime : 357 microseconds 262 microseconds (best of 43 runs)

📝 Explanation and details

o3-mini
We can speed up the function by replacing the multiple in‐place assignments with a single nested np.where call. This avoids creating several intermediate boolean masks and extra writes to the result array. The core arithmetic (computing areas) remains vectorized, but now the category assignment is done in one go.

Note: The function still validates input shape and returns the same result exactly.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 32 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests Details
from __future__ import annotations

from enum import Enum

import numpy as np
import numpy.typing as npt
# imports
import pytest  # used for our unit tests
from supervision.metrics.utils.object_size import get_bbox_size_category


class ObjectSizeCategory(Enum):
    ANY = -1
    SMALL = 1
    MEDIUM = 2
    LARGE = 3

SIZE_THRESHOLDS = (32**2, 96**2)
from supervision.metrics.utils.object_size import get_bbox_size_category

# unit tests

def test_single_bounding_box():
    # Single bounding box, small size
    bbox = np.array([[0, 0, 10, 10]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)

def test_multiple_bounding_boxes():
    # Multiple bounding boxes with different sizes
    bbox = np.array([[0, 0, 10, 10], [0, 0, 50, 50]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)
    expected = np.array([ObjectSizeCategory.SMALL.value, ObjectSizeCategory.MEDIUM.value])

def test_zero_area_bounding_box():
    # Bounding box with zero area
    bbox = np.array([[0, 0, 0, 10]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)

def test_negative_dimensions():
    # Bounding box with negative dimensions
    bbox = np.array([[10, 10, 0, 0]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)

def test_invalid_shape_1d_array():
    # 1D array input
    bbox = np.array([0, 0, 10, 10], dtype=np.float32)
    with pytest.raises(ValueError):
        get_bbox_size_category(bbox)

def test_invalid_shape_incorrect_columns():
    # Incorrect number of columns
    bbox = np.array([[0, 0, 10]], dtype=np.float32)
    with pytest.raises(ValueError):
        get_bbox_size_category(bbox)

def test_large_number_of_bounding_boxes():
    # Large number of bounding boxes
    bbox = np.random.rand(10000, 4).astype(np.float32) * 100
    codeflash_output = get_bbox_size_category(bbox)

def test_boundary_conditions():
    # Bounding boxes exactly at thresholds
    bbox = np.array([[0, 0, 32, 32], [0, 0, 96, 96]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)
    expected = np.array([ObjectSizeCategory.SMALL.value, ObjectSizeCategory.MEDIUM.value])

def test_bounding_boxes_just_below_and_above_thresholds():
    # Bounding boxes just below and above thresholds
    bbox = np.array([[0, 0, 31.9, 31.9], [0, 0, 32.1, 32.1], [0, 0, 95.9, 95.9], [0, 0, 96.1, 96.1]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)
    expected = np.array([ObjectSizeCategory.SMALL.value, ObjectSizeCategory.MEDIUM.value, ObjectSizeCategory.MEDIUM.value, ObjectSizeCategory.LARGE.value])

def test_mixed_size_categories():
    # Bounding boxes of different sizes
    bbox = np.array([[0, 0, 10, 10], [0, 0, 50, 50], [0, 0, 100, 100]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)
    expected = np.array([ObjectSizeCategory.SMALL.value, ObjectSizeCategory.MEDIUM.value, ObjectSizeCategory.LARGE.value])

def test_floating_point_precision():
    # Bounding boxes with high precision coordinates
    bbox = np.array([[0.000001, 0.000001, 32.000001, 32.000001], [0.000001, 0.000001, 95.999999, 95.999999]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)
    expected = np.array([ObjectSizeCategory.MEDIUM.value, ObjectSizeCategory.MEDIUM.value])

def test_min_max_float_values():
    # Bounding boxes with min and max float values
    bbox = np.array([[np.finfo(np.float32).min, np.finfo(np.float32).min, np.finfo(np.float32).max, np.finfo(np.float32).max]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)

def test_non_rectangular_inputs():
    # Bounding boxes with non-standard shapes
    bbox = np.array([[0, 0, 10, 20], [0, 0, 20, 10]], dtype=np.float32)
    codeflash_output = get_bbox_size_category(bbox)
    expected = np.array([ObjectSizeCategory.SMALL.value, ObjectSizeCategory.SMALL.value])

def test_empty_input():
    # No bounding boxes
    bbox = np.array([], dtype=np.float32).reshape(0, 4)
    codeflash_output = get_bbox_size_category(bbox)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

from __future__ import annotations

from enum import Enum

import numpy as np
import numpy.typing as npt
# imports
import pytest  # used for our unit tests
from supervision.metrics.utils.object_size import get_bbox_size_category


class ObjectSizeCategory(Enum):
    ANY = -1
    SMALL = 1
    MEDIUM = 2
    LARGE = 3

SIZE_THRESHOLDS = (32**2, 96**2)
from supervision.metrics.utils.object_size import get_bbox_size_category

# unit tests

def test_single_small_bbox():
    # Single small bounding box
    input_data = np.array([[0, 0, 10, 10]], dtype=np.float32)
    expected_output = np.array([1], dtype=np.int_)

def test_single_medium_bbox():
    # Single medium bounding box
    input_data = np.array([[0, 0, 50, 50]], dtype=np.float32)
    expected_output = np.array([2], dtype=np.int_)

def test_single_large_bbox():
    # Single large bounding box
    input_data = np.array([[0, 0, 100, 100]], dtype=np.float32)
    expected_output = np.array([3], dtype=np.int_)

def test_mixed_sizes():
    # Mixed sizes of bounding boxes
    input_data = np.array([[0, 0, 10, 10], [0, 0, 50, 50], [0, 0, 100, 100]], dtype=np.float32)
    expected_output = np.array([1, 2, 3], dtype=np.int_)

def test_zero_area_bbox():
    # Bounding box with zero area
    input_data = np.array([[0, 0, 0, 0]], dtype=np.float32)
    expected_output = np.array([1], dtype=np.int_)

def test_threshold_small_medium():
    # Bounding box on the threshold of small and medium
    input_data = np.array([[0, 0, 32, 32]], dtype=np.float32)
    expected_output = np.array([1], dtype=np.int_)

def test_threshold_medium_large():
    # Bounding box on the threshold of medium and large
    input_data = np.array([[0, 0, 96, 96]], dtype=np.float32)
    expected_output = np.array([2], dtype=np.int_)

def test_incorrect_shape_1d():
    # Incorrect shape (1D array)
    input_data = np.array([0, 0, 10, 10], dtype=np.float32)
    with pytest.raises(ValueError):
        get_bbox_size_category(input_data)

def test_incorrect_shape_3d():
    # Incorrect shape (3D array)
    input_data = np.array([[[0, 0, 10, 10]]], dtype=np.float32)
    with pytest.raises(ValueError):
        get_bbox_size_category(input_data)

def test_incorrect_inner_dimension():
    # Incorrect inner dimension
    input_data = np.array([[0, 0, 10]], dtype=np.float32)
    with pytest.raises(ValueError):
        get_bbox_size_category(input_data)

def test_large_number_small_bboxes():
    # Large number of small bounding boxes
    input_data = np.array([[0, 0, 10, 10]] * 1000, dtype=np.float32)
    expected_output = np.array([1] * 1000, dtype=np.int_)

def test_large_number_mixed_bboxes():
    # Large number of mixed bounding boxes
    input_data = np.array([[0, 0, 10, 10], [0, 0, 50, 50], [0, 0, 100, 100]] * 1000, dtype=np.float32)
    expected_output = np.array([1, 2, 3] * 1000, dtype=np.int_)

def test_negative_coordinates():
    # Bounding box with negative coordinates
    input_data = np.array([[-10, -10, 10, 10]], dtype=np.float32)
    expected_output = np.array([2], dtype=np.int_)

def test_floating_point_precision():
    # Bounding box with floating point precision
    input_data = np.array([[0.5, 0.5, 32.5, 32.5]], dtype=np.float32)
    expected_output = np.array([2], dtype=np.int_)

def test_extremely_large_input():
    # Extremely large input
    input_data = np.array([[0, 0, 10, 10]] * 1000000, dtype=np.float32)
    expected_output = np.array([1] * 1000000, dtype=np.int_)

def test_exact_threshold():
    # Bounding box at the exact threshold
    input_data = np.array([[0, 0, 32, 32], [0, 0, 96, 96]], dtype=np.float32)
    expected_output = np.array([1, 2], dtype=np.int_)

def test_varied_sizes():
    # Varied bounding box sizes
    input_data = np.array([[0, 0, 10, 10], [0, 0, 32, 32], [0, 0, 50, 50], [0, 0, 96, 96], [0, 0, 150, 150]], dtype=np.float32)
    expected_output = np.array([1, 1, 2, 2, 3], dtype=np.int_)

def test_comprehensive():
    # Comprehensive test combining various edge cases and regular cases
    input_data = np.array([[0, 0, 10, 10], [0, 0, 32, 32], [0, 0, 50, 50], [0, 0, 96, 96], [0, 0, 150, 150], [0, 0, 0, 0], [-10, -10, 10, 10], [0.5, 0.5, 32.5, 32.5]], dtype=np.float32)
    expected_output = np.array([1, 1, 2, 2, 3, 1, 2, 2], dtype=np.int_)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

Codeflash

o3-mini
We can speed up the function by replacing the multiple in‐place assignments with a single nested np.where call. This avoids creating several intermediate boolean masks and extra writes to the result array. The core arithmetic (computing areas) remains vectorized, but now the category assignment is done in one go.

Note: The function still validates input shape and returns the same result exactly.
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Feb 3, 2025
@codeflash-ai codeflash-ai bot requested a review from misrasaurabh1 February 3, 2025 03:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⚡️ codeflash Optimization PR opened by Codeflash AI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants