You compile the CUDA samples, they run, and the GPU works. Then someone installs a Python binding for OpenCV on the same machine, and the device count drops to 0 while every GPU call starts raising errors. The GPU did not fail and neither did the driver: the OpenCV library your program now loads was compiled without CUDA support. That message comes from the library, not from the hardware. This guide gives you the two checks that identify which build you actually linked, the fix for each of the three causes, and the install habits that keep a CUDA-enabled build alive on a production machine.
The symptom: a CPU-only library behind a working GPU
The probe differs by OpenCV generation. On 2.x-era builds the count came from the C++ gpu namespace, as gpu::getCudaEnabledDeviceCount(). Current builds expose it under the cuda module, and Python calls it as cv2.cuda.getCudaEnabledDeviceCount(). Both return 0 when the linked library has no CUDA support, whatever the GPU itself is doing.
The failure strings name the library, not the card. In C++ you see 'OpenCV Error: No GPU support (The library is compiled without CUDA support)'. In Python, a stubbed call raises 'The function/feature is not implemented (The called functionality is disabled for current build or platform)', and cv2.cudacodec reports that the build has no CUDA backend. All of these come from stub code inside a CPU-only build.
The classic sequence shows how a working setup gets there. A vendor image for an embedded board boots with a console banner such as 'Tegra platform detected, optimizations are switched ON!' — that banner is printed by the board's own OpenCV build. A distribution package named python-opencv goes on top, the banner disappears, and the count falls to 0. Nothing about the GPU changed: the program is importing a different library. Remove the extra package and the vendor build's behavior returns immediately.
Three causes, ordered by probability
| Probability | Cause | Signature | Test |
|---|---|---|---|
| Most common | A CPU-only package shadows the CUDA build: the distro python-opencv binding or a standard pip opencv-python wheel | The count was nonzero before an install and 0 after; the boot banner disappeared | Build information plus module path (both tests below) |
| Common on fresh installs | The linked build never had CUDA: a distribution package or default wheel on a clean machine | The CUDA option reads off from day one; the count has always been 0 | Build information |
| Rarest | CUDA is compiled in but no usable device is visible at runtime | Build information shows CUDA enabled while the count stays 0 | Run the device sample from the CUDA toolkit |
Distribution packages and the standard Python wheels ship without CUDA, which is why the second cause dominates clean installs: the option is off before you touch anything. The first cause dominates machines that already worked. The third is the only case where the driver and toolkit themselves need attention.
Test 1: print the build information
Run three lines in the environment your application actually uses: same interpreter, same container, same user.
import cv2 # the binding your process imports
print(cv2.__version__) # version of the linked build
print(cv2.cuda.getCudaEnabledDeviceCount()) # 0 means: no CUDA in this build, or no usable device
print(cv2.getBuildInformation()) # find the CUDA group in this report
The build report answers the compile-time question directly. A CPU-only build states the CUDA option as NO in one short line. A CUDA-enabled build states YES and lists the toolkit version, the GPU compute capabilities the library was compiled for, and the support libraries it found.
Decide from this test alone:
- CUDA reads NO: the library is the problem. Take the fix below.
- CUDA reads YES and the count is still 0: the runtime is the problem. Build and run the device sample that ships with the toolkit. If the sample lists the GPU, the device and driver are healthy, and the machine's install history is the suspect.
Test 2: find the file behind cv2
Python resolves the cv2 module at import time, and the loaded module tells you which file won. cv2.version prints the version; the module's file attribute prints the path. A path under the distribution's package directory is the distro binding, a site-packages path is a pip wheel. Compare the version with your own build: a mismatch means two builds are installed and Python loaded the wrong one.
C++ programs resolve their libraries at link time instead. If the binary predates the CUDA build, relink it against that build's libraries and re-run the C++ probe. Two OpenCV versions side by side cause more than missing CUDA: the crashes in the OpenCV version conflicts guide come from exactly this kind of shadowing.
The fix: one OpenCV build per machine
- Record the baseline. Run the probe, save its output, and note the module path. Every later check compares against this.
- Remove the shadowing package. Uninstall the binding that file pointed to, with the package manager that installed it, then re-run the probe before installing anything new.
- If no CUDA-enabled build exists, make one. Enable the CUDA option in the CMake configuration and read the configure summary before compiling: it must report that CUDA was found, not merely that configuration ended without errors. Match the toolkit to the OpenCV release — CUDA 12.2 shipped after OpenCV 4.8.0, and that pairing is a known configure failure.
- Pin the stack. One library version, installed once, in a pinned environment or container, so the next upgrade cannot resurrect the conflict. The Docker SDK version mismatch fix shows the discipline, and the GPU vision Python environment checks list what to confirm before you freeze it.
- Prove the result. The probe must print a count of 1 or more and the same build report you recorded as the baseline.
Embedded boards repeat the pattern under different package names. Vendor images for Jetson-class modules ship a CUDA-enabled OpenCV — JetPack 4.6, for example, comes with OpenCV 4.5.5 — and in the OpenCV4Tegra era the matching binding was libopencv4tegra-python, while the distro python-opencv package was the conflict. The names have moved on; the rule has not: install the binding that belongs to the CUDA build and nothing else.
Keep CUDA-stack changes on embedded boards surgical, because an install that goes wrong reaches further than the library: one reported case left a Jetson AGX Orin failing to boot after a CUDA install. Make such changes through the vendor's own update path.
Do you need the rebuild? A cycle-time check
Put a number on what the GPU must save before you spend an afternoon compiling. You need two inputs: the required frame rate and a measured CPU processing time per frame.
Illustrative example. The line must run at 30 fps, so the budget is 1000 / 30 = 33.3 ms per frame. The CPU path processes one frame in 25 ms, leaving 33.3 - 25 = 8.3 ms of margin, about 25% of the budget. That margin absorbs normal jitter, so ship the CPU build and skip the rebuild. If the CPU path instead takes 40 ms per frame, every frame runs 40 - 33.3 = 6.7 ms over budget, and the CUDA build is justified: prototype it and measure the same stages before switching the line.
The GPU repays that work only where data stays on the device. Each copy between CPU memory and GPU memory costs time, so one-shot calls on small images often run faster on the CPU; the gain arrives when several heavy stages run in sequence over the same image. The CUDA image to NumPy guide and the GPU tensor input guide show how to keep data there, and the CUDA context guide covers the lifetime trap that surfaces first.
If the count stays 0 and the schedule is tight, CPU inference is a real answer: the CPU object detection guide and the CPU versus GPU setup checks put numbers on when it wins. Where the bottleneck is acquisition rather than processing, the frame rate gap fix is the relevant one.
Provision the controller before you compile
The rebuild is quick on a machine provisioned for it and slow on one that is not. A CUDA-enabled source build is memory-hungry at the link stages: plan 16 GB of memory as the workable floor, 32 GB to link comfortably, plus tens of gigabytes of disk for the build tree — a planning number from the build process itself. Where the platform has no integrated accelerator, the controller needs an expansion slot that accepts the card you choose.
When a CUDA-enabled OpenCV build is the plan, provision a controller with expansion room and memory: the MV-VC5525-256G70-NN pairs PCIe expansion support with 32 GB of memory and 8 built-in light channels.
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- PoE
- PoE camera ports
- Light outputs
- 8 built-in channels
If the workload is model inference rather than a hand-built OpenCV-cuda pipeline, a dedicated inference terminal moves that stage off the general-purpose build: the MV-VAC3504X-128G01 is a single-card AI inference terminal with 4 built-in light channels and 5+ LAN ports for the camera network.
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- Light outputs
- 4 built-in channels
Browse the vision controllers to compare memory, LAN port counts and light channels, or use the system configurator with the camera count and interfaces you already know.
The decisions and the checks that keep them
| Probe result | Decision |
|---|---|
| CUDA reads NO, margin 20% or more of the frame budget | Stay on CPU. Revisit only when the model or the frame rate changes |
| CUDA reads NO, CPU over budget | Remove the shadowing package or make one CUDA-enabled build; the probe must then show a count of 1 or more |
| CUDA reads YES, count 0 | Runtime problem: run the toolkit device sample, and change CUDA-stack parts only through the vendor update path |
| Count 1 or more | Ship it, keep data on the device, and watch context lifetime around camera closes |
Before the next install on any bench or line machine:
- Run the three-line probe and save the output; it is the baseline every later check compares against.
- Keep one OpenCV per machine, installed by one package manager.
- After any install or upgrade, re-run the probe: the version and the CUDA line must be unchanged.
- Match the toolkit to the OpenCV release before compiling, and read the configure summary rather than trusting the absence of errors.
- On embedded boards, make CUDA-stack changes only through the vendor's documented path.
If the probe says the library is healthy but the frame budget still does not close, send us the probe output through the contact page — expert support is included with every order, and we will help you read the build report or size the machine. More systems-side guides sit on the systems topic page.
Questions
Does a zero from getCudaEnabledDeviceCount() mean the GPU is faulty?
No. The function reports what the linked OpenCV library supports at compile time, not the state of the hardware. A perfectly healthy GPU paired with a CPU-only build still returns 0. To test the hardware itself, build and run the device sample that ships with the CUDA toolkit: if it lists the GPU, the device and driver are fine, and the library is the only problem.
Can I add CUDA to the standard opencv-python wheel?
No. The wheels published on the Python package index are built without CUDA, and no switch turns it on afterwards. A CUDA-enabled Python binding is one you compile yourself against your toolkit, or one your board vendor ships. Build it into a pinned environment or a container so a later package install cannot shadow it again.
Why does the count differ between two terminals on the same machine?
Different environments load different bindings. Run the probe from the exact environment your application uses: same interpreter, same virtual environment or container, same user. cv2.__version__ and the module's __file__ attribute then identify the build behind that session. Two terminals can honestly report two different OpenCV builds when one activates a virtual environment and the other does not.
Will every OpenCV function run faster once CUDA is enabled?
No. Only the calls you explicitly move to the GPU benefit, and every copy between CPU memory and GPU memory costs time. One-shot operations on small images frequently finish faster on the CPU. The GPU pays for itself when several heavy stages run in sequence over data that stays resident on the device between stages.

