examples/count_people_in_zone: resolve IoU threshold & improve CLI (#2054)

* refactor(count_people_in_zone): resolve IoU threshold & improve CLI
* fix(pre_commit): 🎨 auto format pre-commit hooks
* Apply suggestions from code review

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-02-03 11:35:09 +01:00
1 parent 0dc9188c5b
commit 6a1b2b8eb4
3 files changed
+86 -117

No files matched your search

@@ -1,4 +1,3 @@
import argparse
import json
import os
@@ -62,7 +61,10 @@ def initiate_annotators(
def detect(
frame: np.ndarray, model: RoboflowInferenceModel, confidence_threshold: float = 0.5
frame: np.ndarray,
model: RoboflowInferenceModel,
confidence_threshold: float = 0.5,
iou_threshold: float = 0.7,
) -> sv.Detections:
"""
Detect objects in a frame using Inference model, filtering detections by class ID
@@ -73,7 +75,8 @@ def detect(
model (RoboflowInferenceModel): The Inference model used for processing the
frame.
confidence_threshold (float): The confidence threshold for filtering
detections. Default is 0.5.
detections.
iou_threshold (float): The IoU threshold for non-maximum suppression.
Returns:
sv.Detections: Filtered detections after processing the frame with the Inference
@@ -83,7 +86,7 @@ def detect(
This function is specifically tailored for an Inference model and assumes class
ID 0 for filtering.
"""
results = model.infer(frame)[0]
results = model.infer(frame, confidence=confidence_threshold, iou=iou_threshold)[0]
detections = sv.Detections.from_inference(results)
filter_by_class = detections.class_id == 0
filter_by_confidence = detections.confidence > confidence_threshold
@@ -124,78 +127,49 @@ def annotate(
return annotated_frame
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Counting people in zones with Inference and Supervision"
)
def main(
zone_configuration_path: str,
source_video_path: str,
model_id: str = "yolov8x-1280",
roboflow_api_key: str | None = None,
target_video_path: str | None = None,
confidence_threshold: float = 0.3,
iou_threshold: float = 0.7,
):
"""
Counting people in zones with Inference and Supervision.
parser.add_argument(
"--zone_configuration_path",
required=True,
help="Path to the zone configuration JSON file",
type=str,
)
parser.add_argument(
"--model_id",
default="yolov8x-1280",
help="Roboflow model ID",
type=str,
)
parser.add_argument(
"--roboflow_api_key",
default=None,
help="Roboflow API KEY",
type=str,
)
parser.add_argument(
"--source_video_path",
required=True,
help="Path to the source video file",
type=str,
)
parser.add_argument(
"--target_video_path",
default=None,
help="Path to the target video file (output)",
type=str,
)
parser.add_argument(
"--confidence_threshold",
default=0.3,
help="Confidence threshold for the model",
type=float,
)
parser.add_argument(
"--iou_threshold",
default=0.7,
help="IOU threshold for the model",
type=float,
)
args = parser.parse_args()
api_key = args.roboflow_api_key
Args:
zone_configuration_path: Path to the zone configuration JSON file
source_video_path: Path to the source video file
model_id: Roboflow model ID
roboflow_api_key: Roboflow API KEY
target_video_path: Path to the target video file (output)
confidence_threshold: Confidence threshold for the model
iou_threshold: IOU threshold for the model
"""
api_key = roboflow_api_key
api_key = os.environ.get("ROBOFLOW_API_KEY", api_key)
if api_key is None:
raise ValueError(
"Roboflow API key is missing. Please provide it as an argument or set the "
"ROBOFLOW_API_KEY environment variable."
)
args.roboflow_api_key = api_key
roboflow_api_key = api_key
video_info = sv.VideoInfo.from_video_path(args.source_video_path)
polygons = load_zones_config(args.zone_configuration_path)
video_info = sv.VideoInfo.from_video_path(source_video_path)
polygons = load_zones_config(zone_configuration_path)
zones, zone_annotators, box_annotators = initiate_annotators(
polygons=polygons, resolution_wh=video_info.resolution_wh
)
model = get_roboflow_model(model_id=args.model_id, api_key=args.roboflow_api_key)
model = get_roboflow_model(model_id=model_id, api_key=roboflow_api_key)
frames_generator = sv.get_video_frames_generator(args.source_video_path)
if args.target_video_path is not None:
with sv.VideoSink(args.target_video_path, video_info) as sink:
frames_generator = sv.get_video_frames_generator(source_video_path)
if target_video_path is not None:
with sv.VideoSink(target_video_path, video_info) as sink:
for frame in tqdm(frames_generator, total=video_info.total_frames):
detections = detect(frame, model, args.confidence_threshold)
detections = detect(frame, model, confidence_threshold, iou_threshold)
annotated_frame = annotate(
frame=frame,
zones=zones,
@@ -206,7 +180,7 @@ if __name__ == "__main__":
sink.write_frame(annotated_frame)
else:
for frame in tqdm(frames_generator, total=video_info.total_frames):
detections = detect(frame, model, args.confidence_threshold)
detections = detect(frame, model, confidence_threshold, iou_threshold)
annotated_frame = annotate(
frame=frame,
zones=zones,
@@ -219,3 +193,10 @@ if __name__ == "__main__":
break
cv2.destroyAllWindows()
if __name__ == "__main__":
from jsonargparse import auto_cli, set_parsing_settings
set_parsing_settings(parse_optionals_as_positionals=True)
auto_cli(main, as_positional=False)
@@ -3,3 +3,4 @@ inference
supervision
tqdm
ultralytics
jsonargparse[signatures]
@@ -1,4 +1,3 @@
import argparse
import json
import cv2
@@ -60,7 +59,10 @@ def initiate_annotators(
def detect(
frame: np.ndarray, model: YOLO, confidence_threshold: float = 0.5
frame: np.ndarray,
model: YOLO,
confidence_threshold: float = 0.5,
iou_threshold: float = 0.7,
) -> sv.Detections:
"""
Detect objects in a frame using a YOLO model, filtering detections by class ID and
@@ -71,6 +73,7 @@ def detect(
model (YOLO): The YOLO model used for processing the frame.
confidence_threshold (float): The confidence threshold for filtering
detections. Default is 0.5.
iou_threshold (float): The IoU threshold for non-maximum suppression.
Returns:
sv.Detections: Filtered detections after processing the frame with the YOLO
@@ -80,7 +83,9 @@ def detect(
This function is specifically tailored for a YOLO model and assumes class ID 0
for filtering.
"""
results = model(frame, imgsz=1280, verbose=False)[0]
results = model(
frame, conf=confidence_threshold, iou=iou_threshold, imgsz=1280, verbose=False
)[0]
detections = sv.Detections.from_ultralytics(results)
filter_by_class = detections.class_id == 0
filter_by_confidence = detections.confidence > confidence_threshold
@@ -121,63 +126,38 @@ def annotate(
return annotated_frame
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Counting people in zones with YOLO and Supervision"
)
def main(
zone_configuration_path: str,
source_video_path: str,
source_weights_path: str = "yolo11x.pt",
target_video_path: str | None = None,
confidence_threshold: float = 0.3,
iou_threshold: float = 0.7,
):
"""
Counting people in zones with YOLO and Supervision.
parser.add_argument(
"--zone_configuration_path",
required=True,
help="Path to the zone configuration JSON file",
type=str,
)
parser.add_argument(
"--source_weights_path",
default="yolo11x.pt",
help="Path to the source weights file",
type=str,
)
parser.add_argument(
"--source_video_path",
required=True,
help="Path to the source video file",
type=str,
)
parser.add_argument(
"--target_video_path",
default=None,
help="Path to the target video file (output)",
type=str,
)
parser.add_argument(
"--confidence_threshold",
default=0.3,
help="Confidence threshold for the model",
type=float,
)
parser.add_argument(
"--iou_threshold",
default=0.7,
help="IOU threshold for the model",
type=float,
)
args = parser.parse_args()
video_info = sv.VideoInfo.from_video_path(args.source_video_path)
polygons = load_zones_config(args.zone_configuration_path)
Args:
zone_configuration_path: Path to the zone configuration JSON file
source_video_path: Path to the source video file
source_weights_path: Path to the source weights file
target_video_path: Path to the target video file (output)
confidence_threshold: Confidence threshold for the model
iou_threshold: IOU threshold for the model
"""
video_info = sv.VideoInfo.from_video_path(source_video_path)
polygons = load_zones_config(zone_configuration_path)
zones, zone_annotators, box_annotators = initiate_annotators(
polygons=polygons, resolution_wh=video_info.resolution_wh
)
model = YOLO(args.source_weights_path)
model = YOLO(source_weights_path)
frames_generator = sv.get_video_frames_generator(args.source_video_path)
if args.target_video_path is not None:
with sv.VideoSink(args.target_video_path, video_info) as sink:
frames_generator = sv.get_video_frames_generator(source_video_path)
if target_video_path is not None:
with sv.VideoSink(target_video_path, video_info) as sink:
for frame in tqdm(frames_generator, total=video_info.total_frames):
detections = detect(frame, model, args.confidence_threshold)
detections = detect(frame, model, confidence_threshold, iou_threshold)
annotated_frame = annotate(
frame=frame,
zones=zones,
@@ -188,7 +168,7 @@ if __name__ == "__main__":
sink.write_frame(annotated_frame)
else:
for frame in tqdm(frames_generator, total=video_info.total_frames):
detections = detect(frame, model, args.confidence_threshold)
detections = detect(frame, model, confidence_threshold, iou_threshold)
annotated_frame = annotate(
frame=frame,
zones=zones,
@@ -201,3 +181,10 @@ if __name__ == "__main__":
break
cv2.destroyAllWindows()
if __name__ == "__main__":
from jsonargparse import auto_cli, set_parsing_settings
set_parsing_settings(parse_optionals_as_positionals=True)
auto_cli(main, as_positional=False)