Skip to content

Astra instance-segmentation - #3006

Merged
PawelPeczek-Roboflow merged 9 commits into
fast-track/post-v1.6.0-pt1from
feat/openai-segmentation-task-v1.6.0
Sep 17, 2026
Merged

PawelPeczek-Roboflow merged 9 commits into
fast-track/post-v1.6.0-pt1from
feat/openai-segmentation-task-v1.6.0

Conversation

@PawelPeczek-Roboflow

@PawelPeczek-Roboflow PawelPeczek-Roboflow commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Mirror of #2997 but with proper base commit and cherry-picked work from @Erol444 on top of last release.

Type of Change

Look at #2997

Testing

  • I have tested this change locally
  • I have added/updated tests for this change

Test details:

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code where necessary, particularly in hard-to-understand areas
  • My changes generate no new warnings or errors
  • I have updated the documentation accordingly (if applicable)

Additional Context

Erol444 and others added 5 commits September 16, 2026 19:50
`roboflow_core/open_ai@v7` gains an `instance-segmentation` task: the
model returns one outline polygon per instance of the requested classes
and the block decodes it into masked predictions
(`instance_segmentation_prediction` kind), so the result plugs straight
into mask/polygon visualization, crops and dataset upload.

Prompt contract (shared, in `common/vlm_decoding/segmentation.py`): a
flat `[x1, y1, x2, y2, ...]` vertex list in absolute pixel coordinates of
the uploaded image, wrapped as `{"segmentations": [{"label", "polygon"}]}`
and enforced via structured outputs. Chosen from a sweep on the 40
vlm-exam images that carry mask ground truth: absolute pixels, 0-1 floats
and 0-1000 ints tie on mask AP@50 (0.850 / 0.853 / 0.828, no significant
pairwise difference), but floats cost ~75% more output tokens and 0-1000
quantisation loses on small objects in large frames. The image is sent at
its ORIGINAL resolution as JPEG - unlike detection's 2048px downscale -
because pre-resizing cost -0.14 mask AP@50 on the images above the cap.

The task defaults to `gpt-6-astra` when the manifest omits
`model_version` (an explicit literal or selector is always honoured); the
block-wide `gpt-5.1` default predates the task.

Decoding is lenient in the same way as detection: `segmentations` /
`detections` wrappers or a bare list, flat / pair / point vertex shapes,
polygon key aliases, numeric strings, out-of-frame clamping; degenerate
polygons are skipped and an answer with no usable polygon surfaces as
`error_status`. Masks are rasterised with `sv.polygon_to_mask` at the
original resolution. Under `ENABLE_TENSOR_DATA_REPRESENTATION` the decode
is handed back as `InstanceDetections` with a dense boolean mask stack.

The shared `predictions` kind union grows by
`instance_segmentation_prediction`; the per-block tests pinning it are
updated accordingly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit f1d2462)
The shared `describe_vlm_prediction_outputs` / `actual_vlm_prediction_outputs`
helpers hard-coded the `predictions` kind union, so widening it for
segmentation made every VLM block (Claude, Gemini, Qwen, ...) advertise
`instance_segmentation_prediction` although none of them can produce it,
and dragged their tests along.

The helpers now take the block's supported task list and derive the union
from it (`prediction_kinds_for_tasks`); blocks that pass nothing keep the
detection + classification union they always declared. Only `open_ai@v7`
passes its task list, so it is the only block whose declared kinds change,
and the other nine test suites are back to their `main` state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit bbee50e)
- Skip polygons that enclose no area (repeated, collinear or clipped-away
  vertices): `fillPoly` would paint them as a one-pixel line and the
  serialiser would then drop the instance silently. Drop them at decode
  time, logged, and keep the empty-mask check as a second guard.
- Rasterise into a preallocated `(N, H, W)` stack instead of a list plus
  `np.stack`, so the dense masks are never held twice.
- Keep the frame size on `metadata[IMAGE_DIMENSIONS_KEY]` for an empty
  result, as `empty_detections_with_image_metadata` does, so the numpy and
  tensor-native representations agree.
- Reword the default-model comment: the block default was never
  benchmarked for segmentation, it is not incapable of it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 8f009a0)
Review feedback: the (N, H, W) mask stack was sized by the raw entry count
before polygons were validated, and dense masks cost 1 B/pixel/instance
(100 instances on 4000x3000 is ~1.2 GB) with no ceiling.

Rather than cap it, stop rasterising: every entry is validated first
(well-formed polygon, non-zero enclosed area), then each survivor is encoded
polygon -> COCO RLE with `pycocotools.frPyObjects` - no dense mask is ever
materialised, so memory is proportional to the run count however many
instances the model returns. The decoder now emits `mask=None` plus
`data["rle_mask"]`, the RLE-first contract of `segment_anything3@v3` /
`instance_segmentation@v4`: the visualization, crop and upload blocks decode
lazily per instance, the Auto Label worker keeps native RLE losslessly, and
the app stores RLE anyway. This also removes the previous lossy
polygon -> raster -> contour -> polygon round trip in serialization.

- `predictions` for the task is declared as
  `[rle_instance_segmentation_prediction, instance_segmentation_prediction]`
  (RLE first, dense kept so consumers declared on it still connect).
- Boxes come from the RLE (`toBbox`) so box and mask always agree.
- Tensor-native carrier is `InstanceDetections` with `InstancesRLEMasks`.
- Test mask reader decodes RLE in both representations.

Verified with a real Astra call through the ExecutionEngine on a 49-chip
image: 49 instances, `mask=None`, 49 RLEs, serialized as `rle_mask`, mask +
polygon visualizations rendered from RLE.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 816109e)
CI runs isort 5.13.2, which keeps the InstanceDetections import on one
line where isort 8 wraps it; match the pinned version so static code
analysis passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 2a7eb60)
@PawelPeczek-Roboflow PawelPeczek-Roboflow changed the title Feat/OpenAI segmentation task v1.6.0 Atrsa instance-segmentation Sep 16, 2026
@PawelPeczek-Roboflow

Copy link
Copy Markdown
Collaborator Author

I am approving this PR to be merged to fast track and deployed.
Deploying on staging to test e2e

@PawelPeczek-Roboflow PawelPeczek-Roboflow changed the title Atrsa instance-segmentation Astra instance-segmentation Sep 17, 2026
@PawelPeczek-Roboflow
PawelPeczek-Roboflow merged commit a80e66e into fast-track/post-v1.6.0-pt1 Sep 17, 2026
7 checks passed
@PawelPeczek-Roboflow
PawelPeczek-Roboflow deleted the feat/openai-segmentation-task-v1.6.0 branch September 17, 2026 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants