Skip to content

Commit 02ad98a

Browse files
f-wrightAuto-format Bot
andauthored
Add documentation and testing for Bounding Box mode (#396)
This PR adds documentation for bounding box mode now that it's enabled for all users. Also, it improves the testing coverage on bounding box mode. There were some disabled tests that needed to be turned on now that the mode is available for all users. Also, additional test coverage was added on adding bbox labels. --------- Co-authored-by: Auto-format Bot <autoformatbot@groundlight.ai>
1 parent 43ff758 commit 02ad98a

File tree

6 files changed

+156
-18
lines changed

6 files changed

+156
-18
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Bounding Box Detectors
2+
3+
Bounding box detectors are used to detect and localize objects in an image by returning bounding boxes around each detected object.
4+
5+
```python notest
6+
from groundlight import ExperimentalApi
7+
gl = ExperimentalApi()
8+
9+
# highlight-start
10+
detector = gl.create_bounding_box_detector(
11+
name="dog-detector",
12+
query="Draw a bounding box around each dog in the image",
13+
class_name="dog",
14+
max_num_bboxes=25,
15+
confidence_threshold=0.6,
16+
)
17+
# highlight-end
18+
```
19+
20+
Bounding box detectors should be provided with a query that asks the model to identify and localize objects in an image, such as "Draw a bounding box around each dog in the image".
21+
22+
The `class_name` parameter specifies the type of object to detect, and this label will be assigned to each returned bounding box.
23+
24+
The `max_num_bboxes` parameter sets the maximum number of bounding boxes that the detector will return (default: 10). If there are more objects in the image than the maximum, the result label will be `GREATER_THAN_MAX`.
25+
26+
The `confidence_threshold` parameter sets the minimum confidence level required for the ML model's predictions. If the model's confidence falls below this threshold, the query will be sent for human review.
27+
28+
:::note
29+
Bounding Box Detectors are currently in beta and available through the `ExperimentalApi`. They are available on [Business and Enterprise plans](https://www.groundlight.ai/pricing).
30+
:::
31+
32+
## Submit an Image Query to a Bounding Box Detector
33+
34+
Now that you have created a bounding box detector, you can submit an image query to it.
35+
36+
```python notest
37+
from groundlight import ExperimentalApi
38+
gl = ExperimentalApi()
39+
40+
detector = gl.get_detector_by_name("dog-detector")
41+
42+
# highlight-start
43+
# Detect dogs in an image
44+
image_query = gl.submit_image_query(detector, "path/to/image.jpg")
45+
# highlight-end
46+
47+
print(f"Label: {image_query.result.label}")
48+
print(f"Confidence: {image_query.result.confidence}")
49+
print(f"Bounding Boxes: {image_query.rois}")
50+
```
51+
52+
For bounding box detectors, the `label` attribute of the result object will be one of:
53+
- `NO_OBJECTS`: No objects of the specified class were detected in the image
54+
- `BOUNDING_BOX`: Objects were detected and bounding boxes are available in `image_query.rois`
55+
- `GREATER_THAN_MAX`: More objects were detected than the `max_num_bboxes` limit
56+
- `UNCLEAR`: The result was unclear
57+
58+
The `rois` (regions of interest) attribute contains the list of bounding boxes, each with:
59+
- `geometry`: Bounding box coordinates (`left`, `top`, `right`, `bottom`) as values between 0 and 1
60+
- `label`: The class name of the detected object
61+
- `score`: Confidence score for this specific object
62+
63+
<!-- TODO: display an example image with bounding boxes -->
64+
65+
:::tip Drawing Bounding Boxes
66+
You can visualize the bounding boxes returned by bounding box detectors using a library like OpenCV. Here's an example of how to draw bounding boxes on an image:
67+
68+
```python notest
69+
import cv2
70+
import numpy as np
71+
72+
def draw_bounding_boxes(image_path, rois):
73+
"""
74+
Draw bounding boxes on an image based on ROIs returned from a bounding box detector.
75+
76+
Args:
77+
image_path: Path to the image file
78+
rois: List of ROI objects returned from image_query.rois
79+
"""
80+
image = cv2.imread(image_path)
81+
if image is None:
82+
raise ValueError(f"Could not read image from {image_path}")
83+
height, width = image.shape[:2]
84+
85+
# Draw bounding boxes
86+
for roi in rois:
87+
x1 = int(roi.geometry.left * width)
88+
y1 = int(roi.geometry.top * height)
89+
x2 = int(roi.geometry.right * width)
90+
y2 = int(roi.geometry.bottom * height)
91+
cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
92+
label_text = f"{roi.label}: {roi.score:.2f}"
93+
cv2.putText(image, label_text, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
94+
95+
# Display the image
96+
cv2.imshow("Image with Bounding Boxes", image)
97+
cv2.waitKey(0)
98+
cv2.destroyAllWindows()
99+
100+
# Example usage:
101+
# image_query = gl.submit_image_query(detector, "path/to/image.jpg")
102+
# draw_bounding_boxes("path/to/image.jpg", image_query.rois)
103+
```
104+
:::
105+
106+
## Add a Label to a Bounding Box Detector
107+
108+
The Groundlight API allows you to add labels to image queries, including Region of Interest (ROI) data.
109+
When adding a label to a bounding box detector, you must include the ROIs that correspond to the objects in the image.
110+
111+
```python notest
112+
from groundlight import ExperimentalApi
113+
gl = ExperimentalApi()
114+
115+
# highlight-start
116+
# Add a bounding box label with corresponding ROIs to the image query from the previous example.
117+
# ROIs are specified as (left, top) and (right, bottom) coordinates, with values
118+
# between 0 and 1 representing the percentage of the image width and height.
119+
roi1 = gl.create_roi("dog", (0.1, 0.2), (0.3, 0.4))
120+
roi2 = gl.create_roi("dog", (0.5, 0.3), (0.7, 0.6))
121+
rois = [roi1, roi2]
122+
gl.add_label(image_query, label="BOUNDING_BOX", rois=rois)
123+
# highlight-end
124+
```
125+
126+
Valid label values for bounding box detectors are:
127+
- `"NO_OBJECTS"`: Use when there are no objects of the target class in the image (no ROIs needed)
128+
- `"BOUNDING_BOX"`: Use when objects are present and you are providing ROIs
129+
- `"GREATER_THAN_MAX"`: Use when there are more objects than `max_num_bboxes`
130+
- `"UNCLEAR"`: Use when the image is unclear or the answer cannot be determined

docs/docs/answer-modes/answer-modes.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ Groundlight offers several detector modalities to suit different computer vision
66
- **[Binary Detectors](1-binary-detectors.md)**: Learn how to create detectors that answer yes/no questions about images.
77
- **[Multiple Choice (Choose One) Detectors](2-multi-choice-detectors.md)**: Create detectors that select one answer from a predefined list of options.
88
- **[Count Detectors](3-counting-detectors.md)**: Use detectors to count the number of objects present in an image - and return bounding boxes around the counted objects.
9-
<!-- 4. [Text Recognition Detectors](4-text-recognition-detectors.md) -->
10-
11-
<!-- TODO: object detection modes -->
9+
- **[Bounding Box Detectors](4-bounding-box-detectors.md)**: Detectors to identify and localize objects in an image.
10+
<!-- 5. [Text Recognition Detectors](4-text-recognition-detectors.md) -->

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ packages = [
99
{include = "**/*.py", from = "src"},
1010
]
1111
readme = "README.md"
12-
version = "0.23.3"
12+
version = "0.23.4"
1313

1414
[tool.poetry.dependencies]
1515
# For certifi, use ">=" instead of "^" since it upgrades its "major version" every year, not really following semver

src/groundlight/client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ def add_label(
11931193
Provide a new label (annotation) for an image query. This is used to provide ground-truth labels
11941194
for training detectors, or to correct the results of detectors.
11951195
1196-
**Example usage**::
1196+
**Example usage for binary detectors**::
11971197
11981198
gl = Groundlight()
11991199
@@ -1208,12 +1208,15 @@ def add_label(
12081208
rois = [ROI(x=100, y=100, width=50, height=50)]
12091209
gl.add_label(image_query, "YES", rois=rois)
12101210
1211+
Examples for other answer modes can be found in the documentation for each of the modes.
1212+
12111213
:param image_query: Either an ImageQuery object (returned from methods like
12121214
`ask_ml`) or an image query ID string starting with "iq_".
12131215
12141216
:param label: The label value to assign, typically "YES" or "NO" for binary
12151217
classification detectors. For multi-class detectors, use one of
1216-
the defined class names.
1218+
the defined class names. See answer mode documentation for all
1219+
possible label options for all modes.
12171220
12181221
:param rois: Optional list of ROI objects defining regions of interest in the
12191222
image. Each ROI specifies a bounding box with x, y coordinates

test/unit/test_experimental.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,6 @@ def test_text_recognition_detector(gl_experimental: ExperimentalApi):
103103
assert mc_iq.result.text is not None
104104

105105

106-
@pytest.mark.skip(
107-
reason=(
108-
"General users currently currently can't use bounding box detectors. If you have questions, reach out"
109-
" to Groundlight support, or upgrade your plan."
110-
)
111-
)
112106
def test_bounding_box_detector(gl_experimental: ExperimentalApi):
113107
"""
114108
Verify that we can create and submit to a bounding box detector
@@ -123,12 +117,6 @@ def test_bounding_box_detector(gl_experimental: ExperimentalApi):
123117
assert bbox_iq.rois is not None
124118

125119

126-
@pytest.mark.skip(
127-
reason=(
128-
"General users currently currently can't use bounding box detectors. If you have questions, reach out"
129-
" to Groundlight support, or upgrade your plan."
130-
)
131-
)
132120
def test_bounding_box_detector_async(gl_experimental: ExperimentalApi):
133121
"""
134122
Verify that we can create and submit to a bounding box detector with ask_async

test/unit/test_labels.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,24 @@ def test_multiclass_labels(gl_experimental: ExperimentalApi):
6666
gl_experimental.add_label(iq1, "MAYBE")
6767

6868

69+
def test_bounding_box_labels(gl_experimental: ExperimentalApi):
70+
name = f"Test bounding box labels{datetime.utcnow()}"
71+
det = gl_experimental.create_bounding_box_detector(name, "test_query", "test_class")
72+
iq1 = gl_experimental.submit_image_query(det, "test/assets/cat.jpeg")
73+
gl_experimental.add_label(iq1, "NO_OBJECTS")
74+
iq1 = gl_experimental.get_image_query(iq1.id)
75+
assert iq1.result.label == "NO_OBJECTS"
76+
gl_experimental.add_label(
77+
iq1,
78+
"BOUNDING_BOX",
79+
rois=[gl_experimental.create_roi(label="test_class", top_left=(0.1, 0.1), bottom_right=(0.6, 0.6))],
80+
)
81+
iq1 = gl_experimental.get_image_query(iq1.id)
82+
assert iq1.result.label == "BOUNDING_BOX"
83+
with pytest.raises(ApiException) as _:
84+
gl_experimental.add_label(iq1, "MAYBE")
85+
86+
6987
def test_text_recognition_labels(gl_experimental: ExperimentalApi):
7088
name = f"Test text recognition labels{datetime.utcnow()}"
7189
det = gl_experimental.create_text_recognition_detector(name, "test_query")

0 commit comments

Comments
 (0)