fix: return empty int ndarray instead of None for class_id on empty VLM parse (#2239)
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 --------- Co-authored-by: YousefZahran1 <youssefzahran.y@gmail.com> Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com>
This commit is contained in:
parent
f4b0767a88
commit
01ec36b31d
|
|
@ -202,7 +202,7 @@ def validate_vlm_parameters(vlm: VLM | str, result: Any, kwargs: dict[str, Any])
|
|||
|
||||
def from_paligemma(
|
||||
result: str, resolution_wh: tuple[int, int], classes: list[str] | None = None
|
||||
) -> tuple[npt.NDArray[Any], npt.NDArray[Any] | None, npt.NDArray[Any]]:
|
||||
) -> tuple[npt.NDArray[Any], npt.NDArray[Any], npt.NDArray[Any]]:
|
||||
"""
|
||||
Parse bounding boxes from paligemma-formatted text, scale them to the specified
|
||||
resolution, and optionally filter by classes.
|
||||
|
|
@ -229,7 +229,7 @@ def from_paligemma(
|
|||
matches = np.array(matches) if matches else np.empty((0, 5))
|
||||
|
||||
if matches.shape[0] == 0:
|
||||
return np.empty((0, 4)), None, np.empty(0, dtype=str)
|
||||
return np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0, dtype=str)
|
||||
|
||||
xyxy, class_name = matches[:, [1, 0, 3, 2]], matches[:, 4]
|
||||
xyxy = xyxy.astype(int) / 1024 * np.array([w, h, w, h])
|
||||
|
|
@ -626,7 +626,7 @@ def from_google_gemini_2_0(
|
|||
try:
|
||||
data = json.loads(result)
|
||||
except json.JSONDecodeError:
|
||||
return np.empty((0, 4)), None, np.empty((0,), dtype=str)
|
||||
return np.empty((0, 4)), np.empty((0,), dtype=int), np.empty((0,), dtype=str)
|
||||
|
||||
labels = []
|
||||
xyxy = []
|
||||
|
|
@ -640,7 +640,7 @@ def from_google_gemini_2_0(
|
|||
xyxy.append([box[1], box[0], box[3], box[2]])
|
||||
|
||||
if len(xyxy) == 0:
|
||||
return np.empty((0, 4)), None, np.empty((0,), dtype=str)
|
||||
return np.empty((0, 4)), np.empty((0,), dtype=int), np.empty((0,), dtype=str)
|
||||
|
||||
xyxy = denormalize_boxes(
|
||||
np.array(xyxy, dtype=np.float64),
|
||||
|
|
|
|||
|
|
@ -27,49 +27,49 @@ from supervision.detection.vlm import (
|
|||
"",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0).astype(str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0).astype(str)),
|
||||
), # empty text
|
||||
(
|
||||
does_not_raise(),
|
||||
"",
|
||||
(1000, 1000),
|
||||
["cat", "dog"],
|
||||
(np.empty((0, 4)), None, np.empty(0).astype(str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0).astype(str)),
|
||||
), # empty text, classes
|
||||
(
|
||||
does_not_raise(),
|
||||
"\n",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0).astype(str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0).astype(str)),
|
||||
), # newline only
|
||||
(
|
||||
does_not_raise(),
|
||||
"the quick brown fox jumps over the lazy dog.",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0).astype(str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0).astype(str)),
|
||||
), # random text, no location
|
||||
(
|
||||
does_not_raise(),
|
||||
"<loc0256><loc0768><loc0768> cat",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0).astype(str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0).astype(str)),
|
||||
), # partial location
|
||||
(
|
||||
does_not_raise(),
|
||||
"<loc0256><loc0256><loc0768><loc0768><loc0768> cat",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0).astype(str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0).astype(str)),
|
||||
), # extra loc
|
||||
(
|
||||
does_not_raise(),
|
||||
"<loc0256><loc0256><loc0768><loc0768>",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0).astype(str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0).astype(str)),
|
||||
), # no class
|
||||
(
|
||||
does_not_raise(),
|
||||
|
|
@ -436,21 +436,21 @@ def test_from_qwen_2_5_vl(
|
|||
"random text",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0, dtype=str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0, dtype=str)),
|
||||
), # random text without JSON format
|
||||
(
|
||||
does_not_raise(),
|
||||
"```json\ninvalid json\n```",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0, dtype=str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0, dtype=str)),
|
||||
), # invalid JSON within code blocks
|
||||
(
|
||||
does_not_raise(),
|
||||
"```json\n[]\n```",
|
||||
(1000, 1000),
|
||||
None,
|
||||
(np.empty((0, 4)), None, np.empty(0, dtype=str)),
|
||||
(np.empty((0, 4)), np.empty((0,), dtype=int), np.empty(0, dtype=str)),
|
||||
), # empty JSON array
|
||||
(
|
||||
does_not_raise(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue