github roboflow/supervision 0.30.3
supervision-0.30.3

5 hours ago

0.30.3: Pose, VLM, and video/CSV crash and correctness fixes

supervision 0.30.3 is a bug-fix release closing crash and silent-correctness gaps across pose estimation, VLM parsing, video/CSV output, and geometry utilities. Non-finite key points — how pose estimators report an undetected joint — no longer produce duplicate poses that survive sv.KeyPoints.with_nms, or crash the key point annotators outright. sv.Detections.from_vlm now orders backwards box corners, closing a bug where such a box scored a false 0.0 IoU and both survived NMS as a duplicate and counted as a total miss in mAP. sv.TraceAnnotator and sv.CSVSink no longer crash or silently drop columns on the first frame with no detections — a case every non-ByteTrack tracker pipeline hits. sv.process_video no longer hangs forever when max_frames exceeds the video length. Continuing 0.30.2's numeric-correctness theme, sv.pad_boxes and sv.scale_boxes are fixed against integer overflow. No breaking API changes, no new public API.

✨ Spotlights / highlights

Non-finite key points no longer produce duplicate poses or crash annotators

sv.KeyPoints.with_nms tested key point validity with xy == 0 alone, and NaN — how pose estimators report an undetected joint — is not 0. The stale joint stayed in the NMS box, so a duplicate skeleton scored False on every IoU comparison against it and survived suppression. The key point annotators (sv.VertexAnnotator, sv.EdgeAnnotator, sv.VertexLabelAnnotator, the sv.VertexEllipse*Annotator family) had the matching crash: a single undetected joint raised ValueError: cannot convert float NaN to integer for the whole frame. Both now skip non-finite coordinates, matching sv.KeyPoints.as_detections.

keypoints = sv.KeyPoints(xy=xy, confidence=confidence)
keypoints.with_nms(
    threshold=0.5
)  # duplicate skeletons with a NaN joint are now suppressed

sv.Detections.from_vlm no longer scores a false IoU miss on backwards box corners

A VLM that emits a corner pair backwards produced an xyxy row with x_min > x_max. Nothing downstream caught it: sv.box_iou_batch clamps intersection width at zero, so the box scored 0.0 IoU against itself — surviving NMS as a duplicate and counting as a total miss in mAP — while box_area still reported a plausible positive value. Every VLM parser now orders each box's corners before returning it.

sv.TraceAnnotator and sv.CSVSink no longer crash or silently corrupt output on an empty-detections frame

sv.TraceAnnotator.annotate raised ValueError: The tracker_id field is missing on the first frame with no detections, for every tracker except sv.ByteTrack. Such a frame now draws nothing and still advances the frame counter, so trace_length stays a window over elapsed frames rather than only over populated ones. sv.CSVSink had a quieter failure: an empty batch fixed the CSV header without the data/custom_data columns, and every later row was silently truncated to that schema — dropping fields like class_name for the whole file. The header is now fixed by the first batch that actually carries detections.

sv.process_video no longer hangs forever when max_frames exceeds the video length

The reader thread failed on the out-of-range end before enqueuing its sentinel, leaving the main loop blocked on the read queue indefinitely. max_frames is now capped at the video length, and any reader-thread error surfaces as RuntimeError("Reader thread raised: ...") instead of stalling the call.

Integer-coordinate overflow fixed in sv.pad_boxes and sv.scale_boxes

Both computed intermediate values that could overflow or silently wrap for large integer coordinates (e.g. large int32/uint16/int64 boxes). Both now use overflow-safe arithmetic.

xyxy = np.array([[10, 20, 30, 40]], dtype=np.int64)
sv.pad_boxes(xyxy=xyxy, px=5, py=10)  # int64 output, no wraparound

pad_boxes changes return dtype for integer input — see the migration guide below.

sv.scale_image and sv.resize_image(keep_aspect_ratio=True) no longer crash on an extreme aspect ratio or tiny scale factor

A small enough factor — or an aspect ratio too extreme for the target box — could round an output axis down to 0, and cv2.resize raised an assertion naming nothing the caller passed. Each axis now keeps at least one pixel. Two callers inherit the fix: sv.letterbox_image could not fill the resolution it was asked for, and sv.CropAnnotator with scale_factor < 1 aborted the whole frame as soon as one detection box was a few pixels across.

🔄 Migration guide

No breaking API changes. One fix changes return dtype for integer input:

  • sv.pad_boxes — integer xyxy now returns int64 (or float64 if a padded coordinate exceeds the int64 range), instead of the input's original integer dtype, which could silently overflow or wrap for small dtypes like int16/uint8.

If your code assumes pad_boxes preserves the input's exact dtype (e.g. reusing the result as an int16 array), cast explicitly: sv.pad_boxes(...).astype(np.int16).

sv.scale_boxes also fixes an integer-overflow bug, but its return dtype was already float64 for integer input before this release — unaffected.

📝 Notable changes

🔧 Fixed

  • sv.Detections.from_ultralytics now assigns the placeholder class ID 0 to every mask in a masks-only result, instead of sequential IDs across masks that belong to the same image. (#2566)
  • sv.pad_boxes now computes integer-coordinate padding without overflow or unsigned casting errors. (#2565)
  • sv.scale_image and sv.resize_image(keep_aspect_ratio=True) no longer derive a zero-sized target, fixing a crash reached via sv.letterbox_image and sv.CropAnnotator. (#2564)
  • sv.KeyPoints.with_nms no longer stops suppressing duplicate skeletons as soon as a key point is non-finite. (#2563)
  • sv.tint_image no longer tints the caller's own image array in place. (#2562)
  • sv.LineZone no longer consumes the triggering_anchors iterable during validation, so a generator or map passed in is no longer exhausted before the first trigger() call. (#2561)
  • The key point annotators now skip key points whose coordinates are not finite instead of raising ValueError. (#2560)
  • sv.PolygonZone now rejects a polygon with fewer than three vertices instead of building a zone that can never trigger; sv.Detections.from_vlm now orders each parsed box's corners. (#2554)
  • sv.ClassificationDataset.as_folder_structure now rejects images that would overwrite the same class-relative filename before writing any files. (#2551)
  • sv.process_video no longer hangs forever when max_frames is larger than the number of frames in the video. (#2546)
  • sv.filter_polygons_by_area and sv.approximate_polygon now preserve local geometry for large-origin integer and float64 polygons. (#2542)
  • sv.TraceAnnotator.annotate no longer raises on an empty-detections frame; sv.CSVSink no longer lets an empty batch fix the CSV header. (#2539)
  • sv.scale_boxes now preserves exact integer intermediates, preventing overflow and scaled-corner rounding errors for large integer-coordinate boxes. (#2541)
  • A release's own version-pinned docs no longer show the outdated-version banner on the day it ships, and the docs-publish and canonical-backfill workflows now share one gh-pages write lock instead of racing each other. (#2536)

🏆 Contributors

  • kevin (@kevin9327) — fixed KeyPoints.with_nms/key point annotators crashing on non-finite key points, tint_image image aliasing, LineZone generator exhaustion, and the scale_image/resize_image zero-target crash
  • Durgamani Sasikumar (@tedo001, LinkedIn) — fixed PolygonZone/from_vlm box-corner ordering and the TraceAnnotator/CSVSink empty-frame crash
  • S B Pranay (@pranaysb, LinkedIn) — fixed integer overflow in scale_boxes and dtype loss in filter_polygons_by_area/approximate_polygon
  • JiantaoPeng (@PengJianT) — fixed from_ultralytics masks-only class ID sizing
  • trueoneplusone (@trueoneplusone) — fixed integer overflow in pad_boxes
  • Andrew Barnes (@Bortlesboat, LinkedIn) — fixed classification export filename collisions
  • Abhijith Neil Abraham (@abhijithneilabraham, LinkedIn) — fixed process_video hanging when max_frames exceeds the video length
  • Jirka Borovec (@Borda, LinkedIn) — fixed the release-day outdated-docs banner

Full changelog: 0.30.2...0.30.3

Don't miss a new supervision release

NewReleases is sending notifications on new releases.