Oval Annotator
Draw elliptical footprints at the bottom of detected objects for ground-plane visualization.
Overview
The oval() annotator draws elliptical arcs at the bottom center of each detection's bounding box. This creates a natural ground-plane footprint effect, ideal for spatial awareness, shadow effects, and tracking visualizations where you want to indicate object presence on the ground.
pf.annotators.oval(image, detections)
Function Signature
def oval( image: np.ndarray, detections: Detections, thickness: Optional[int] = None, start_angle: int = -45, end_angle: int = 235, colors: Optional[List[Tuple[int, int, int]]] = None) -> np.ndarray
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| image | np.ndarray | required | Input image in BGR format. Modified in-place. |
| detections | Detections | required | PixelFlow detections with bounding boxes. |
| thickness | int or None | None | Line thickness in pixels. None = auto-adapt to image size. |
| start_angle | int | -45 | Starting angle of ellipse arc in degrees. |
| end_angle | int | 235 | Ending angle of ellipse arc in degrees. |
| colors | List[tuple] or None | None | List of BGR colors. None = use default palette. |
Returns:np.ndarray - The input image with elliptical footprints drawn (same array, modified in-place).
Basic Usage
import cv2import pixelflow as pffrom ultralytics import YOLO# Load image and run detectionimage = cv2.imread("pedestrians.jpg")model = YOLO("yolo11n.pt")results = model.predict(image)detections = pf.from_ultralytics(results)# Draw elliptical footprintsimage = pf.annotators.oval(image, detections)cv2.imshow("Result", image)cv2.waitKey(0)
How It Works
The ellipse is positioned and sized based on the bounding box:
- Center: Bottom-center of the bounding box (x_center, y2)
- Width: Matches the full width of the bounding box
- Height: 25% of the width for natural ground-plane proportions
- Arc: By default, draws bottom arc from -45° to 235° (~280°)
┌─────────────────────┐ │ │ │ Bounding Box │ │ │ │ │ └──────────┬──────────┘ │ center point ╭───┴───╮ ╱ ╲ ( ellipse ) ← Footprint drawn here ╲ ╱ ╰───────╯
Angle Control
The start_angle and end_angle parameters control which portion of the ellipse is drawn. Angles follow OpenCV convention:
90° (down) │ │ 180° ───────┼─────── 0° (right) (left) │ │ 270° (up)
# Default: bottom arc (-45° to 235°)image = pf.annotators.oval(image, detections)# Full ellipse (complete circle)image = pf.annotators.oval(image, detections, start_angle=0, end_angle=360)# Narrow bottom arc (subtle footprint)image = pf.annotators.oval(image, detections, start_angle=30, end_angle=150)# Top arc onlyimage = pf.annotators.oval(image, detections, start_angle=180, end_angle=360)
Styling Options
Custom Thickness
# Thin lines for subtle effectimage = pf.annotators.oval(image, detections, thickness=1)# Thick lines for emphasisimage = pf.annotators.oval(image, detections, thickness=4)
Custom Colors
# Custom colors (BGR format)custom_colors = [ (0, 255, 255), # Cyan (255, 0, 255), # Magenta (255, 255, 0), # Yellow]image = pf.annotators.oval(image, detections, colors=custom_colors)
Common Patterns
Pedestrian Tracking Visualization
Combine oval footprints with labels to show tracked people with ground-plane indicators:
# Tracking visualization with footprintsimage = pf.annotators.oval(image, detections)image = pf.annotators.label(image, detections, "ID: {tracker_id}")
Alternative to Bounding Boxes
Use ovals instead of boxes for a cleaner, less cluttered visualization:
# Clean visualization without boxesimage = pf.annotators.oval(image, detections)image = pf.annotators.label(image, detections, position='top_center')
Combined with Boxes
# Full visualization: box + oval footprint + labelimage = pf.annotators.box(image, detections)image = pf.annotators.oval(image, detections)image = pf.annotators.label(image, detections)
Shadow Effect
Create a shadow-like effect with dark colored full ellipses:
# Dark shadow-like footprintimage = pf.annotators.oval( image, detections, start_angle=0, end_angle=360, colors=[(50, 50, 50)], # Dark gray thickness=2)image = pf.annotators.box(image, detections)
Notes
-
In-place modification: The input image is modified directly. Use
image.copy()if you need the original preserved. -
Anti-aliased: Uses
cv2.LINE_AAfor smooth curves. - Adaptive sizing: Thickness automatically adapts to image resolution when not specified.
- Proportions: Ellipse height is fixed at 25% of width for natural ground-plane appearance.
- Color cycling: If more detections than colors, colors will cycle through the list.
See Also
-
box()- Draw rectangular bounding boxes -
anchors()- Draw anchor points on detections -
label()- Add text labels to detections