fix: return empty int ndarray instead of None for class_id on empty VLM parse#2239
Conversation
… parse When from_paligemma or from_google_gemini_2_0 find no detections (no regex matches, JSON decode error, or empty bounding-box list), they previously returned None for class_id. All other early-exit and filter paths already return a zero-length ndarray of dtype int. This inconsistency causes downstream AttributeError when callers unconditionally call .shape or iterate over the result. Affected paths: - from_paligemma: matches.shape[0] == 0 branch - from_google_gemini_2_0: JSONDecodeError branch and len(xyxy) == 0 branch Closes roboflow#2219
@YousefZahran1 could you pls sign ^^ 🦝 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #2239 +/- ##
=======================================
Coverage 78% 78%
=======================================
Files 66 66
Lines 8374 8374
=======================================
Hits 6501 6501
Misses 1873 1873 🚀 New features to boost your workflow:
|
|
"CLA signed! Ready for review when you have a moment. |
Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR standardizes the “empty parse” return contract for VLM parsers so class_id is always a zero-length np.ndarray of dtype=int (instead of None) when no detections are produced, aligning PaliGemma and Gemini 2.0 behavior with other parsers and preventing downstream AttributeError/TypeError on common empty-detection paths (see #2219).
Changes:
- Update
from_paligemma(...)to returnnp.empty((0,), dtype=int)forclass_idwhen regex matching yields zero detections. - Update
from_google_gemini_2_0(...)to returnnp.empty((0,), dtype=int)forclass_idon JSON decode failure and when no boxes are parsed. - Adjust unit test expectations for those empty-result scenarios.
Quality assessment (n/5):
- Code quality: 4/5
- Testing: 4/5
- Docs: 4/5
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/supervision/detection/vlm.py |
Align empty-result class_id to an empty int ndarray for PaliGemma and Gemini 2.0 error/empty paths. |
tests/detection/test_vlm.py |
Update expected tuples for empty-result cases to match the new class_id contract. |
Comments suppressed due to low confidence (1)
src/supervision/detection/vlm.py:630
- In the
classes is not Nonebranch,class_id = np.array([...])does not specifydtype=int, so if the class filter removes all detections it returns an empty float array. This undermines the new behavior of returning an emptyintndarray on other empty-result paths; consider forcingdtype=intfor consistent downstream handling.
try:
data = json.loads(result)
except json.JSONDecodeError:
return np.empty((0, 4)), np.empty((0,), dtype=int), np.empty((0,), dtype=str)
What
On empty-parse paths in two VLM parsers,
class_idwas returned asNoneinstead of a zero-lengthndarrayof dtypeint, inconsistent with every other exit point in these functions and with the convention established byfrom_google_gemini_2_5,from_qwen_2_5_vl, andfrom_qwen_3_vl.Affected code paths
from_paligemmaNonenp.empty((0,), dtype=int)from_google_gemini_2_0JSONDecodeErrorNonenp.empty((0,), dtype=int)from_google_gemini_2_0Nonenp.empty((0,), dtype=int)Why it matters
Callers that do
class_id.shape,len(class_id), orclass_id[mask]immediately after parsing will get anAttributeError/TypeErrorwhenever zero detections are returned — a very common path in production (frame with nothing to detect). DownstreamDetections.from_vlm(...)pipelines hit this at runtime while unit tests may pass because they check happy paths.Changes
src/supervision/detection/vlm.py— 3 one-line fixes (no logic change)tests/detection/test_vlm.py— updated 10 test expectations to match the new contractAll 88 tests in
tests/detection/test_vlm.pypass.Fixes #2219