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 skeletonsDerives 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.
feat-keypoints-with-nms-compressed.mp4
Notable changes
Bug fixes
-
sv.DetectionDataset.as_pascal_vocno 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.Precisionandsv.F1Scorecorrectly count background false positives (#2331) Predictions on images with no ground-truth objects, and predictions of classes absent from any annotation, were previously ignored. UnderMICROandMACROaveraging they are now counted as false positives.WEIGHTEDaveraging is unchanged. Users should re-evaluate existing metric results after upgrading. -
sv.DetectionsSmootherworks 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 produceNone. -
sv.Detections.from_vlmis robust to malformed Gemini/Qwen output (#2342) Valid JSON that is not a list, or whose elements are not dicts, now degrades to emptyDetectionsinstead of raisingTypeError. A malformed mask value in Gemini 2.5 responses no longer misaligns thexyxy/confidence/masksarrays. -
sv.JSONSinkserializes NumPy scalars incustom_data(#2334)np.int64frame indices and other NumPy scalars incustom_datano longer raiseTypeErrorat flush time. NumPy arrays are serialized as lists. The file handle closes even when serialization fails. -
sv.approximate_polygonrespects the point-count budget (#2332) The function now returns at mostfloor(N * (1 - percentage))points (minimum 3). Previously it could return more points than requested.epsilon_stepis 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.HaloAnnotatoris ~4× faster withCompactMaskdetections (#2339)HaloAnnotatornow uses the same optimized CompactMask paint path asMaskAnnotator. 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_xyxyandsv.KeyPoints.as_detectionsvectorized (#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: 0.29.0...0.29.1