How to Run Object Detection with OpenCV 5

For the first time since 2018, OpenCV has a new major version. OpenCV 5.0 arrived in June 2026, and the single biggest change for computer vision engineers is a completely rewritten DNN module with a new inference engine built for modern neural networks. Indeed, that makes it the perfect moment to answer a very practical question: can we run a state of the art object detector, fast, on nothing but a CPU?

To keep it grounded, we point the detector at something happening right now. The 2026 FIFA World Cup is underway across the United States, Canada, and Mexico. Therefore, we pull live clips from the tournament and ask a YOLO26 model, running entirely through the OpenCV 5 DNN module, to find every player and the ball.

Crucially, there is no GPU here and no PyTorch at inference time, just an ONNX file and OpenCV. This is Part 1, so it covers the full Python workflow. In turn, Part 2 will take the exact same pipeline into C++, and later parts will explore more of what OpenCV 5 can do, from pose estimation to segmentation.

OpenCV 5 and YOLO26 detecting football players with bounding boxes on a 2026 World Cup pitch

In the free-kick above, OpenCV 5 and YOLO26 detect all players and the ball.

Table of Contents

  1. Why OpenCV 5? What is new
  2. Getting set up
    1. Install OpenCV 5
    2. Export YOLO26 to ONNX
  3. Running YOLO26 with OpenCV 5
    1. Load the model and pick the engine
    2. Preprocess and the NMS-free output
    3. Detection on an image
    4. Detection on video
  4. Where exactly is OpenCV 5 doing the work?
  5. Results
  6. Conclusion
  7. References

What you will learn

By the end of this post you will be able to install OpenCV 5, export a YOLO26 model to ONNX with the correct settings, and run object detection on both images and video using OpenCV 5’s new DNN engine, all on a CPU. In addition, you will understand why YOLO26 makes the post-processing far simpler than earlier YOLO models.

1. Why OpenCV 5? What is new

OpenCV 5.0 is the first major release since OpenCV 4.0 in 2018, so a fair amount has been modernized. First, a few changes matter before we write any code. For example, C++17 is now the minimum standard, and the old C API has been removed. In addition, the DNN module dropped its Darknet and Caffe importers. As a result, older YOLOv3 and YOLOv4 workflows that loaded .cfg and .weights files no longer work directly, so the supported route is to convert to ONNX first.

The headline, though, is the DNN rewrite. Specifically, OpenCV 5 ships two inference engines side by side, selected through a new engine type. Moreover, the new engine is a rewritten graph engine with shape inference, constant folding, and operator fusion, and it lifts ONNX operator coverage from a small fraction of the specification to the large majority of it. In plain terms, many more exported models, including modern YOLO detectors, now load and run without hand editing the graph.

ONNX operator coverage in OpenCV 4.x versus OpenCV 5, rising from about 22 to over 80 percent

This is only a short summary. For the full list of features, read the official announcement: OpenCV 5 Is Here. The technical details and benchmarks live on the OpenCV 5 wiki.

2. Getting set up

2.1 Install OpenCV 5

OpenCV 5 wheels are on PyPI, so a plain pip install is enough for Python. In practice, we recommend an isolated virtual environment, especially if you already have OpenCV 4.x installed for other projects, because upgrading the global install in place can change behavior in unrelated code.

python -m venv .venv
# Windows:  .venv\Scripts\activate
# Linux/macOS:  source .venv/bin/activate

pip install "opencv-python==5.0.0.93" ultralytics onnx onnxruntime

Of course, the pip wheel is CPU only. Specifically, it includes the new engine and the classic CPU path, but it does not bundle CUDA. For GPU, therefore, you would build OpenCV from source, which is out of scope here since our whole point is CPU inference.

2.2 Export YOLO26 to ONNX

The OpenCV DNN module loads ONNX files, so the first step is to export YOLO26 from its native format. In particular, three export settings matter: use opset 12 or newer, export with a static input shape, and do not bake NMS into the graph.

from ultralytics import YOLO

model = YOLO("yolo26n.pt")          # nano = fastest on CPU

model.export(
    format="onnx",
    opset=12,        # OpenCV 5 DNN needs opset >= 12
    imgsz=640,       # fixed square input
    dynamic=False,   # static shapes avoid a known DNN parsing issue
    simplify=True,   # fold constants for a cleaner graph
    nms=False,       # keep the raw head; YOLO26 is already NMS-free
)

3. Running YOLO26 with OpenCV 5

3.1 Load the model and pick the engine

Notably, this is where OpenCV 5 differs most from 4.x. In practice, the readNet call now takes an engine argument, and OpenCV exposes the engine choices as an enum.

import cv2

def build_net(onnx_path, engine="auto"):
    engines = {
        "auto":    cv2.dnn.ENGINE_AUTO,     # new engine, falls back to classic
        "new":     cv2.dnn.ENGINE_NEW,      # OpenCV 5's rewritten CPU engine
        "classic": cv2.dnn.ENGINE_CLASSIC,  # the 4.x engine (needed for CUDA)
        "ort":     cv2.dnn.ENGINE_ORT,      # bundled ONNX Runtime (source build)
    }
    # readNet takes (model, config, framework, engine) in OpenCV 5
    return cv2.dnn.readNet(onnx_path, "", "", engines[engine.lower()])

3.2 Preprocess and the NMS-free output

YOLO26 is an end-to-end, NMS-free detector. Earlier YOLO models output a large grid of candidate boxes (for example the [1, 84, 8400] tensor of YOLOv8) that you then transpose and clean up with Non-Maximum Suppression. By contrast, YOLO26 emits a fixed [1, 300, 6] tensor, where each of the 300 rows is already a final detection: x1, y1, x2, y2, score, class id. As a result, there is no transpose and no NMS call. In the end, all we do is drop rows below a score threshold and map the boxes back onto the original frame.

To go deeper on this NMS-free design, read our detailed guide: YOLO26 NMS-Free Inference.

def detect(net, img, size=640, conf_thres=0.25):
    padded, r, (dw, dh) = letterbox(img, size)                 # keep aspect ratio
    blob = cv2.dnn.blobFromImage(padded, 1/255.0, (size, size),
                                 swapRB=True, crop=False)
    net.setInput(blob)
    out = net.forward()[0]          # (300, 6): x1,y1,x2,y2,score,class

    dets = []
    for x1, y1, x2, y2, score, cls in out:
        if score < conf_thres:
            continue
        # undo the letterbox padding and scaling
        dets.append(((x1-dw)/r, (y1-dh)/r, (x2-dw)/r, (y2-dh)/r,
                     float(score), int(cls)))
    return dets

3.3 Detection on an image

net = build_net("models/yolo26n_640.onnx", engine="auto")
img = cv2.imread("media/wc2026_still_1.jpg")

detect(net, img)                          # warm-up
dets = detect(net, img, conf_thres=0.3)   # timed run
cv2.imwrite("outputs/detected.jpg", draw(img, dets))

Let’s see an example output, a frame of Messi on the ball in a World Cup match, with the players and the ball detected.

OpenCV 5 and YOLO26 object detection output on a football image, players and the ball boxed

A quick aside before we move to video. If you look at number 10 on the ball, YOLO26 confidently labels it a person. Some readers will object here. That box, they will insist, should clearly read GOAT. Unfortunately, the COCO dataset the model was trained on never added that class, so person is the best it can do, at least for now.

3.4 Detection on video

In fact, video is just the same call inside a read loop. Here OpenCV handles decoding and drawing, while the model runs on every frame.

cap = cv2.VideoCapture("media/Messi_Goal.mp4")
writer = cv2.VideoWriter("outputs/detected.mp4",
                         cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
while True:
    ok, frame = cap.read()
    if not ok:
        break
    dets = detect(net, frame, conf_thres=0.3)
    writer.write(draw(frame, dets))

Run that loop on a clip and you get output like the video below. We also take it one step further here and color each box by team, green for one side and red for the other. That coloring is a small add-on beyond the snippet above, and you can style it however you like to separate the two sides.

4. Where exactly is OpenCV 5 doing the work?

It is worth being precise here, because this point is easy to overstate. YOLO26, the trained model, is the brain: it is what actually knows a player from a ball. OpenCV 5, by contrast, is the engine that runs that brain, together with all the pixel plumbing around it.

Per frame, in fact, OpenCV 5 does five separate jobs. First, it reads the pixels with VideoCapture or imread. Next, it preprocesses them with blobFromImage. Then it runs the network with net.forward() inside its own DNN engine, with no PyTorch or external runtime involved. Finally, it draws the boxes and writes the output.

The genuinely new-in-OpenCV-5 piece is therefore small but central. It is the engine enum and the extended readNet signature from Section 4.1, which together run the model on the rewritten CPU engine.

5. Results

On high-resolution stills, the detector holds boxes on every player, the goalkeeper, and the ball. Even on a corner into a crowded box, moreover, it separates overlapping shirts cleanly.

Fast action also means motion blur, and broadcast frames are rarely tack sharp. Encouragingly, the detector holds up well here. In the blurred frame below, YOLO26 still boxes the goalkeeper and the outfield players, even where limbs smear and edges soften. This robustness comes from training on large and varied datasets, so a little blur rarely breaks detection. Naturally, very heavy blur or a tiny, fast-moving ball is harder, and that is where a higher-resolution model or a lower confidence threshold helps.

OpenCV 5 and YOLO26 detecting football players on a motion-blurred match frame

The same detector runs just as happily on video. In the clip below, it tracks the players frame by frame, at real-world speed, on nothing but the CPU.

Finally, here is another example from open play. Even with the players spread across the pitch, the boxes stay tight and the ball is still picked out.

OpenCV 5 and YOLO26 detecting football players and the ball across a match pitch

6. Conclusion

In short, OpenCV 5 turns the library into a serious, self-contained inference runtime rather than just a preprocessing helper around someone else’s model. Pairing it with YOLO26 made that concrete. Because YOLO26 is NMS-free, the code to turn raw output into clean detections was almost trivial. Moreover, because both pieces are efficient, we ran real 2026 World Cup detection on nothing but a CPU. In Part 2 we will take this exact pipeline into C++, build it against OpenCV 5, and see how the story changes with no Python in the loop. Beyond detection, the same OpenCV 5 DNN engine handles far more, so later parts will put it to work on other tasks such as pose estimation and segmentation.

From idea to working model to real-time deployment

Big Vision takes computer vision projects through the full journey, not just the easy parts.

7. References

Similar Posts

Leave a Reply