What’s New

supervision 0.29.1

0.29.1

supervision-0.29.1

Added
  • Add `KeyPoints.with_nms()` method to remove duplicate skeletons using non-maximum suppression on axis-aligned bounding boxes derived from keypoints, supporting `class_agnostic` mode and any `OverlapMetric`
Changed
  • Optimize `sv.HaloAnnotator` to use the same optimized CompactMask paint path as `MaskAnnotator`, reducing memory materialization
  • Optimize mask IoU computation to use matrix multiplication on flattened masks instead of explicit tensor allocation
  • Vectorize `sv.mask_to_xyxy` and `sv.KeyPoints.as_detections` to use batched NumPy operations instead of per-element loops
Fixed
  • Fix `sv.DetectionDataset.as_pascal_voc` mutating bounding boxes by shifting them +1 px in-place on each export
  • Fix `sv.Precision` and `sv.F1Score` to correctly count background false positives under `MICRO` and `MACRO` averaging modes
  • Fix `sv.DetectionsSmoother` raising when detections have no confidence scores
  • Fix `sv.Detections.from_vlm` to degrade gracefully on malformed Gemini and Qwen output instead of raising `TypeError`
  • Fix `sv.JSONSink` serializing NumPy scalars in `custom_data` by converting them to JSON-compatible types
  • Fix `sv.approximate_polygon` exceeding the point-count budget and validate `epsilon_step` to be positive
  • Fix COCO export to preserve all polygon segments for multi-part masks instead of writing only the first polygon
What's new
🚀 KeyPoints.with_nms() — NMS for pose estimation
import supervision as sv

key_points = model.predict(image)  # sv.KeyPoints
key_points = key_points.with_nms(threshold=0.5)  # removes duplicate skeletons

Derives axis-aligned bounding boxes from each skeleton's valid (non-zero and visible) keypoints, then applies standard box NMS. Supports class_agnostic mode and any OverlapMetric (IOU, IOS). Raises ValueError if detection_confidence is not set.

https://github.com/user-attachments/assets/ed7bd310-4868-4275-ae04-c88e7a0c2561


Notable changes
Bug fixes
  • sv.DetectionDataset.as_pascal_voc no longer mutates bounding boxes (#2341) Previously, every export shifted every bounding box by +1 px in-place. A second call compounded the shift. Fixed by rebinding to a new array; on-disk XML output is unchanged.

  • sv.Precision and sv.F1Score correctly count background false positives (#2331) Predictions on images with no ground-truth objects, and predictions of classes absent from any annotation, were previously ignored. Under MICRO and MACRO averaging they are now counted as false positives. WEIGHTED averaging is unchanged. Users should re-evaluate existing metric results after upgrading.

  • sv.DetectionsSmoother works with confidence-free detections (#2333) The smoother no longer raises when detections have no confidence scores. Confidence is averaged over the frames that carry it; tracks without any confidence produce None.

  • sv.Detections.from_vlm is robust to malformed Gemini/Qwen output (#2342) Valid JSON that is not a list, or whose elements are not dicts, now degrades to empty Detections instead of raising TypeError. A malformed mask value in Gemini 2.5 responses no longer misaligns the xyxy/confidence/masks arrays.

  • sv.JSONSink serializes NumPy scalars in custom_data (#2334) np.int64 frame indices and other NumPy scalars in custom_data no longer raise TypeError at flush time. NumPy arrays are serialized as lists. The file handle closes even when serialization fails.

  • sv.approximate_polygon respects the point-count budget (#2332) The function now returns at most floor(N * (1 - percentage)) points (minimum 3). Previously it could return more points than requested. epsilon_step is now validated to be positive.

  • COCO export preserves all segments for multi-part masks (#2322) Previously, only the first polygon was written when a non-crowd detection had disjoint mask segments. All polygon parts are now written.

Performance
  • sv.HaloAnnotator is ~4× faster with CompactMask detections (#2339) HaloAnnotator now uses the same optimized CompactMask paint path as MaskAnnotator. Previously it materialized each mask full-frame; now it operates on the bounding-box crop. Annotated output is unchanged.

  • Mask IoU uses less peak memory (#2323) Mask IoU computation now uses matrix multiplication on flattened masks instead of an explicit (N, M, H, W) tensor. For masks larger than 4096×4096 px, computation promotes to float64 automatically. Results are numerically identical.

  • sv.mask_to_xyxy and sv.KeyPoints.as_detections vectorized (#2330) Both functions now use batched NumPy operations instead of per-element loops. Outputs are bit-identical.


Contributors
  • Ruben Haisma (@RubenHaisma, LinkedIn) — VLM robustness, Pascal VOC export fix, DetectionsSmoother, JSONSink, metrics correctness, polygon budgeting, vectorization
  • Agis Kounelis (@kounelisagis, LinkedIn) — HaloAnnotator perf, mask IoU matmul, mask_to_xyxy/KeyPoints.as_detections vectorization, OBB cookbook
  • Piotr Skalski (@SkalskiP, LinkedIn) — KeyPoints.with_nms()
  • Abdelrahman Gomaa (@abdogomaa201099, LinkedIn) — COCO multi-polygon export

Full Changelog: https://github.com/roboflow/supervision/compare/0.29.0...0.29.1

View original