Restructure annotator docs tabs to avoid Material’s 20-tab limit (#2257)
* Initial plan * fix: split annotator docs tabs into categories * test: harden annotator docs regression test * docs: fix blur example assignment * docs(annotators): restructure tabs, harden regression tests * docs(annotators): remove broken oriented box preview image --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Borda <6035284+Borda@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
2c13b269e6
commit
6461d3fbac
|
|
@ -7,510 +7,527 @@ description: API reference for supervision's annotator classes — draw bounding
|
|||
|
||||
Annotators accept detections and apply box or mask visualizations to the detections. Annotators have many available styles.
|
||||
|
||||
=== "Box"
|
||||
=== "Outlines"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
=== "Box"
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
annotated_frame = box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "RoundBox"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
round_box_annotator = sv.RoundBoxAnnotator()
|
||||
annotated_frame = round_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "BoxCorner"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
corner_annotator = sv.BoxCornerAnnotator()
|
||||
annotated_frame = corner_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Color"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
color_annotator = sv.ColorAnnotator()
|
||||
annotated_frame = color_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Circle"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
circle_annotator = sv.CircleAnnotator()
|
||||
annotated_frame = circle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Dot"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
dot_annotator = sv.DotAnnotator()
|
||||
annotated_frame = dot_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Triangle"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
triangle_annotator = sv.TriangleAnnotator()
|
||||
annotated_frame = triangle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Ellipse"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
ellipse_annotator = sv.EllipseAnnotator()
|
||||
annotated_frame = ellipse_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Halo"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
halo_annotator = sv.HaloAnnotator()
|
||||
annotated_frame = halo_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "PercentageBar"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
percentage_bar_annotator = sv.PercentageBarAnnotator()
|
||||
annotated_frame = percentage_bar_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Mask"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
mask_annotator = sv.MaskAnnotator()
|
||||
annotated_frame = mask_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Polygon"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
polygon_annotator = sv.PolygonAnnotator()
|
||||
annotated_frame = polygon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Label"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
labels = [
|
||||
f"{class_name} {confidence:.2f}"
|
||||
for class_name, confidence in zip(
|
||||
detections["class_name"],
|
||||
detections.confidence,
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
annotated_frame = box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
]
|
||||
```
|
||||
|
||||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
|
||||
annotated_frame = label_annotator.annotate(
|
||||
scene=image.copy(), detections=detections, labels=labels
|
||||
)
|
||||
```
|
||||
<div class="result" markdown>
|
||||
|
||||
<div class="result" markdown>
|
||||
{ align=center width="800" }
|
||||
|
||||
{ align=center width="800" }
|
||||
</div>
|
||||
|
||||
</div>
|
||||
=== "RoundBox"
|
||||
|
||||
=== "RichLabel"
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
labels = [
|
||||
f"{class_name} {confidence:.2f}"
|
||||
for class_name, confidence in zip(
|
||||
detections["class_name"],
|
||||
detections.confidence,
|
||||
round_box_annotator = sv.RoundBoxAnnotator()
|
||||
annotated_frame = round_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
]
|
||||
```
|
||||
|
||||
rich_label_annotator = sv.RichLabelAnnotator(
|
||||
font_path="TTF_FONT_PATH",
|
||||
text_position=sv.Position.CENTER,
|
||||
)
|
||||
annotated_frame = rich_label_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
labels=labels,
|
||||
)
|
||||
```
|
||||
<div class="result" markdown>
|
||||
|
||||
<div class="result" markdown>
|
||||
{ align=center width="800" }
|
||||
|
||||
{ align=center width="800" }
|
||||
</div>
|
||||
|
||||
</div>
|
||||
=== "BoxCorner"
|
||||
|
||||
=== "Icon"
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
corner_annotator = sv.BoxCornerAnnotator()
|
||||
annotated_frame = corner_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
icon_paths = ["<ICON_PATH>" for _ in detections]
|
||||
<div class="result" markdown>
|
||||
|
||||
icon_annotator = sv.IconAnnotator()
|
||||
annotated_frame = icon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
icon_path=icon_paths,
|
||||
)
|
||||
```
|
||||
{ align=center width="800" }
|
||||
|
||||
<div class="result" markdown>
|
||||
</div>
|
||||
|
||||
{ align=center width="800" }
|
||||
=== "Circle"
|
||||
|
||||
</div>
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
<!-- === "Crop"
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
circle_annotator = sv.CircleAnnotator()
|
||||
annotated_frame = circle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
<div class="result" markdown>
|
||||
|
||||
crop_annotator = sv.CropAnnotator()
|
||||
annotated_frame = crop_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
{ align=center width="800" }
|
||||
|
||||
<div class="result" markdown>
|
||||
</div>
|
||||
|
||||
{ align=center width="800" }
|
||||
=== "Ellipse"
|
||||
|
||||
</div>
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
-->
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
=== "Blur"
|
||||
ellipse_annotator = sv.EllipseAnnotator()
|
||||
annotated_frame = ellipse_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
<div class="result" markdown>
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
{ align=center width="800" }
|
||||
|
||||
blur_annotator = sv.BlurAnnotator()
|
||||
annotated_frame = (blur_annotator.annotate(scene=image.copy(), detections=detections),)
|
||||
```
|
||||
</div>
|
||||
|
||||
<div class="result" markdown>
|
||||
=== "Polygon"
|
||||
|
||||
{ align=center width="800" }
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
</div>
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
=== "Pixelate"
|
||||
polygon_annotator = sv.PolygonAnnotator()
|
||||
annotated_frame = polygon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
<div class="result" markdown>
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
{ align=center width="800" }
|
||||
|
||||
pixelate_annotator = sv.PixelateAnnotator()
|
||||
annotated_frame = pixelate_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
</div>
|
||||
|
||||
<div class="result" markdown>
|
||||
=== "Shading"
|
||||
|
||||
{ align=center width="800" }
|
||||
=== "Color"
|
||||
|
||||
</div>
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
=== "Trace"
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
color_annotator = sv.ColorAnnotator()
|
||||
annotated_frame = color_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
model = YOLO("yolov8x.pt")
|
||||
<div class="result" markdown>
|
||||
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
{ align=center width="800" }
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path="...")
|
||||
frames_generator = sv.get_video_frames_generator(source_path="...")
|
||||
tracker = sv.ByteTrack()
|
||||
</div>
|
||||
|
||||
with sv.VideoSink(target_path="...", video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
annotated_frame = trace_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections,
|
||||
=== "Halo"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
halo_annotator = sv.HaloAnnotator()
|
||||
annotated_frame = halo_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Mask"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
mask_annotator = sv.MaskAnnotator()
|
||||
annotated_frame = mask_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Markers"
|
||||
|
||||
=== "Dot"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
dot_annotator = sv.DotAnnotator()
|
||||
annotated_frame = dot_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Triangle"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
triangle_annotator = sv.TriangleAnnotator()
|
||||
annotated_frame = triangle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Labels"
|
||||
|
||||
=== "Label"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
labels = [
|
||||
f"{class_name} {confidence:.2f}"
|
||||
for class_name, confidence in zip(
|
||||
detections["class_name"],
|
||||
detections.confidence,
|
||||
)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
]
|
||||
|
||||
<div class="result" markdown>
|
||||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
|
||||
annotated_frame = label_annotator.annotate(
|
||||
scene=image.copy(), detections=detections, labels=labels
|
||||
)
|
||||
```
|
||||
|
||||
{ align=center width="800" }
|
||||
<div class="result" markdown>
|
||||
|
||||
</div>
|
||||
{ align=center width="800" }
|
||||
|
||||
=== "HeatMap"
|
||||
</div>
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
=== "RichLabel"
|
||||
|
||||
model = YOLO("yolov8x.pt")
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
heat_map_annotator = sv.HeatMapAnnotator()
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path="...")
|
||||
frames_generator = sv.get_video_frames_generator(source_path="...")
|
||||
|
||||
with sv.VideoSink(target_path="...", video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
annotated_frame = heat_map_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections,
|
||||
labels = [
|
||||
f"{class_name} {confidence:.2f}"
|
||||
for class_name, confidence in zip(
|
||||
detections["class_name"],
|
||||
detections.confidence,
|
||||
)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
]
|
||||
|
||||
<div class="result" markdown>
|
||||
rich_label_annotator = sv.RichLabelAnnotator(
|
||||
font_path="TTF_FONT_PATH",
|
||||
text_position=sv.Position.CENTER,
|
||||
)
|
||||
annotated_frame = rich_label_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
labels=labels,
|
||||
)
|
||||
```
|
||||
|
||||
{ align=center width="800" }
|
||||
<div class="result" markdown>
|
||||
|
||||
</div>
|
||||
{ align=center width="800" }
|
||||
|
||||
=== "Background Color"
|
||||
</div>
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
=== "Transformative"
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
=== "Blur"
|
||||
|
||||
background_overlay_annotator = sv.BackgroundOverlayAnnotator()
|
||||
annotated_frame = background_overlay_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
<div class="result" markdown>
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
{ align=center width="800" }
|
||||
blur_annotator = sv.BlurAnnotator()
|
||||
annotated_frame = blur_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
</div>
|
||||
<div class="result" markdown>
|
||||
|
||||
=== "Comparison"
|
||||
{ align=center width="800" }
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
</div>
|
||||
|
||||
image = ...
|
||||
detections_1 = sv.Detections(...)
|
||||
detections_2 = sv.Detections(...)
|
||||
=== "Pixelate"
|
||||
|
||||
comparison_annotator = sv.ComparisonAnnotator()
|
||||
annotated_frame = comparison_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections_1=detections_1,
|
||||
detections_2=detections_2,
|
||||
)
|
||||
```
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
<div class="result" markdown>
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
{ align=center width="800" }
|
||||
pixelate_annotator = sv.PixelateAnnotator()
|
||||
annotated_frame = pixelate_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
</div>
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
<!-- === "Crop"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
crop_annotator = sv.CropAnnotator()
|
||||
annotated_frame = crop_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
-->
|
||||
|
||||
=== "Tracking & Aggregation"
|
||||
|
||||
=== "Trace"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8x.pt")
|
||||
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path="...")
|
||||
frames_generator = sv.get_video_frames_generator(source_path="...")
|
||||
tracker = sv.ByteTrack()
|
||||
|
||||
with sv.VideoSink(target_path="...", video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
annotated_frame = trace_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "HeatMap"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8x.pt")
|
||||
|
||||
heat_map_annotator = sv.HeatMapAnnotator()
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path="...")
|
||||
frames_generator = sv.get_video_frames_generator(source_path="...")
|
||||
|
||||
with sv.VideoSink(target_path="...", video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
annotated_frame = heat_map_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Others"
|
||||
|
||||
=== "PercentageBar"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
percentage_bar_annotator = sv.PercentageBarAnnotator()
|
||||
annotated_frame = percentage_bar_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Icon"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
icon_paths = ["<ICON_PATH>" for _ in detections]
|
||||
|
||||
icon_annotator = sv.IconAnnotator()
|
||||
annotated_frame = icon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
icon_path=icon_paths,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Background Color"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
background_overlay_annotator = sv.BackgroundOverlayAnnotator()
|
||||
annotated_frame = background_overlay_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Comparison"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections_1 = sv.Detections(...)
|
||||
detections_2 = sv.Detections(...)
|
||||
|
||||
comparison_annotator = sv.ComparisonAnnotator()
|
||||
annotated_frame = comparison_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections_1=detections_1,
|
||||
detections_2=detections_2,
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>Try Supervision Annotators on your own image</h2>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
"""Regression tests for docs/detection/annotators.md tab structure."""
|
||||
|
||||
import ast
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parent
|
||||
while not (_REPO_ROOT / "pyproject.toml").exists():
|
||||
_REPO_ROOT = _REPO_ROOT.parent
|
||||
|
||||
REPO_ROOT = _REPO_ROOT
|
||||
|
||||
EXPECTED_ANNOTATOR_TAB_GROUPS: dict[str, list[str]] = {
|
||||
"Outlines": [
|
||||
"Box",
|
||||
"RoundBox",
|
||||
"BoxCorner",
|
||||
"Circle",
|
||||
"Ellipse",
|
||||
"Polygon",
|
||||
],
|
||||
"Shading": ["Color", "Halo", "Mask"],
|
||||
"Markers": ["Dot", "Triangle"],
|
||||
"Labels": ["Label", "RichLabel"],
|
||||
"Transformative": ["Blur", "Pixelate"],
|
||||
"Tracking & Aggregation": ["Trace", "HeatMap"],
|
||||
"Others": [
|
||||
"PercentageBar",
|
||||
"Icon",
|
||||
"Background Color",
|
||||
"Comparison",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _extract_annotator_tab_groups() -> dict[str, list[str]]:
|
||||
"""Parse annotator tab structure from docs/detection/annotators.md.
|
||||
|
||||
Returns:
|
||||
Mapping of category name to list of annotator tab labels within that
|
||||
category.
|
||||
"""
|
||||
docs_path = REPO_ROOT / "docs" / "detection" / "annotators.md"
|
||||
content = docs_path.read_text(encoding="utf-8")
|
||||
|
||||
start_marker = '=== "Outlines"'
|
||||
end_marker = "Try Supervision Annotators on your own image"
|
||||
|
||||
start = content.find(start_marker)
|
||||
if start == -1:
|
||||
pytest.fail(
|
||||
f"Could not find start marker {start_marker!r} in {docs_path} while "
|
||||
"parsing annotator example tab groups."
|
||||
)
|
||||
|
||||
end = content.find(end_marker, start)
|
||||
if end == -1:
|
||||
pytest.fail(
|
||||
f"Could not find end marker {end_marker!r} in {docs_path} while "
|
||||
"parsing annotator example tab groups."
|
||||
)
|
||||
if end <= start:
|
||||
pytest.fail(
|
||||
f"End marker {end_marker!r} must appear after start marker "
|
||||
f"{start_marker!r} in {docs_path}."
|
||||
)
|
||||
|
||||
example_section = content[start:end]
|
||||
|
||||
groups: dict[str, list[str]] = {}
|
||||
current_group = None
|
||||
|
||||
for line in example_section.splitlines():
|
||||
if match := re.match(r'^(?P<indent>\s*)=== "(?P<label>[^"]+)"\s*$', line):
|
||||
indent = 1 if match.group("indent") else 0
|
||||
label = match.group("label")
|
||||
|
||||
if indent == 0:
|
||||
current_group = label
|
||||
groups[current_group] = []
|
||||
elif indent > 0 and current_group:
|
||||
groups[current_group].append(label)
|
||||
return groups
|
||||
|
||||
|
||||
def test_all_expected_annotators_have_tab_entries() -> None:
|
||||
"""Assert every annotator in EXPECTED_ANNOTATOR_TAB_GROUPS has a tab in the docs.
|
||||
|
||||
Tests flat membership only — does not enforce which category each annotator
|
||||
belongs to. Update EXPECTED_ANNOTATOR_TAB_GROUPS when adding or removing
|
||||
annotator tab entries.
|
||||
"""
|
||||
tab_groups = _extract_annotator_tab_groups()
|
||||
actual_tabs = {tab for group in tab_groups.values() for tab in group}
|
||||
expected_tabs = {
|
||||
tab for group in EXPECTED_ANNOTATOR_TAB_GROUPS.values() for tab in group
|
||||
}
|
||||
assert actual_tabs == expected_tabs, (
|
||||
f"Tab set mismatch. "
|
||||
f"Missing from docs: {expected_tabs - actual_tabs!r}. "
|
||||
f"Extra in docs (add to EXPECTED_ANNOTATOR_TAB_GROUPS): "
|
||||
f"{actual_tabs - expected_tabs!r}."
|
||||
)
|
||||
|
||||
|
||||
def test_annotator_example_tab_groups_stay_within_material_limit() -> None:
|
||||
"""Assert no tab group exceeds MkDocs Material's 20-tab rendering limit."""
|
||||
tab_groups = _extract_annotator_tab_groups()
|
||||
|
||||
# Outer-tab guard is defense-in-depth; the original failure was strictly an
|
||||
# inner-tab overflow (22 inner tabs in a single flat group).
|
||||
assert len(tab_groups) <= 20
|
||||
assert all(len(group) <= 20 for group in tab_groups.values())
|
||||
|
||||
|
||||
def test_annotator_code_examples_have_no_tuple_assignment() -> None:
|
||||
"""Assert no annotated_frame assignment wraps the call in a tuple.
|
||||
|
||||
Guards against the paren-comma typo: ``annotated_frame = (call(),)``
|
||||
which creates a 1-tuple instead of the annotated image.
|
||||
"""
|
||||
docs_path = REPO_ROOT / "docs" / "detection" / "annotators.md"
|
||||
content = docs_path.read_text(encoding="utf-8")
|
||||
|
||||
fenced_blocks = re.findall(r"```python\n(.*?)```", content, re.DOTALL)
|
||||
|
||||
for block_idx, block in enumerate(fenced_blocks, 1):
|
||||
try:
|
||||
tree = ast.parse(block)
|
||||
except SyntaxError:
|
||||
continue
|
||||
|
||||
for node in ast.walk(tree):
|
||||
if (
|
||||
isinstance(node, ast.Assign)
|
||||
and any(
|
||||
isinstance(t, ast.Name) and t.id == "annotated_frame"
|
||||
for t in node.targets
|
||||
)
|
||||
and isinstance(node.value, ast.Tuple)
|
||||
):
|
||||
pytest.fail(
|
||||
f"Block {block_idx}: 'annotated_frame' assigned a tuple "
|
||||
"(paren-comma typo). "
|
||||
"Use 'annotated_frame = call()' not 'annotated_frame = (call(),)'."
|
||||
)
|
||||
Loading…
Reference in New Issue