How to run a trained YOLO model on a webcam and save detections with OpenCV

Based on contributions by elenavoicila, aashish.

Once you’ve trained a YOLO model (see How to train a YOLO object detection model with Roboflow and Ultralytics), use this to run it live on a webcam feed, draw the detected boxes and labels with OpenCV, find the centre point of the best detection (useful as a robot pickup point), and save annotated frames to disk.

This code is for a regular (axis-aligned box) YOLO model. It will not work as-is with an OBB model — see How to train a YOLO OBB model and get the center and angle of detections for oriented bounding boxes.

What you need

Steps

1. Import the required libraries

import cv2
from ultralytics import YOLO

2. Load your trained model

MODEL_PATH = r"path/to/best.pt"  # e.g. runs/detect/train/weights/best.pt
model = YOLO(MODEL_PATH)

3. Set up the camera

cap = cv2.VideoCapture(0)  # 0 for the default camera
if not cap.isOpened():
    print("Error: cannot access the camera.")
    exit()

4. Run detection on each frame

Read a frame, resize it to your training image size, and run the model on it:

ret, frame = cap.read()
if not ret:
    print("Error: cannot read frame.")
    break

frame_resized = cv2.resize(frame, (640, 640))
results = model.predict(frame_resized, verbose=False)

5. Draw the boxes and labels

for box in results[0].boxes:
    x1, y1, x2, y2 = map(int, box.xyxy[0])
    conf = box.conf[0].item()
    cls = int(box.cls[0].item())
    label = f"{model.names[cls]} {conf:.2f}"
    cv2.rectangle(frame_resized, (x1, y1), (x2, y2), (0, 255, 0), 2)
    cv2.putText(frame_resized, label, (x1, y1 - 10),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

6. Find the centre of the most confident detection (optional)

If you need a single pickup point rather than every detection, take the box with the highest confidence and compute its centre:

boxes = results[0].boxes
if len(boxes) > 0:
    confs = boxes.conf.cpu().numpy()
    max_idx = confs.argmax()
    x1, y1, x2, y2 = boxes.xyxy[max_idx].cpu().numpy()
    cx, cy = (x1 + x2) / 2, (y1 + y2) / 2

    print(f"Most confident detection centre: ({cx:.2f}, {cy:.2f})")
    cv2.circle(frame_resized, (int(cx), int(cy)), 5, (0, 0, 255), -1)

If the bounding-box centre isn’t precise enough for your use case (e.g. an irregular object), you can refine it further with your own OpenCV logic on the pixels inside the box.

7. Save the annotated frame

output_path = "annotated_frame.jpg"
cv2.imwrite(output_path, frame_resized)
print(f"Saved annotated frame to {output_path}")

This overwrites annotated_frame.jpg every loop iteration. If you want to keep every frame, use a unique filename per save, e.g. f"annotated_{frame_count}.jpg".

8. Display the frame and exit on ‘q’

cv2.imshow("YOLO Detection", frame_resized)
if cv2.waitKey(1) & 0xFF == ord('q'):
    break

9. Release resources

cap.release()
cv2.destroyAllWindows()

Full code

import cv2
from ultralytics import YOLO

MODEL_PATH = r"path/to/best.pt"
model = YOLO(MODEL_PATH)

cap = cv2.VideoCapture(0)  # 0 for the default camera
if not cap.isOpened():
    print("Error: cannot access the camera.")
    exit()

frame_count = 0

while True:
    ret, frame = cap.read()
    if not ret:
        print("Error: cannot read frame.")
        break

    frame_resized = cv2.resize(frame, (640, 640))
    results = model.predict(frame_resized, verbose=False)
    boxes = results[0].boxes

    for box in boxes:
        x1, y1, x2, y2 = map(int, box.xyxy[0])
        conf = box.conf[0].item()
        cls = int(box.cls[0].item())
        label = f"{model.names[cls]} {conf:.2f}"
        cv2.rectangle(frame_resized, (x1, y1), (x2, y2), (0, 255, 0), 2)
        cv2.putText(frame_resized, label, (x1, y1 - 10),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

    if len(boxes) > 0:
        confs = boxes.conf.cpu().numpy()
        max_idx = confs.argmax()
        x1, y1, x2, y2 = boxes.xyxy[max_idx].cpu().numpy()
        cx, cy = (x1 + x2) / 2, (y1 + y2) / 2
        print(f"Most confident detection centre: ({cx:.2f}, {cy:.2f})")
        cv2.circle(frame_resized, (int(cx), int(cy)), 5, (0, 0, 255), -1)

    output_path = f"annotated_{frame_count}.jpg"
    cv2.imwrite(output_path, frame_resized)
    frame_count += 1

    cv2.imshow("YOLO Detection", frame_resized)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Common mistakes

  • Loading the default pretrained weights (yolo11n.pt) instead of your own trained model — always point MODEL_PATH at your best.pt.
  • Saving every frame to the same filename, silently overwriting previous results — use a counter or timestamp if you want to keep them all.
  • Forgetting model.predict(..., verbose=False) — without it, Ultralytics prints a log line for every single frame.

Related


Rewritten and consolidated (Sept 2026) from the original student how-to’s: How to Annotate and Save YOLO Detections with OpenCV, How to train and implement an AI optimally:.