fix(workflows): preserve image dimensions on empty VLM detections - #2892
Open
davidnichols-ops wants to merge 3 commits into
Open
fix(workflows): preserve image dimensions on empty VLM detections#2892davidnichols-ops wants to merge 3 commits into
davidnichols-ops wants to merge 3 commits into
Conversation
davidnichols-ops
requested review from
PawelPeczek-Roboflow,
dkosowski87,
grzegorz-roboflow,
hansent,
probicheaux,
rafel-roboflow and
yeldarby
as code owners
August 31, 2026 00:10
Contributor
Author
|
Because this comes from a fork, could someone approve the Actions run before merging? I'm trying to document the actual external-contributor path rather than infer it from the workflow YAML. |
The numpy serialiser returned {"width": null, "height": null} for empty
VLM-as-detector results because sv.Detections.data is per-row — zero rows
means the serialiser per-row loop never executes and IMAGE_DIMENSIONS_KEY
is never read. The tensor-native path stores image dimensions in
image_metadata (a dict on the Detections object), so it correctly emits
real width/height for empty detections.
Fix: store IMAGE_DIMENSIONS_KEY in sv.Detections.metadata (a free-form
dict that survives zero rows) and teach the numpy serialiser to read it as
a fallback when the per-row loop yields no rows.
Before: serialise_sv_detections(parse_...(empty_input)) returns
{image: {width: null, height: null}, predictions: []}
After: serialise_sv_detections(parse_...(empty_input)) returns
{image: {width: 640, height: 480}, predictions: []}
All 7 VLM-as-detector parsers updated (anthropic, gemini, muse, openai,
qwen, spacexai, v2/llm). 1905 tests pass.
davidnichols-ops
force-pushed
the
fix/vlm-detectors-empty-lineage
branch
from
August 31, 2026 14:36
840f480 to
1049093
Compare
SpaceXAI was the only VLM-as-detector parser without a dedicated test file. Adds 23 tests covering: - extract_spacexai_detection_entries (list, wrapper, empty, invalid) - convert_spacexai_detection_to_pixel_xyxy (scaling, clamping, non-integer) - parse_spacexai_object_detection_response (empty, wrapper, assembly, clamping, error cases, image metadata on empty)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The numpy serialiser returned
{"width": null, "height": null}for empty VLM-as-detector results, while the tensor-native path returned real image dimensions. This PR fixes the discrepancy.Root Cause
sv.Detections.datais per-row. For zero-row detections, the serialiser's per-row loop never executes, soIMAGE_DIMENSIONS_KEYstored indatais never read. The tensor-native path avoids this because it stores image dimensions inimage_metadata(a dict on the Detections object, not per-row).What Changed
New helper
empty_detections_with_image_metadata()inutils.py— creates a zero-rowsv.Detectionswithmetadata={IMAGE_DIMENSIONS_KEY: [h, w]}.sv.Detections.metadatais a free-form dict that survives zero rows (unlikedata, which is per-row).Serialiser fallback in
serialise_sv_detections()— after the per-row loop, ifimage_dimensionsis stillNone, read fromdetections.metadata.get(IMAGE_DIMENSIONS_KEY).All 7 VLM-as-detector parsers (anthropic, gemini, muse, openai, qwen, spacexai, v2/llm) — replaced
return sv.Detections.empty()withreturn empty_detections_with_image_metadata(h, w).Before
After
Testing
Notes
sv.Detections.metadatais an existing supervision field that was unused in this codebasesv.Detections.merge()filters empty detections before merging, so metadata on empty detections does not interfere with merge operationsint()call in the serialiser replaces.item()to handle both numpy arrays and plain Python lists from metadata