Install OpenCV 5 on Linux: pip, Source, and CUDA

To install OpenCV 5 on Linux reliably, first decide whether the project needs only Python, native C++ libraries, custom backends, or CUDA. On Ubuntu 24.04, for example, apt install python3-opencv still selects OpenCV 4.6.0. A command can therefore succeed while installing the wrong release for an OpenCV 5 tutorial.

Table of Contents

The 60-second selector below sends each reader directly to the smallest verified installation that meets the application’s requirements: a prebuilt Python wheel, a versioned CPU source build, or an optional CUDA source build.

OpenCV 5.0.0 was released on June 6, 2026 and raises the C++ baseline to C++17. It also reorganizes native modules, so a correct installation must be verified by its capabilities—not only by a version string. OpenCV 5.0.0 release · OpenCV 5 overview

Verification boundary: The wheel, desktop CPU source, and headless CUDA source paths were tested end to end on Ubuntu 24.04.4 LTS x86-64. The original wheel/CUDA verification ran July 17, 2026, the desktop CPU build ran July 19, and the final companion regression reran every profile July 20. Both source builds used GCC 13.3, CMake 3.28, Python 3.12, and opencv_contrib 5.0.0. The desktop profile exercised GTK 3, FFmpeg, and GStreamer; the CUDA profile used CUDA 12.9, cuDNN 9.11.1, and four NVIDIA GTX 1080 Ti GPUs. Routes for other distributions and architectures are identified from primary package sources but are not presented as runtime-tested installations. Ubuntu 24.04 remains under standard support through May 2029 and is listed in NVIDIA’s CUDA 12.9 Linux support matrix. Ubuntu release cycle · CUDA 12.9 Linux installation guide

What You Will Learn

  • How to choose between pip, a distribution package, and a source build.
  • How to install exactly one OpenCV 5 Python package in an isolated environment.
  • How to route Ubuntu, Debian, Fedora-family, Arch, Alpine, WSL2, Jetson, and ARM systems without applying the wrong instructions.
  • How to build OpenCV 5 and matching contrib modules on Ubuntu 24.04.
  • How to install native libraries without overwriting the system OpenCV.
  • How to build Python bindings and verify their actual import path.
  • How to enable CUDA and test every visible NVIDIA GPU.
  • How to use OpenCV 5 from a CMake C++ project.
  • How to diagnose wrong versions, missing modules, linker conflicts, and driver mismatches.
  • How to run a downloadable verifier that records Python, C++, linkage, and backend evidence.

Table of Contents

  1. Choose Your Route in 60 Seconds
  2. Check the Existing Linux Environment
  3. Install OpenCV 5 for Python with pip
  4. Build and Install OpenCV 5 from Source
  5. Build OpenCV 5 with CUDA
  6. Use OpenCV 5 from C++
  7. Verify the Installed Capabilities
  8. Troubleshoot OpenCV 5 Installation Problems
  9. Upgrade, Roll Back, and Uninstall Safely
  10. OpenCV 5 Linux Installation FAQ
  11. Conclusion
  12. References

1. Choose Your Route in 60 Seconds

Choose the first lane that satisfies the application. A source build is not inherently better than a wheel; it is useful only when the project needs capabilities the wheel does not provide.

If you are unsure, choose Lane A. Move to Lane B only when native C++ or a different feature inventory is required, and move to Lane C only when a measured workload benefits from CUDA.

Prefer an automated evidence record? The companion verifier checks the selected wheel or Python-enabled source profile after installation. Lane A readers can jump directly there after Section 3.3.

A Linux distribution package is still reasonable when its candidate version is OpenCV 5 and OS-managed updates matter more than an isolated, pinned installation. It is not a universal fast lane because the available version depends on the distribution and release.

1.1 Time, disk space, and privileges

Plan resources before starting a source build. The measurements below come from the verified 12-thread Ubuntu host; they are reference points, not performance promises.

The reader-facing source commands use eight parallel jobs. Parallelism is a tuning value, not a requirement: use --parallel 2 or --parallel 4 on a smaller machine or whenever compilation causes heavy swapping or an out-of-memory termination. The CUDA Toolkit, system package cache, model files, and application data are not included in the measured OpenCV workspace.

1.2 Route other Linux platforms safely

Before following a route, identify the operating system, architecture, C library, and special platform markers:

cat /etc/os-release
uname -m
ldd --version 2>&1 | head -n 1
python3 -c 'import platform, sys; print(platform.machine(), sys.version)'
grep -qi microsoft /proc/version 2>/dev/null && echo "WSL detected" || true
test -r /etc/nv_tegra_release && cat /etc/nv_tegra_release || true

Only the first row below is runtime-tested in this guide. The other routes reflect current upstream wheel or distribution metadata and deliberately avoid claiming that Ubuntu package names apply everywhere.

Package availability in this table was checked July 20, 2026. Rolling repositories will change, so inspect the linked metadata before installation.

The wheel-platform facts come from the OpenCV 5.0.0.93 file list and Python’s manylinux/musllinux compatibility specification. Current distribution versions are documented by Debian, Fedora, Arch, and Alpine. WSL and Jetson CUDA rules come from NVIDIA’s CUDA on WSL guide and JetPack documentation.

1.3 Package-manager alternatives that already carry OpenCV 5

These package-manager routes are useful when the operating-system repository is behind; most are CPU-oriented, while Arch also ships a CUDA family. They are alternatives to—not layers on top of—the wheel and source environments in this guide.

Arch Linux: inspect the rolling repository, update the complete system, and select one package family:

pacman -Si opencv python-opencv opencv-cuda python-opencv-cuda

# CPU family
sudo pacman -Syu opencv python-opencv

# Or CUDA family; do not install both families
sudo pacman -Syu opencv-cuda python-opencv-cuda

conda-forge: create a channel-isolated CPU environment. The current OpenCV 5.0.0 builds cover linux-64, linux-aarch64, and linux-ppc64le; the recipe does not provide a CUDA variant. Do not add a pip OpenCV wheel to the same environment. conda-forge OpenCV package · opencv-feedstock

conda create --name opencv5 \
  --channel conda-forge \
  --override-channels \
  "opencv=5.0.0"
conda activate opencv5

Homebrew on Linux: use the current bottled CPU formula primarily as a convenient native C++ route. It pulls a substantial dependency stack and is not a CUDA build. OpenCV formula metadata · Homebrew on Linux

brew update
brew info opencv
brew install opencv
opencv_version
brew --prefix opencv

Whichever package manager is used, finish with the same identity-and-capability principle as Section 7: confirm version, import path or linked-library path, build inventory, and a representative runtime operation.

On Ubuntu or Debian, do not assume that the distribution package is OpenCV 5. Check first:

apt-cache policy python3-opencv libopencv-dev

On the tested Ubuntu 24.04.4 machine, both candidates were 4.6.0+dfsg-13.1ubuntu1. Those packages remain valid for an OpenCV 4 application, but they are not the installation used in this article. Ubuntu Noble python3-opencv package · Ubuntu Noble libopencv-dev package

If the goal is to upgrade an existing application rather than start a new environment, installation is only the first step. OpenCV 5 changes the C++ module layout and removes some legacy APIs; use the OpenCV 5 migration guide before switching a production deployment.

2. Check the Existing Linux Environment

Before installing anything, record the distribution, architecture, and Python version. The loop also inventories the compiler and CMake for source-build readers; a missing source tool is expected in a wheel-only container.

Shell requirement: The multi-line shell blocks in this guide target GNU Bash 4 or newer. They use Bash arrays and strict-mode features; do not run them with sh. Simple one-line package-manager commands can still be entered individually.

cat /etc/os-release
uname -m
python3 --version

for tool in gcc g++ cmake; do
  if command -v "$tool" >/dev/null 2>&1; then
    "$tool" --version | head -n 1
  else
    printf '%s: not installed (needed only for a source route)\n' "$tool"
  fi
done

OpenCV 5 requires C++17. The official migration page lists GCC 8, Clang 9, and MSVC 2017 19.14 as minimum compiler generations. Ubuntu 24.04’s GCC 13 and CMake 3.28 are comfortably above those minimums. OpenCV 4-to-5 build requirements

Next, look for existing Python and native installations:

python3 -m pip list | grep -Ei 'opencv|numpy' || true

python3 - <<'PY'
try:
    import cv2
    print("OpenCV:", cv2.__version__)
    print("Imported from:", cv2.__file__)
except ModuleNotFoundError:
    print("No cv2 module is visible to this Python interpreter")
PY

if command -v pkg-config >/dev/null 2>&1; then
  pkg-config --modversion opencv5 2>/dev/null || true
  pkg-config --modversion opencv4 2>/dev/null || true
else
  echo "pkg-config: not installed (native inventory skipped)"
fi

if command -v ldconfig >/dev/null 2>&1; then
  ldconfig -p 2>/dev/null | grep libopencv | head || true
else
  echo "ldconfig: not available (native inventory skipped)"
fi

The import path is as important as cv2.__version__. A virtual environment can accidentally import a system module from /usr/lib, a manual install from /usr/local, or a different environment. Native applications have the same class of problem when CMake or the dynamic linker selects OpenCV 4 after OpenCV 5 has been installed elsewhere.

Two rules prevent most conflicts:

  1. Install Python wheels in a virtual environment.
  2. Install native OpenCV 5 into a dedicated prefix instead of overwriting /usr or /usr/local.

The source recipe below uses $HOME/.local/opencv/5.0.0. A system administrator can use /opt/opencv/5.0.0 instead, but a user-owned prefix avoids elevated privileges and makes rollback simpler.

3. Install OpenCV 5 for Python with pip

3.1 Create an isolated Python environment

On Ubuntu, install the venv support package if it is not already present:

sudo apt update
sudo apt install -y python3-venv

Create the environment and address its interpreter explicitly:

create_opencv_wheel_venv() {
  OPENCV_WHEEL_VENV="$HOME/.venvs/opencv5-wheel"
  OPENCV_WHEEL_PYTHON="$OPENCV_WHEEL_VENV/bin/python"

  python3 -m venv "$OPENCV_WHEEL_VENV" || return
  [ -x "$OPENCV_WHEEL_PYTHON" ] || return 2
  "$OPENCV_WHEEL_PYTHON" -m pip install --upgrade pip
}

create_opencv_wheel_venv

Using the virtual environment’s full interpreter path ties every installer command to that environment even when shell activation is absent or fails. This avoids the common mistake where pip and python refer to different environments.

3.2 Select exactly one wheel

OpenCV provides four mutually exclusive wheel variants. They all install into the same cv2 namespace, so do not install more than one in an environment. Official OpenCV Python wheel project

For release 5.0.0.93, PyPI publishes CPython 3.7+ Linux binaries for x86-64 and aarch64 systems with glibc 2.17 or newer. It does not publish an ARM32 or musl/Alpine wheel. The --only-binary=:all: option below makes an unsupported platform fail immediately instead of silently beginning an unplanned source build.

For an application that actually calls OpenCV HighGUI functions such as cv2.imshow:

OPENCV_WHEEL_PYTHON="$HOME/.venvs/opencv5-wheel/bin/python"
"$OPENCV_WHEEL_PYTHON" -m pip install --only-binary=:all: \
  "opencv-python==5.0.0.93"

For a headless server that needs contrib modules:

OPENCV_WHEEL_PYTHON="$HOME/.venvs/opencv5-wheel/bin/python"
"$OPENCV_WHEEL_PYTHON" -m pip install --only-binary=:all: \
  "opencv-contrib-python-headless==5.0.0.93"

“Headless” describes the OpenCV package, not the entire application. A desktop program that renders through PyQt, PySide, GTK, a browser, or another GUI toolkit should normally use a headless OpenCV wheel unless it also calls HighGUI. This avoids loading OpenCV’s bundled Qt stack alongside the application’s GUI stack.

The pin fixes the OpenCV package version. For a fully repeatable Python environment, also record the interpreter and transitive dependencies with "$OPENCV_WHEEL_PYTHON" -m pip freeze. When a newer OpenCV 5 wheel is adopted, update the pin deliberately and rerun the capability checks. OpenCV Python 5.0.0.93 files

The official wheels are prebuilt CPU packages. The non-headless Linux wheels tested here reported Qt 5 and FFmpeg; the headless wheels reported no GUI. All four reported FFmpeg, none reported GStreamer, and none provided CUDA execution. Build from source when the application requires a different backend inventory. Official OpenCV wheel build scope

3.3 Verify the Python wheel

This smoke test checks the exact release, imported environment, image processing, and image I/O without leaving a fixed output file behind. Clearing PYTHONOPTIMIZE ensures an inherited optimization setting cannot disable its assertions:

OPENCV_WHEEL_PYTHON="$HOME/.venvs/opencv5-wheel/bin/python"
PYTHONOPTIMIZE= "$OPENCV_WHEEL_PYTHON" - <<'PY'
from pathlib import Path
import sys
import tempfile

import cv2
import numpy as np

print("OpenCV:", cv2.__version__)
print("Imported from:", cv2.__file__)
assert cv2.__version__ == "5.0.0"

expected_environment = Path(sys.prefix).resolve()
module_path = Path(cv2.__file__).resolve()
assert expected_environment in module_path.parents

image = np.zeros((240, 320, 3), dtype=np.uint8)
cv2.circle(image, (160, 120), 70, (0, 200, 255), thickness=-1)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

with tempfile.TemporaryDirectory() as directory:
    output = Path(directory) / "opencv5-wheel-smoke.png"
    assert cv2.imwrite(str(output), gray)
    loaded = cv2.imread(str(output), cv2.IMREAD_GRAYSCALE)
    assert loaded is not None and loaded.shape == gray.shape

print("Image processing and I/O: PASS")
PY

Also inspect the build rather than guessing what the package contains:

OPENCV_WHEEL_PYTHON="$HOME/.venvs/opencv5-wheel/bin/python"
"$OPENCV_WHEEL_PYTHON" - <<'PY'
from importlib import metadata

import cv2

wheel_families = {
    "opencv-python",
    "opencv-contrib-python",
    "opencv-python-headless",
    "opencv-contrib-python-headless",
}
installed = sorted(
    distribution.metadata["Name"].lower().replace("_", "-")
    for distribution in metadata.distributions()
    if distribution.metadata.get("Name", "").lower().replace("_", "-")
    in wheel_families
)
if len(installed) != 1:
    raise RuntimeError(f"Expected exactly one OpenCV wheel; found {installed}")

print("Single cv2 owner:", installed[0])
print(cv2.getBuildInformation())
print("Has xfeatures2d:", hasattr(cv2, "xfeatures2d"))
print("CUDA devices:", cv2.cuda.getCudaEnabledDeviceCount())
PY

A CPU wheel may expose the cv2.cuda namespace while returning zero devices because its native libraries were not compiled with CUDA. Namespace presence is not a CUDA verification test.

If a non-headless wheel was chosen because the application uses HighGUI, verify a real window from an active graphical session:

OPENCV_WHEEL_PYTHON="$HOME/.venvs/opencv5-wheel/bin/python"
PYTHONOPTIMIZE= "$OPENCV_WHEEL_PYTHON" - <<'PY'
import cv2
import numpy as np

image = np.zeros((48, 64, 3), dtype=np.uint8)
try:
    cv2.namedWindow("opencv5-wheel-gui")
    cv2.imshow("opencv5-wheel-gui", image)
    cv2.waitKey(1)
    backend = cv2.currentUIFramework()
    print("HighGUI backend:", backend)
    if not backend or backend == "NONE":
        raise RuntimeError("No active HighGUI backend")
finally:
    cv2.destroyAllWindows()
PY

Skip that window test for a headless wheel. For a non-headless wheel with no $DISPLAY, the Section 7.4 companion verifier requires and uses xvfb-run; on Ubuntu or Debian install the xvfb package first, or choose a headless wheel when HighGUI is unnecessary.

Stop here — Python wheel route complete. The installation is ready when the smoke test exits successfully, reports OpenCV 5.0.0, imports cv2 from the selected virtual environment, prints Image processing and I/O: PASS, and reports exactly one wheel owner. The CUDA device count should be zero, and xfeatures2d should be present only for a contrib wheel. If the application uses HighGUI, the window test must also pass. Sections 4–6 and 7.1–7.3 are unnecessary unless the project also needs C++, custom backends, or CUDA; optionally jump to Section 7.4 for an automated evidence record.

4. Build and Install OpenCV 5 from Source

Use a source build for C++ development, contrib modules, a custom backend inventory, or CUDA. The following path keeps source, build output, native installation, and Python environment separate.

4.1 Install Ubuntu build dependencies

Install the required toolchain first:

sudo apt update
sudo apt install -y \
  build-essential cmake ninja-build git pkg-config \
  python3-dev python3-venv

For a desktop build with common image, video, GUI, parallel, and numerical integrations, add:

sudo apt install -y \
  libgtk-3-dev \
  libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
  libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
  gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
  libjpeg-dev libpng-dev libtiff-dev libwebp-dev libopenjp2-7-dev \
  libtbb-dev libeigen3-dev liblapacke-dev

Every package name above was installed from the Ubuntu 24.04 Noble repositories for the desktop verification. The strict CPU command in Section 4.4 assumes that both dependency groups are installed. A headless server can omit GTK, FFmpeg, and GStreamer while retaining the image-codec and numerical development packages, then disable those three backends in CMake. If any other package is omitted, adjust the corresponding WITH_... and BUILD_... options intentionally; do not paste the strict command unchanged and expect CMake to fill every gap. Both the desktop CPU profile in this section and the headless CUDA profile in Section 5 were built and runtime-tested end to end.

IEEE-1394/DC1394 capture is intentionally omitted. OpenCV 5.0.0 defaults WITH_1394 to OFF; if an application uses a FireWire camera, install libdc1394-dev and change that option to ON.

If Ninja is not available system-wide but Python is, a rootless alternative is:

OPENCV_BUILD_TOOLS_VENV="$PWD/.venv-build-tools"
python3 -m venv "$OPENCV_BUILD_TOOLS_VENV"
"$OPENCV_BUILD_TOOLS_VENV/bin/python" -m pip install ninja
export PATH="$OPENCV_BUILD_TOOLS_VENV/bin:$PATH"

The tested CUDA build used the Python Ninja package successfully; the desktop CPU build used Ubuntu’s ninja-build package.

4.2 Download matching source tags and verify commits

Set versioned paths. The variable names are task-specific so they do not alter shell defaults:

OPENCV_VERSION=5.0.0
OPENCV_WORKDIR="$HOME/src/opencv-$OPENCV_VERSION"
OPENCV_PREFIX="$HOME/.local/opencv/$OPENCV_VERSION"
OPENCV_PYTHON_VENV="$HOME/.venvs/opencv-$OPENCV_VERSION"

mkdir -p "$OPENCV_WORKDIR"

Before continuing, inspect the selected prefix and virtual-environment paths. If either already contains an older or partial build, choose a new explicit suffix instead of installing over it; stale modules or a previously installed wheel can make a new build appear to pass while importing the wrong files.

Clone the main and contrib repositories at exactly matching tags, then assert the commits used by this guide:

(
set -o errexit
set -o nounset

OPENCV_VERSION=5.0.0
OPENCV_WORKDIR="$HOME/src/opencv-$OPENCV_VERSION"
mkdir -p "$OPENCV_WORKDIR"

git clone --branch "$OPENCV_VERSION" --depth 1 \
   \
  "$OPENCV_WORKDIR/opencv"

git clone --branch "$OPENCV_VERSION" --depth 1 \
   \
  "$OPENCV_WORKDIR/opencv_contrib"

OPENCV_EXPECTED_COMMIT=40738fb16ceddb5fb3fea747585f7ce6abb0605b
OPENCV_CONTRIB_EXPECTED_COMMIT=755e50675d97db9b7d449d8bd6b09888646f6c6e

test "$(git -C "$OPENCV_WORKDIR/opencv" rev-parse HEAD)" = \
  "$OPENCV_EXPECTED_COMMIT"

test "$(git -C "$OPENCV_WORKDIR/opencv_contrib" rev-parse HEAD)" = \
  "$OPENCV_CONTRIB_EXPECTED_COMMIT"
)

For 5.0.0, the verified commits were 40738fb16ceddb5fb3fea747585f7ce6abb0605b for opencv and 755e50675d97db9b7d449d8bd6b09888646f6c6e for opencv_contrib.

Do not use moving master or 5.x branches in a reproducible installation recipe. A main/contrib mismatch may fail during configuration, compilation, or much later when an expected symbol is used. The source revisions and Python packages are pinned here; the Ubuntu package snapshot and pip bootstrap are not, so this is a capability-reproducible recipe rather than a bit-for-bit build.

4.3 Prepare Python before configuring CMake

For a C++-only build, skip this subsection and set OPENCV_BUILD_PYTHON=OFF in Section 4.4. After installation, skip the .pth and Python import blocks in Section 4.5, then continue with Section 6.

Create the Python environment and install NumPy before running CMake:

OPENCV_VERSION=5.0.0
OPENCV_PREFIX="$HOME/.local/opencv/$OPENCV_VERSION"
OPENCV_PYTHON_VENV="$HOME/.venvs/opencv-$OPENCV_VERSION"

/usr/bin/python3 -m venv "$OPENCV_PYTHON_VENV"
"$OPENCV_PYTHON_VENV/bin/python" -m pip install --upgrade pip
"$OPENCV_PYTHON_VENV/bin/python" -m pip install "numpy==2.2.6"

OPENCV_PYTHON_VERSION=$("$OPENCV_PYTHON_VENV/bin/python" -c \
  'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')

OPENCV_PYTHON_PACKAGES="$OPENCV_PREFIX/lib/python${OPENCV_PYTHON_VERSION}/site-packages"

The Python development library must match the interpreter. On Typhoon, a Python 3.14 interpreter and NumPy headers were visible, but CMake reported Libraries: NO and install path: - because Python 3.14 development files were absent. Switching to Ubuntu’s Python 3.12 interpreter with python3-dev enabled the binding.

4.4 Configure a strict CPU build

Start with a new build directory. Never point OpenCV 5 at a CMake cache generated for OpenCV 4.

OPENCV_VERSION=5.0.0
OPENCV_WORKDIR="$HOME/src/opencv-$OPENCV_VERSION"
OPENCV_PREFIX="$HOME/.local/opencv/$OPENCV_VERSION"
OPENCV_PYTHON_VENV="$HOME/.venvs/opencv-$OPENCV_VERSION"
OPENCV_BUILD="$OPENCV_WORKDIR/build-cpu"
OPENCV_BUILD_PYTHON=ON  # change to OFF for C++ only

(
set -o errexit
set -o nounset
set -o pipefail

case "$OPENCV_BUILD_PYTHON" in
  ON)
    test -x "$OPENCV_PYTHON_VENV/bin/python"
    OPENCV_PYTHON_VERSION=$("$OPENCV_PYTHON_VENV/bin/python" -c \
      'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
    OPENCV_PYTHON_PACKAGES="$OPENCV_PREFIX/lib/python${OPENCV_PYTHON_VERSION}/site-packages"
    OPENCV_PYTHON_ARGS=(
      -DBUILD_opencv_python3=ON
      "-DPYTHON3_EXECUTABLE=$OPENCV_PYTHON_VENV/bin/python"
      "-DPYTHON3_PACKAGES_PATH=$OPENCV_PYTHON_PACKAGES"
    )
    ;;
  OFF)
    OPENCV_PYTHON_ARGS=(-DBUILD_opencv_python3=OFF)
    ;;
  *)
    printf 'OPENCV_BUILD_PYTHON must be ON or OFF, not %s\n' \
      "$OPENCV_BUILD_PYTHON" >&2
    exit 2
    ;;
esac

cmake -S "$OPENCV_WORKDIR/opencv" -B "$OPENCV_BUILD" -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_CXX_STANDARD=17 \
  -DCMAKE_INSTALL_PREFIX="$OPENCV_PREFIX" \
  -DCMAKE_INSTALL_RPATH="$OPENCV_PREFIX/lib" \
  -DOPENCV_EXTRA_MODULES_PATH="$OPENCV_WORKDIR/opencv_contrib/modules" \
  -DOPENCV_GENERATE_PKGCONFIG=ON \
  -DENABLE_CONFIG_VERIFICATION=ON \
  -DBUILD_TESTS=OFF \
  -DBUILD_PERF_TESTS=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DBUILD_JAVA=OFF \
  "${OPENCV_PYTHON_ARGS[@]}" \
  -DWITH_CUDA=OFF \
  -DBUILD_opencv_cudacodec=OFF \
  -DWITH_1394=OFF \
  -DWITH_GTK=ON \
  -DWITH_GTK_2_X=OFF \
  -DWITH_QT=OFF \
  -DWITH_WAYLAND=OFF \
  -DWITH_OPENGL=OFF \
  -DWITH_FFMPEG=ON \
  -DWITH_GSTREAMER=ON \
  -DWITH_V4L=ON \
  -DWITH_TBB=ON \
  -DWITH_EIGEN=ON \
  -DWITH_LAPACK=ON \
  -DWITH_JPEG=ON \
  -DWITH_PNG=ON \
  -DWITH_TIFF=ON \
  -DWITH_WEBP=ON \
  -DWITH_OPENJPEG=ON \
  -DBUILD_JPEG=OFF \
  -DBUILD_PNG=OFF \
  -DBUILD_TIFF=OFF \
  -DBUILD_WEBP=OFF \
  -DBUILD_OPENJPEG=OFF \
  -DBUILD_TBB=OFF \
  -DBUILD_CLAPACK=OFF \
  -DWITH_AVIF=OFF \
  -DWITH_VTK=OFF \
  -DWITH_NVCUVID=OFF \
  -DWITH_NVCUVENC=OFF \
  -DWITH_JASPER=OFF \
  -DWITH_OPENEXR=OFF \
  -DWITH_OPENCLAMDFFT=OFF \
  -DWITH_OPENCLAMDBLAS=OFF \
  -DWITH_VA=OFF \
  -DWITH_VA_INTEL=OFF \
  -DWITH_TESSERACT=OFF \
  2>&1 | tee "$OPENCV_WORKDIR/configure-cpu.log"
)

Why list so many OFF options? ENABLE_CONFIG_VERIFICATION=ON converts requested-but-missing dependencies into a configuration failure. OpenCV 5 has several optional integrations that default to enabled. Explicitly disabling the integrations this recipe does not install makes the feature contract reproducible. It is safer than turning verification off and accepting a quietly reduced build. The version-pinned OpenCV 5.0.0 configuration reference source documents the major build and install controls.

One subtlety matters for system-library provenance: for several codecs and LAPACK, BUILD_...=OFF means “try the system library first,” not “forbid a bundled fallback.” Strict verification proves that the capability exists; the configuration summary shows where it came from. In the tested desktop configuration, JPEG, PNG, TIFF, WebP, OpenJPEG, and LAPACK resolved to system libraries under /usr; Section 7.2 separately validates FFmpeg, GStreamer, and GTK behavior at runtime. The CUDA profile explicitly selects bundled CLAPACK.

CMake may still download pinned third-party sources or data during configuration. A shallow Git clone alone is not an offline build; preserve OpenCV’s download cache or configure a controlled mirror when the deployment must be reproducible without internet access.

For a headless build that retains the non-GUI dependencies from Section 4.1, set WITH_GTK, WITH_FFMPEG, and WITH_GSTREAMER to OFF; TBB and Eigen can remain enabled. If TBB, Eigen, LAPACK, or any image-codec development package is also omitted, change its WITH_... option and do not force the matching bundled BUILD_... option to OFF. A strict configuration must describe the packages that are actually present.

Before compiling, inspect the summary:

grep -E \
  'To be built:|Disabled:|Unavailable:|GUI:|FFMPEG:|GStreamer:|Parallel framework:|Eigen:|Lapack:|JPEG:|PNG:|TIFF:|WEBP:|JPEG 2000:|Python 3:|Interpreter:|Libraries:|numpy:|install path:' \
  "$OPENCV_WORKDIR/configure-cpu.log"

For a Python build, confirm all of these:

  • python3 appears under To be built.
  • The expected interpreter is shown.
  • A Python library is found; it must not say Libraries: NO.
  • NumPy points into the intended environment.
  • The install path is not -.

4.5 Compile and install into the versioned prefix

Build with a parallelism level appropriate for the machine:

OPENCV_VERSION=5.0.0
OPENCV_WORKDIR="$HOME/src/opencv-$OPENCV_VERSION"
OPENCV_PREFIX="$HOME/.local/opencv/$OPENCV_VERSION"
OPENCV_PYTHON_VENV="$HOME/.venvs/opencv-$OPENCV_VERSION"
OPENCV_BUILD="$OPENCV_WORKDIR/build-cpu"

(
set -o errexit
set -o nounset
set -o pipefail

cmake --build "$OPENCV_BUILD" --parallel 8 \
  2>&1 | tee "$OPENCV_WORKDIR/build-cpu.log"

cmake --install "$OPENCV_BUILD" \
  2>&1 | tee "$OPENCV_WORKDIR/install-cpu.log"
)

The generated Python binding translation unit, cv2.cpp, can take several minutes to compile, so a build may appear stationary near 100% while still making progress.

Connect the installed Python package to the virtual environment:

(
set -o errexit
set -o nounset

OPENCV_PYTHON_VERSION=$("$OPENCV_PYTHON_VENV/bin/python" -c \
  'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
OPENCV_PYTHON_PACKAGES="$OPENCV_PREFIX/lib/python${OPENCV_PYTHON_VERSION}/site-packages"
OPENCV_VENV_SITE=$("$OPENCV_PYTHON_VENV/bin/python" -c \
  'import site; print(site.getsitepackages()[0])')

OPENCV_VENV_REAL=$(realpath -e -- "$OPENCV_PYTHON_VENV")
OPENCV_VENV_SITE_REAL=$(realpath -e -- "$OPENCV_VENV_SITE")
OPENCV_VENV_PARENT_REAL=$(realpath -e -- "$HOME/.venvs")
test -f "$OPENCV_VENV_REAL/pyvenv.cfg"
case "$OPENCV_VENV_REAL/" in
  "$OPENCV_VENV_PARENT_REAL"/*) ;;
  *)
    printf 'Refusing a venv that resolves outside %s: %s\n' \
      "$OPENCV_VENV_PARENT_REAL" "$OPENCV_VENV_REAL" >&2
    exit 2
    ;;
esac

case "$OPENCV_VENV_SITE_REAL/" in
  "$OPENCV_VENV_REAL"/*) ;;
  *)
    printf 'Refusing .pth write outside the selected venv: %s\n' \
      "$OPENCV_VENV_SITE_REAL" >&2
    exit 2
    ;;
esac

test -d "$OPENCV_PYTHON_PACKAGES"
printf '%s\n' "$OPENCV_PYTHON_PACKAGES" \
  > "$OPENCV_VENV_SITE_REAL/opencv5-prefix.pth"
)

Now verify that the environment imports from the versioned prefix:

PYTHONOPTIMIZE= "$OPENCV_PYTHON_VENV/bin/python" - "$OPENCV_PREFIX" <<'PY'
from pathlib import Path
import sys

import cv2

print("OpenCV:", cv2.__version__)
print("Imported from:", cv2.__file__)
print("Has xfeatures2d:", hasattr(cv2, "xfeatures2d"))

expected_prefix = Path(sys.argv[1]).resolve()
module_path = Path(cv2.__file__).resolve()
assert cv2.__version__ == "5.0.0"
assert expected_prefix in module_path.parents
assert hasattr(cv2, "xfeatures2d")
PY

The assertions require cv2.__file__ to point into $OPENCV_PREFIX, not a system directory or wheel environment. Section 7 adds runtime tests for the desktop backends requested by this profile.

5. Build OpenCV 5 with CUDA

Scope — CUDA must work before OpenCV is configured. This section builds OpenCV against an existing NVIDIA software stack; it does not install or upgrade the NVIDIA display driver or CUDA Toolkit. On conventional x86-64 Linux, do not continue until nvidia-smi sees the GPU and the selected nvcc runs. Repair that foundation with NVIDIA’s platform-specific installation guide first if either check fails. On WSL2, the CUDA driver comes from the Windows host—do not install a Linux display driver inside WSL. On Jetson, retain the JetPack/Jetson Linux-paired stack and replace the x86-64 toolkit path, package, diagnostic, and architecture examples with values for that device. The downloadable companion’s headless-cuda verifier targets the tested conventional x86-64 profile and requires nvidia-smi; it is not a drop-in Jetson verifier. /usr/local/cuda-12.9, CUDA 12.9, and compute capability 61 are Typhoon’s tested values, not universal defaults. NVIDIA CUDA Linux installation guide · NVIDIA CUDA on WSL guide · NVIDIA JetPack documentation

The official Python wheels do not provide CUDA execution. For this lane, run the first toolchain command in Section 4.1 and complete Section 4.2 so the compiler, CMake, Ninja, Git, pkg-config, Python development files, and matching source tags exist. Then use the separate CUDA build directory, prefix, and optional Python environment below. The desktop-backend package group in Section 4.1 is unnecessary for the tested headless profile. DNN acceleration with cuDNN adds another dependency.

5.1 Verify the NVIDIA stack first

On conventional native Linux—not WSL2 or Jetson—run these checks before configuring OpenCV:

verify_cuda_prerequisites() {
CUDA_TOOLKIT_ROOT=/usr/local/cuda-12.9  # adjust if installed elsewhere
if [ ! -x "$CUDA_TOOLKIT_ROOT/bin/nvcc" ]; then
  printf 'nvcc is not executable under %s\n' "$CUDA_TOOLKIT_ROOT" >&2
  return 2
fi
export PATH="$CUDA_TOOLKIT_ROOT/bin:$PATH"

if ! command -v nvidia-smi >/dev/null 2>&1; then
  echo "nvidia-smi is not available" >&2
  return 2
fi
nvidia-smi || return
nvidia-smi --query-gpu=name,driver_version,compute_cap --format=csv || return
"$CUDA_TOOLKIT_ROOT/bin/nvcc" --version || return
if [ -r /proc/driver/nvidia/version ]; then
  cat /proc/driver/nvidia/version
else
  echo "NVIDIA kernel-driver record is unavailable on this host"
fi
}

verify_cuda_prerequisites

On WSL2, use the host-provided nvidia-smi (often also available at /usr/lib/wsl/lib/nvidia-smi), run the selected Linux nvcc --version, and skip /proc/driver/nvidia/version; the kernel driver belongs to Windows. On Jetson, inspect /etc/nv_tegra_release, run the JetPack-provided nvcc --version, and execute the CUDA Samples deviceQuery test. Jetson readers should not install a desktop Linux display driver or assume that nvidia-smi exists.

The tested profile below enables the classic DNN CUDA backend, so it also requires the cuDNN development headers and libraries. cuDNN is not a requirement for OpenCV’s general cv2.cuda image-processing modules. On the tested Ubuntu x86-64 host, once the NVIDIA CUDA repository is configured, install cuDNN for CUDA 12 using NVIDIA’s documented package and confirm that the development package and runtime libraries are visible:

sudo apt update
sudo apt install -y cudnn9-cuda-12

dpkg-query -W libcudnn9-dev-cuda-12
ldconfig -p | grep libcudnn

The meta-package selects a cuDNN 9 release for CUDA 12 from the configured repository. Pin its package version when a deployment requires the exact tested cuDNN release; Typhoon used 9.11.1. Jetson readers must keep JetPack’s paired cuDNN packages and skip this desktop package command; WSL2 readers should follow NVIDIA’s WSL-aware cuDNN instructions. If the application needs CUDA image processing but not the classic DNN CUDA backend, set both WITH_CUDNN=OFF and OPENCV_DNN_CUDA=OFF in the configuration below. OpenCV’s build explicitly requires cuDNN when OPENCV_DNN_CUDA=ON. NVIDIA cuDNN Linux installation · OpenCV 5.0.0 DNN CUDA dependency checks

Record the GPU model, driver, toolkit, and compute capability. The CUDA architecture flags are hardware-specific. Convert each capability major.minor to CMake’s dotless integer form: 6.1 becomes 61, 8.6 becomes 86, and 10.0 becomes 100. OpenCV’s legacy architecture option retains the dotted form. On a heterogeneous host, include every unique capability as matching semicolon-separated lists—for example, 61;86 and 6.1;8.6—or create separate builds. The tested GTX 1080 Ti is Pascal compute capability 6.1; do not copy 61 to a different GPU. NVIDIA CUDA GPU compute-capability table

NVIDIA lists Ubuntu 24.04 as a supported CUDA 12.9 host. CUDA 12.9 GA corresponds to Linux driver 575.51.03 or newer, while CUDA 12.x minor-version compatibility permits Linux drivers from 525.60.13 with restrictions. Typhoon’s 535.309.01 driver therefore used minor-version compatibility, not the forward-compatibility package. Driver-dependent features may still require a newer driver, and PTX produced by a newer toolkit cannot be JIT-compiled by an older driver. The build included native code for compute capability 6.1, and the four-GPU runtime test established that this path worked. Prefer driver 575.51.03 or newer when the application needs the full CUDA 12.9 driver feature set or newer PTX JIT support. CUDA 12.9 release notes · CUDA minor-version compatibility

5.2 Configure a fresh CUDA build directory

Keep the CUDA build, install prefix, and Python environment separate from the CPU installation. These assignments repeat the base paths so the section is safe to enter from a new shell:

This configuration requires CMake 3.18 or newer because it uses CMAKE_CUDA_ARCHITECTURES. Upgrade CMake before continuing on an older distribution or JetPack release; otherwise create a separately reviewed legacy configuration instead of silently dropping that setting. CMake CMAKE_CUDA_ARCHITECTURES documentation

The tested CUDA profile includes Python. For a C++-only CUDA build, set OPENCV_BUILD_PYTHON=OFF; retain the version, workdir, build, prefix, toolkit, and architecture variables. The conditional blocks then omit the Python environment, CMake binding options, and .pth selector.

prepare_opencv_cuda() {
OPENCV_VERSION=5.0.0
OPENCV_WORKDIR="$HOME/src/opencv-$OPENCV_VERSION"
OPENCV_CUDA_BUILD="$OPENCV_WORKDIR/build-cuda"
OPENCV_CUDA_PREFIX="$HOME/.local/opencv/${OPENCV_VERSION}-cuda12.9"
OPENCV_CUDA_PYTHON_VENV="$HOME/.venvs/opencv-${OPENCV_VERSION}-cuda12.9"
OPENCV_BUILD_PYTHON=ON  # change to OFF for C++ only

CUDA_TOOLKIT_ROOT=/usr/local/cuda-12.9  # adjust if installed elsewhere
if [ ! -x "$CUDA_TOOLKIT_ROOT/bin/nvcc" ]; then
  printf 'nvcc is not executable under %s\n' "$CUDA_TOOLKIT_ROOT" >&2
  return 2
fi
export PATH="$CUDA_TOOLKIT_ROOT/bin:$PATH"

if ! command -v cmake >/dev/null 2>&1; then
  echo "CMake is not installed" >&2
  return 2
fi
if ! IFS= read -r CMAKE_VERSION_LINE < <(cmake --version); then
  echo "Could not run cmake --version" >&2
  return 2
fi
CMAKE_VERSION=${CMAKE_VERSION_LINE#cmake version }
if [[ "$CMAKE_VERSION" =~ ^([0-9]+)[.]([0-9]+) ]]; then
  CMAKE_MAJOR=${BASH_REMATCH[1]}
  CMAKE_MINOR=${BASH_REMATCH[2]}
else
  printf 'Could not parse CMake version: %s\n' "$CMAKE_VERSION_LINE" >&2
  return 2
fi
if (( CMAKE_MAJOR < 3 || (CMAKE_MAJOR == 3 && CMAKE_MINOR < 18) )); then
  printf 'CMake 3.18 or newer is required, found %s\n' "$CMAKE_VERSION" >&2
  return 2
fi

CUDA_CMAKE_ARCH=61
CUDA_OPENCV_ARCH=6.1

# Heterogeneous example; use only capabilities actually present:
# CUDA_CMAKE_ARCH='61;86'
# CUDA_OPENCV_ARCH='6.1;8.6'

if [ "$OPENCV_BUILD_PYTHON" = ON ]; then
  /usr/bin/python3 -m venv "$OPENCV_CUDA_PYTHON_VENV" || return
  "$OPENCV_CUDA_PYTHON_VENV/bin/python" -m pip install --upgrade pip || return
  "$OPENCV_CUDA_PYTHON_VENV/bin/python" -m pip install "numpy==2.2.6" || return

  OPENCV_CUDA_PYTHON_VERSION=$("$OPENCV_CUDA_PYTHON_VENV/bin/python" -c \
    'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') || return

  OPENCV_CUDA_PYTHON_PACKAGES="$OPENCV_CUDA_PREFIX/lib/python${OPENCV_CUDA_PYTHON_VERSION}/site-packages"
elif [ "$OPENCV_BUILD_PYTHON" != OFF ]; then
  printf 'OPENCV_BUILD_PYTHON must be ON or OFF, not %s\n' \
    "$OPENCV_BUILD_PYTHON" >&2
  return 2
fi
}

prepare_opencv_cuda

The following strict, headless CUDA configuration matches the profile tested on Typhoon. It intentionally disables GUI, FFmpeg, and GStreamer; start from the desktop dependency profile in Section 4 if the CUDA application also needs those backends. Before building, confirm that the two architecture variables describe every GPU this installation must execute on.

(
set -o errexit
set -o nounset
set -o pipefail

case "$OPENCV_BUILD_PYTHON" in
  ON)
    OPENCV_CUDA_PYTHON_ARGS=(
      -DBUILD_opencv_python3=ON
      "-DPYTHON3_EXECUTABLE=$OPENCV_CUDA_PYTHON_VENV/bin/python"
      "-DPYTHON3_PACKAGES_PATH=$OPENCV_CUDA_PYTHON_PACKAGES"
    )
    ;;
  OFF)
    OPENCV_CUDA_PYTHON_ARGS=(-DBUILD_opencv_python3=OFF)
    ;;
  *)
    printf 'OPENCV_BUILD_PYTHON must be ON or OFF, not %s\n' \
      "$OPENCV_BUILD_PYTHON" >&2
    exit 2
    ;;
esac

cmake -S "$OPENCV_WORKDIR/opencv" -B "$OPENCV_CUDA_BUILD" -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_CXX_STANDARD=17 \
  -DCMAKE_CUDA_COMPILER="$CUDA_TOOLKIT_ROOT/bin/nvcc" \
  -DCUDAToolkit_ROOT="$CUDA_TOOLKIT_ROOT" \
  -DCMAKE_INSTALL_PREFIX="$OPENCV_CUDA_PREFIX" \
  -DCMAKE_INSTALL_RPATH="$OPENCV_CUDA_PREFIX/lib" \
  -DOPENCV_EXTRA_MODULES_PATH="$OPENCV_WORKDIR/opencv_contrib/modules" \
  -DOPENCV_GENERATE_PKGCONFIG=ON \
  -DENABLE_CONFIG_VERIFICATION=ON \
  -DBUILD_TESTS=OFF \
  -DBUILD_PERF_TESTS=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DBUILD_JAVA=OFF \
  "${OPENCV_CUDA_PYTHON_ARGS[@]}" \
  -DWITH_CUDA=ON \
  -DENABLE_CUDA_FIRST_CLASS_LANGUAGE=ON \
  -DCMAKE_CUDA_ARCHITECTURES="$CUDA_CMAKE_ARCH" \
  -DCUDA_ARCH_BIN="$CUDA_OPENCV_ARCH" \
  -DWITH_CUBLAS=ON \
  -DWITH_CUDNN=ON \
  -DOPENCV_DNN_CUDA=ON \
  -DBUILD_opencv_cudacodec=OFF \
  -DWITH_1394=OFF \
  -DWITH_GTK=OFF \
  -DWITH_FFMPEG=OFF \
  -DWITH_GSTREAMER=OFF \
  -DWITH_TBB=OFF \
  -DWITH_EIGEN=OFF \
  -DWITH_LAPACK=ON \
  -DBUILD_CLAPACK=ON \
  -DWITH_AVIF=OFF \
  -DWITH_VTK=OFF \
  -DWITH_NVCUVID=OFF \
  -DWITH_NVCUVENC=OFF \
  -DWITH_JASPER=OFF \
  -DWITH_OPENEXR=OFF \
  -DWITH_OPENCLAMDFFT=OFF \
  -DWITH_OPENCLAMDBLAS=OFF \
  -DWITH_VA=OFF \
  -DWITH_VA_INTEL=OFF \
  -DWITH_TESSERACT=OFF \
  2>&1 | tee "$OPENCV_WORKDIR/configure-cuda.log"
)

The first-class CUDA language option matters with strict verification: the tested configuration reported CUDA as present while that option was initially unset, which caused verification to fail. Setting it explicitly aligned the CMake request with the detected build.

cudacodec, NVCUVID, and NVCUVENC are disabled here because the CUDA Toolkit alone does not guarantee the NVIDIA Video Codec SDK interfaces needed by those paths. Enable them only when their headers, libraries, licensing, and deployment requirements have been handled intentionally.

The headless CUDA recipe builds OpenCV’s bundled CLAPACK explicitly. That preserves the LAPACK capability used by the tested build without requiring the desktop numerical package group from Section 4.1.

If the build includes Python, OpenCV 5.0.0’s new DNN engine is CPU-only. OPENCV_DNN_CUDA=ON enables the CUDA backend for the classic engine; it does not make the default ENGINE_AUTO path GPU-accelerated. In an inference script, replace the model path, then load the model and select both the CUDA backend and target:

import cv2

model_path = "model.onnx"  # replace with a compatible ONNX model
net = cv2.dnn.readNetFromONNX(model_path)
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)

Start that script with the classic engine forced for this process; in OpenCV 5.0.0, OPENCV_FORCE_DNN_ENGINE=1 selects ENGINE_CLASSIC. The setting must exist before the model is loaded:

OPENCV_FORCE_DNN_ENGINE=1 \
  "$OPENCV_CUDA_PYTHON_VENV/bin/python" inference.py

Then validate a model-specific forward pass. The cv2.cuda.cvtColor smoke test in Section 5.3 verifies CUDA image-processing modules, but it does not prove DNN or cuDNN compatibility for a particular model. OpenCV 5 DNN engine selection · OpenCV 5.0.0 DNN Net methods

Build and install into the CUDA-specific prefix, then connect its Python package to the CUDA-specific environment:

(
set -o errexit
set -o nounset
set -o pipefail

cmake --build "$OPENCV_CUDA_BUILD" --parallel 8 \
  2>&1 | tee "$OPENCV_WORKDIR/build-cuda.log"

cmake --install "$OPENCV_CUDA_BUILD" \
  2>&1 | tee "$OPENCV_WORKDIR/install-cuda.log"

if [ "$OPENCV_BUILD_PYTHON" = ON ]; then
  OPENCV_CUDA_VENV_SITE=$("$OPENCV_CUDA_PYTHON_VENV/bin/python" -c \
    'import site; print(site.getsitepackages()[0])')

  OPENCV_CUDA_VENV_REAL=$(realpath -e -- "$OPENCV_CUDA_PYTHON_VENV")
  OPENCV_CUDA_VENV_SITE_REAL=$(realpath -e -- "$OPENCV_CUDA_VENV_SITE")
  OPENCV_CUDA_VENV_PARENT_REAL=$(realpath -e -- "$HOME/.venvs")
  test -f "$OPENCV_CUDA_VENV_REAL/pyvenv.cfg"
  case "$OPENCV_CUDA_VENV_REAL/" in
    "$OPENCV_CUDA_VENV_PARENT_REAL"/*) ;;
    *)
      printf 'Refusing a venv that resolves outside %s: %s\n' \
        "$OPENCV_CUDA_VENV_PARENT_REAL" "$OPENCV_CUDA_VENV_REAL" >&2
      exit 2
      ;;
  esac

  case "$OPENCV_CUDA_VENV_SITE_REAL/" in
    "$OPENCV_CUDA_VENV_REAL"/*) ;;
    *)
      printf 'Refusing .pth write outside the selected venv: %s\n' \
        "$OPENCV_CUDA_VENV_SITE_REAL" >&2
      exit 2
      ;;
  esac

  test -d "$OPENCV_CUDA_PYTHON_PACKAGES"
  printf '%s\n' "$OPENCV_CUDA_PYTHON_PACKAGES" \
    > "$OPENCV_CUDA_VENV_SITE_REAL/opencv5-cuda-prefix.pth"
elif [ "$OPENCV_BUILD_PYTHON" != OFF ]; then
  printf 'OPENCV_BUILD_PYTHON must be ON or OFF, not %s\n' \
    "$OPENCV_BUILD_PYTHON" >&2
  exit 2
fi
)

The tested headless contrib/CUDA build imported cv2 from the CUDA-specific prefix and exposed contrib modules including xfeatures2d.

The successful Typhoon summary included:

NVIDIA CUDA:                   YES (ver 12.9.86, CUFFT CUBLAS)
NVIDIA GPU arch:             61
cuDNN:                         YES (ver 9.11.1)

CUDA 12.9 warned that offline compilation for architectures below compute capability 7.5 would be removed in a future toolkit. CUDA 13.0 subsequently removed offline compilation and library support for Maxwell, Pascal, and Volta; CUDA 12.x is therefore the last supported toolkit family for the GTX 1080 Ti. Pin a known-compatible CUDA 12.x toolchain for Pascal deployments instead of automatically following the newest release. CUDA 13.0 removed architectures

5.3 Test real GPU work on every device

Run this test from the source-built CUDA Python environment. A C++-only build should skip it and execute an equivalent cv::cuda::GpuMat upload, kernel, and download test in the native application:

PYTHONOPTIMIZE= "$OPENCV_CUDA_PYTHON_VENV/bin/python" - "$OPENCV_CUDA_PREFIX" <<'PY'
from pathlib import Path
import sys

import cv2
import numpy as np

expected_prefix = Path(sys.argv[1]).resolve()
module_path = Path(cv2.__file__).resolve()
assert cv2.__version__ == "5.0.0"
assert expected_prefix in module_path.parents
assert hasattr(cv2, "xfeatures2d")

count = cv2.cuda.getCudaEnabledDeviceCount()
print("CUDA devices:", count)
assert count > 0

image = np.zeros((96, 128, 3), dtype=np.uint8)
image[:, :, 1] = 173
expected_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

for index in range(count):
    cv2.cuda.setDevice(index)

    gpu_image = cv2.cuda_GpuMat()
    gpu_image.upload(image)
    gpu_gray = cv2.cuda.cvtColor(gpu_image, cv2.COLOR_BGR2GRAY)

    downloaded = gpu_image.download()
    gray = gpu_gray.download()

    assert np.array_equal(downloaded, image)
    assert np.array_equal(gray, expected_gray)
    print(f"GPU {index}: upload + cvtColor + download PASS")
PY

This validates more than device discovery: it allocates device memory, transfers data, executes a CUDA image-processing kernel, and copies the result back.

Stop here — Python CUDA route complete. The installation is ready for Python CUDA work when cv2 imports from the CUDA prefix, at least one device is found, and every listed GPU reports upload + cvtColor + download PASS. Continue to Section 6 only for C++ integration and to Section 7.3 when the build record must be preserved. A successful compile or nonzero device count alone is not this checkpoint.

During testing, the initial runtime failed with NVIDIA error 804 even though compilation succeeded. The loaded kernel driver was 535.288.01 while the installed user-space libraries were 535.309.01, and nvidia-smi separately reported a driver/library mismatch. After a controlled reboot loaded 535.309.01 consistently, OpenCV enumerated four GPUs and the complete smoke test passed on each one. NVIDIA defines error 803 as a system driver mismatch and error 804 as an unsupported forward-compatibility path; do not treat the two codes as interchangeable. CUDA forward-compatibility diagnostics

The lesson is simple: a successful CUDA compile proves that headers, compiler, and libraries were available. It does not prove that the loaded driver can execute the result.

6. Use OpenCV 5 from C++

OpenCV 5 installs its supported CMake package under lib/cmake/opencv5; use that package for normal C++ integration. When the deprecated OPENCV_GENERATE_PKGCONFIG=ON option is requested, OpenCV also generates opencv5.pc, not opencv4.pc.

Select the installation profile that the native commands should use. This block repeats every base path, rejects profile typos, and creates a profile-specific application build directory so a fresh shell cannot derive paths such as /cpp-smoke:

OPENCV_VERSION=5.0.0
OPENCV_WORKDIR="$HOME/src/opencv-$OPENCV_VERSION"
CUDA_TOOLKIT_ROOT=/usr/local/cuda-12.9  # match Section 5 or adjust

# Choose cpu or cuda.
OPENCV_PROFILE=cpu

case "$OPENCV_PROFILE" in
  cpu)
    OPENCV_ACTIVE_PREFIX="$HOME/.local/opencv/$OPENCV_VERSION"
    ;;
  cuda)
    OPENCV_ACTIVE_PREFIX="$HOME/.local/opencv/${OPENCV_VERSION}-cuda12.9"
    ;;
  *)
    printf 'OPENCV_PROFILE must be cpu or cuda, not %s\n' \
      "$OPENCV_PROFILE" >&2
    OPENCV_ACTIVE_PREFIX=
    ;;
esac

if [ -n "$OPENCV_ACTIVE_PREFIX" ]; then
  printf 'Selected OpenCV profile: %s\nPrefix: %s\n' \
    "$OPENCV_PROFILE" "$OPENCV_ACTIVE_PREFIX"

  OPENCV_CPP_PROJECT="$OPENCV_WORKDIR/cpp-smoke"
  OPENCV_CPP_BUILD_DIR="$OPENCV_CPP_PROJECT/build-$OPENCV_PROFILE"
  mkdir -p "$OPENCV_CPP_PROJECT"
  cd "$OPENCV_CPP_PROJECT"
else
  false
fi

Create CMakeLists.txt:

cmake_minimum_required(VERSION 3.18)
project(opencv5_smoke LANGUAGES CXX)option(OPENCV5_EXPECT_CUDA “Execute and require CUDA runtime operations” OFF)set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)set(OPENCV5_COMPONENTS core imgproc imgcodecs features xfeatures2d)
if(OPENCV5_EXPECT_CUDA)
list(APPEND OPENCV5_COMPONENTS cudaarithm cudaimgproc)
endif()find_package(OpenCV 5.0.0 EXACT REQUIRED COMPONENTS ${OPENCV5_COMPONENTS})add_executable(opencv5_smoke main.cpp)
target_link_libraries(opencv5_smoke PRIVATE ${OpenCV_LIBS})
target_include_directories(opencv5_smoke PRIVATE ${OpenCV_INCLUDE_DIRS})
target_compile_definitions(
opencv5_smoke
PRIVATE OPENCV5_EXPECT_CUDA=$
)message(STATUS “OpenCV version: ${OpenCV_VERSION}”)
message(STATUS “OpenCV config: ${OpenCV_CONFIG_PATH}”)
message(STATUS “OpenCV libraries: ${OpenCV_LIBS}”)

OpenCV 5 renamed the native features2d module to features; the header is now . Python names such as cv2.SIFT_create() remain familiar. OpenCV module restructuring

Create main.cpp:

#include 
#include 
#include 
#include 
#include 
#include 

#if OPENCV5_EXPECT_CUDA
#include 
#endif

#include 
#include 
#include 
#include 
#include 
#include 

int main() {
    if (std::string(CV_VERSION) != "5.0.0") {
        throw std::runtime_error("Expected OpenCV 5.0.0, found " +
                                 std::string(CV_VERSION));
    }

    cv::Mat image(192, 256, CV_8UC3, cv::Scalar(0, 0, 0));
    for (int row = 0; row < image.rows; row += 24) {
        for (int column = 0; column < image.cols; column += 24) {
            if (((row / 24) + (column / 24)) % 2 == 0) {
                cv::rectangle(image, cv::Rect(column, row, 24, 24),
                              cv::Scalar(0, 180, 255), cv::FILLED);
            }
        }
    }

    std::vector encoded;
    if (!cv::imencode(".png", image, encoded))
        throw std::runtime_error("PNG encoding failed");
    cv::Mat decoded = cv::imdecode(encoded, cv::IMREAD_COLOR);
    if (decoded.empty() || cv::norm(decoded, image, cv::NORM_INF) != 0.0)
        throw std::runtime_error("PNG round trip changed the image");

    cv::Mat gray;
    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    std::vector<:keypoint> keypoints;
    cv::SIFT::create()->detect(gray, keypoints);
    if (keypoints.empty())
        throw std::runtime_error("SIFT did not detect any keypoints");
    const std::size_t sift_keypoint_count = keypoints.size();

    cv::Mat brief_descriptors;
    cv::xfeatures2d::BriefDescriptorExtractor::create()->compute(
        gray, keypoints, brief_descriptors);
    if (brief_descriptors.empty())
        throw std::runtime_error("BRIEF contrib descriptor extraction failed");

    const int device_count = cv::cuda::getCudaEnabledDeviceCount();
#if OPENCV5_EXPECT_CUDA
    if (device_count <= 0)
        throw std::runtime_error("CUDA profile found no usable GPU");
    for (int index = 0; index < device_count; ++index) {
        cv::cuda::setDevice(index);
        cv::cuda::GpuMat gpu_image, gpu_gray;
        gpu_image.upload(image);
        cv::cuda::cvtColor(gpu_image, gpu_gray, cv::COLOR_BGR2GRAY);

        cv::Mat downloaded_image, downloaded_gray;
        gpu_image.download(downloaded_image);
        gpu_gray.download(downloaded_gray);
        if (cv::norm(downloaded_image, image, cv::NORM_INF) != 0.0 ||
            cv::norm(downloaded_gray, gray, cv::NORM_INF) != 0.0)
            throw std::runtime_error("CUDA round trip changed the result");
        std::cout << "GPU " << index
                  << ": upload + cvtColor + download PASS\n";
    }
#else
    const std::regex cuda_enabled(R"((^|\n)\s*NVIDIA CUDA:\s*YES)");
    if (std::regex_search(cv::getBuildInformation(), cuda_enabled))
        throw std::runtime_error("CPU profile was compiled with CUDA support");
    if (device_count != 0)
        throw std::runtime_error("CPU profile unexpectedly exposed CUDA");
#endif

    std::cout << "OpenCV " << CV_VERSION << '\n'
              << "In-memory PNG round trip: PASS\n"
              << "SIFT keypoints: " << sift_keypoint_count << '\n'
              << "BRIEF descriptors: " << brief_descriptors.rows << '\n'
              << "CUDA devices: " << device_count << '\n';
    return 0;
}

Configure against the explicit OpenCV 5 package:

(
set -o errexit
set -o nounset

test -d "$OPENCV_ACTIVE_PREFIX/lib/cmake/opencv5"

OPENCV5_EXPECT_CUDA=OFF

if [ "$OPENCV_PROFILE" = "cuda" ]; then
  test -x "$CUDA_TOOLKIT_ROOT/bin/nvcc"
  OPENCV5_EXPECT_CUDA=ON
fi

CMAKE_OPENCV_ARGS=(
  "-DOpenCV_DIR=$OPENCV_ACTIVE_PREFIX/lib/cmake/opencv5"
  "-DOPENCV5_EXPECT_CUDA=$OPENCV5_EXPECT_CUDA"
)
if [ "$OPENCV5_EXPECT_CUDA" = ON ]; then
  CMAKE_OPENCV_ARGS+=("-DCUDAToolkit_ROOT=$CUDA_TOOLKIT_ROOT")
fi

cmake -S "$OPENCV_CPP_PROJECT" -B "$OPENCV_CPP_BUILD_DIR" \
  "${CMAKE_OPENCV_ARGS[@]}"

cmake --build "$OPENCV_CPP_BUILD_DIR" --parallel 4
"$OPENCV_CPP_BUILD_DIR/opencv5_smoke"
)

The CPU profile must report zero CUDA devices. On Typhoon, the tested output was:

OpenCV 5.0.0
In-memory PNG round trip: PASS
SIFT keypoints: 496
BRIEF descriptors: 380
CUDA devices: 0

After setting OPENCV_PROFILE=cuda and rerunning the selection and CMake commands, the tested CUDA profile executed upload, cvtColor, and download on all four GPUs before reporting CUDA devices: 4. The profile-specific build directory prevents CMake from reusing the CPU cache. A first-class CUDA OpenCV package requires CMake 3.18 or newer in the consuming project and searches for the exact toolkit version used to build OpenCV—12.9.86 in this test. Set CUDAToolkit_ROOT explicitly when multiple toolkits are installed.

Confirm that the executable resolves the expected native libraries:

(
set -o errexit
set -o nounset
set -o pipefail

ldd "$OPENCV_CPP_BUILD_DIR/opencv5_smoke" |
  awk -v prefix="$OPENCV_ACTIVE_PREFIX/" '
    /libopencv/ {
      print
      seen = 1
      if ($0 ~ /not found/ || index($0, prefix) == 0)
        bad = 1
    }
    END { exit (!seen || bad) }
  '
)

The check exits unsuccessfully unless it sees at least one OpenCV library and every OpenCV library points into $OPENCV_ACTIVE_PREFIX. not found indicates a loader configuration problem; a path under /usr/lib or /usr/local indicates that the wrong OpenCV installation was selected.

For pkg-config consumers:

(
set -o nounset
PKG_CONFIG_PATH="$OPENCV_ACTIVE_PREFIX/lib/pkgconfig" \
  pkg-config --modversion opencv5
)

The verified result was 5.0.0. Treat the generated .pc file as a convenience for simple shared-library builds: OpenCV marks OPENCV_GENERATE_PKGCONFIG as deprecated, and its metadata can omit third-party dependencies, especially for static builds. CMake’s installed OpenCV package remains the authoritative native integration. OpenCV 5.0.0 pkg-config guidance

Stop here — C++ route complete. The native installation is ready when CMake finds exactly OpenCV 5.0.0, the sample completes, and ldd resolves every OpenCV library inside the selected prefix. If the application uses pkg-config, it must report 5.0.0 as well. In the CUDA profile, every visible GPU must also report upload + cvtColor + download PASS.

7. Verify the Installed Capabilities

A version string answers only one question. A useful installation record includes the source tag, build summary, installed path, imported Python path, linked C++ libraries, and runtime backend tests.

Select the same profile again. Repeating the complete path set makes this section safe to enter from a fresh shell:

OPENCV_VERSION=5.0.0
OPENCV_WORKDIR="$HOME/src/opencv-$OPENCV_VERSION"

# Choose cpu or cuda.
OPENCV_PROFILE=cpu

case "$OPENCV_PROFILE" in
  cpu)
    OPENCV_ACTIVE_PREFIX="$HOME/.local/opencv/$OPENCV_VERSION"
    OPENCV_ACTIVE_PYTHON="$HOME/.venvs/opencv-$OPENCV_VERSION/bin/python"
    ;;
  cuda)
    OPENCV_ACTIVE_PREFIX="$HOME/.local/opencv/${OPENCV_VERSION}-cuda12.9"
    OPENCV_ACTIVE_PYTHON="$HOME/.venvs/opencv-${OPENCV_VERSION}-cuda12.9/bin/python"
    ;;
  *)
    printf 'OPENCV_PROFILE must be cpu or cuda, not %s\n' \
      "$OPENCV_PROFILE" >&2
    OPENCV_ACTIVE_PREFIX=
    OPENCV_ACTIVE_PYTHON=
    ;;
esac

if [ -n "$OPENCV_ACTIVE_PREFIX" ]; then
  OPENCV_ACTIVE_BUILD="$OPENCV_WORKDIR/build-$OPENCV_PROFILE"
  OPENCV_ACTIVE_CONFIG_LOG="$OPENCV_WORKDIR/configure-$OPENCV_PROFILE.log"
  OPENCV_ACTIVE_BUILD_LOG="$OPENCV_WORKDIR/build-$OPENCV_PROFILE.log"
  OPENCV_ACTIVE_INSTALL_LOG="$OPENCV_WORKDIR/install-$OPENCV_PROFILE.log"
else
  false
fi

7.1 Python capability report

Skip this subsection for a C++-only build. Otherwise, assert the exact release, import prefix, contrib module, and profile-appropriate CUDA runtime before printing the full report:

(
set -o errexit
set -o nounset

test -x "$OPENCV_ACTIVE_PYTHON"

PYTHONOPTIMIZE= "$OPENCV_ACTIVE_PYTHON" - "$OPENCV_ACTIVE_PREFIX" "$OPENCV_PROFILE" <<'PY'
from pathlib import Path
import sys

import cv2

expected_prefix = Path(sys.argv[1]).resolve()
profile = sys.argv[2]
module_path = Path(cv2.__file__).resolve()
cuda_devices = cv2.cuda.getCudaEnabledDeviceCount()

print("Version:", cv2.__version__)
print("Module path:", cv2.__file__)
print("Contrib xfeatures2d:", hasattr(cv2, "xfeatures2d"))
print("CUDA devices:", cuda_devices)

assert cv2.__version__ == "5.0.0"
assert expected_prefix in module_path.parents
assert hasattr(cv2, "xfeatures2d")
assert cuda_devices == 0 if profile == "cpu" else cuda_devices > 0

print(cv2.getBuildInformation())
PY
)

The interpreter, import path, CMake prefix, logs, and expected runtime capabilities must all belong to the selected profile.

In getBuildInformation(), review at least:

  • Modules under To be built and Unavailable.
  • GUI backend.
  • FFmpeg, GStreamer, and V4L/V4L2.
  • Parallel framework.
  • CUDA architecture, cuBLAS, and cuDNN.
  • Python interpreter, library, NumPy, and install path.

Checkpoint — CPU source identity verified. The assertions establish the exact release, import prefix, contrib module, and zero-CUDA contract. If Section 4.4 was deliberately adapted into a headless build, this is an identity checkpoint—not an end-to-end-tested headless CPU recipe. Test every backend the application requests. For the tested desktop CPU profile, continue through Section 7.2; complete Section 6 as well when the deployment has a C++ consumer.

7.2 Test desktop video and GUI backends

This subsection applies to the strict desktop CPU profile from Section 4 and requires its Python binding. A C++-only build should skip it or implement equivalent cv::VideoWriter, cv::VideoCapture, and HighGUI checks in the native application. The test performs real FFmpeg and GStreamer reads, not only build-summary checks:

(
set -o errexit
set -o nounset

test "$OPENCV_PROFILE" = cpu

PYTHONOPTIMIZE= "$OPENCV_ACTIVE_PYTHON" - <<'PY'
from pathlib import Path
import tempfile

import cv2
import numpy as np

frame = np.zeros((48, 64, 3), dtype=np.uint8)
frame[:, :, 1] = 173

with tempfile.TemporaryDirectory() as directory:
    video_path = Path(directory) / "ffmpeg-smoke.avi"
    writer = cv2.VideoWriter(
        str(video_path),
        cv2.CAP_FFMPEG,
        cv2.VideoWriter_fourcc(*"MJPG"),
        5.0,
        (64, 48),
    )
    assert writer.isOpened()
    assert writer.getBackendName() == "FFMPEG"
    writer.write(frame)
    writer.release()

    capture = cv2.VideoCapture(str(video_path), cv2.CAP_FFMPEG)
    assert capture.isOpened()
    assert capture.getBackendName() == "FFMPEG"
    ok, decoded = capture.read()
    capture.release()
    assert ok and decoded.shape == frame.shape

pipeline = (
    "videotestsrc num-buffers=1 ! videoconvert ! "
    "video/x-raw,format=BGR,width=64,height=48 ! "
    "appsink sync=false"
)
capture = cv2.VideoCapture(pipeline, cv2.CAP_GSTREAMER)
assert capture.isOpened()
assert capture.getBackendName() == "GSTREAMER"
ok, generated = capture.read()
capture.release()
assert ok and generated.shape == frame.shape

print("FFmpeg read/write: PASS")
print("GStreamer pipeline: PASS")
PY
)

On a machine without an active desktop session, install Xvfb only for the off-screen GTK check:

sudo apt install -y xvfb

PYTHONOPTIMIZE= xvfb-run -a "$OPENCV_ACTIVE_PYTHON" - <<'PY'
import cv2
import numpy as np

image = np.zeros((48, 64, 3), dtype=np.uint8)
cv2.namedWindow("opencv5-gtk-smoke")
cv2.imshow("opencv5-gtk-smoke", image)
cv2.waitKey(1)

backend = cv2.currentUIFramework()
print("HighGUI backend:", backend)
assert backend == "GTK3"

cv2.destroyAllWindows()
PY

These checks intentionally fail when a requested backend is missing or cannot run. A headless build should skip them rather than claim desktop support. OpenCV 5.0.0 HighGUI backend API

Stop here — desktop CPU source route complete. The desktop installation is ready when the prefix and capability checks pass and every backend the application requires succeeds at runtime. For the strict profile in this guide, that means FFmpeg read/write, the GStreamer pipeline, and the GTK3 window test all pass. Preserve Section 7.3 evidence for production or long-lived deployments.

7.3 Preserve build evidence

Keep the configuration, build, install, and installation manifest with the deployment:

(
set -o errexit
set -o nounset

test -n "$OPENCV_ACTIVE_PREFIX"
test -d "$OPENCV_ACTIVE_PREFIX"

OPENCV_EVIDENCE="$OPENCV_ACTIVE_PREFIX/share/opencv5/verification"
mkdir -p "$OPENCV_EVIDENCE"

cp "$OPENCV_ACTIVE_CONFIG_LOG" "$OPENCV_EVIDENCE/"
cp "$OPENCV_ACTIVE_BUILD_LOG" "$OPENCV_EVIDENCE/"
cp "$OPENCV_ACTIVE_INSTALL_LOG" "$OPENCV_EVIDENCE/"
cp "$OPENCV_ACTIVE_BUILD/install_manifest.txt" "$OPENCV_EVIDENCE/"

{
  printf 'opencv='
  git -C "$OPENCV_WORKDIR/opencv" rev-parse HEAD
  printf 'opencv_contrib='
  git -C "$OPENCV_WORKDIR/opencv_contrib" rev-parse HEAD
} > "$OPENCV_EVIDENCE/source-commits.txt"
)

These five files preserve build outputs, the install manifest, and exact source revisions. For a complete deployment record, also save the Python capability report, C++ run, ldd and pkg-config output, and runtime backend test output in $OPENCV_EVIDENCE. This record explains why two machines with OpenCV 5.0.0 may behave differently: one may have FFmpeg, GStreamer, contrib, or CUDA while the other does not.

7.4 Run the downloadable verification bundle

The OpenCV 5 Linux companion bundle turns the identity, Python, C++, linkage, pkg-config, and runtime checks into one repeatable command. Download its SHA-256 checksum alongside the ZIP, then verify and unpack it:

verify_and_unpack_opencv5_companion() {
  sha256sum -c opencv5-linux-companion-5.0.0.sha256.txt || return
  if [ -e opencv5-linux-companion-5.0.0 ]; then
    echo "Refusing to overwrite existing opencv5-linux-companion-5.0.0" >&2
    return 2
  fi
  unzip -q opencv5-linux-companion-5.0.0.zip || return
  cd opencv5-linux-companion-5.0.0 || return
}

verify_and_unpack_opencv5_companion

The verifier installs nothing, uses no network access, and refuses to overwrite an existing evidence directory. Its source modes require the completed Python binding used by this guide. For a C++-only source installation, skip those source invocations and use the native CMake, runtime, ldd, and pkg-config checks in Sections 6–7. Choose the invocation that matches the installation:

# Lane A: wheel environment
./verify-opencv5.sh wheel \
  --python "$HOME/.venvs/opencv5-wheel/bin/python" \
  --variant opencv-python \
  --version 5.0.0 \
  --evidence "$PWD/evidence-wheel"

# Lane B: desktop CPU source profile
./verify-opencv5.sh source \
  --profile desktop-cpu \
  --prefix "$HOME/.local/opencv/5.0.0" \
  --python "$HOME/.venvs/opencv-5.0.0/bin/python" \
  --source "$HOME/src/opencv-5.0.0/opencv" \
  --contrib "$HOME/src/opencv-5.0.0/opencv_contrib" \
  --version 5.0.0 \
  --evidence "$PWD/evidence-cpu"

# Lane C: headless CUDA source profile
./verify-opencv5.sh source \
  --profile headless-cuda \
  --prefix "$HOME/.local/opencv/5.0.0-cuda12.9" \
  --python "$HOME/.venvs/opencv-5.0.0-cuda12.9/bin/python" \
  --source "$HOME/src/opencv-5.0.0/opencv" \
  --contrib "$HOME/src/opencv-5.0.0/opencv_contrib" \
  --cuda-root /usr/local/cuda-12.9 \
  --dnn-cuda on \
  --version 5.0.0 \
  --evidence "$PWD/evidence-cuda"

The bundle also includes a configure-only helper. It prints a shell-escaped CMake command by default and runs configuration only with an explicit --run; it never installs dependencies, clones OpenCV source, builds, installs, deletes, or uses sudo. OpenCV’s own CMake logic may fetch pinned third-party assets during --run, just as noted in Section 4.4. The bundle README documents the required paths and profile options.

8. Troubleshoot OpenCV 5 Installation Problems

When diagnosing, start with read-only evidence. Avoid deleting broad paths such as /usr/local or guessing which library files belong to OpenCV.

9. Upgrade, Roll Back, and Uninstall Safely

9.1 Python wheel lifecycle

Upgrade the one selected package deliberately:

upgrade_opencv_wheel() (
  set -o errexit
  set -o nounset
  set -o pipefail

  OPENCV_WHEEL_VENV="$HOME/.venvs/opencv5-wheel"
  OPENCV_WHEEL_PYTHON="$OPENCV_WHEEL_VENV/bin/python"
  OPENCV_WHEEL_PACKAGE=opencv-python  # set to the installed variant
  TESTED_OPENCV_WHEEL_VERSION=5.0.0.93

  test -f "$OPENCV_WHEEL_VENV/pyvenv.cfg"
  test -x "$OPENCV_WHEEL_PYTHON"
  OPENCV_WHEEL_VENV_REAL=$(realpath -e -- "$OPENCV_WHEEL_VENV")
  OPENCV_WHEEL_PREFIX_REAL=$(realpath -e -- \
    "$("$OPENCV_WHEEL_PYTHON" -c 'import sys; print(sys.prefix)')")
  if [ "$OPENCV_WHEEL_PREFIX_REAL" != "$OPENCV_WHEEL_VENV_REAL" ]; then
    printf 'Refusing pip mutation outside %s\n' "$OPENCV_WHEEL_VENV_REAL" >&2
    exit 2
  fi

  "$OPENCV_WHEEL_PYTHON" -m pip install --upgrade --only-binary=:all: \
    "$OPENCV_WHEEL_PACKAGE==$TESTED_OPENCV_WHEEL_VERSION"
)

upgrade_opencv_wheel

Change OPENCV_WHEEL_PACKAGE to opencv-contrib-python, opencv-python-headless, or opencv-contrib-python-headless when that is the environment’s selected variant. Do not use this command to switch variants.

To change variants, remove every possible owner of the shared cv2 namespace, then install exactly one replacement:

switch_opencv_wheel_variant() (
  set -o errexit
  set -o nounset
  set -o pipefail

  OPENCV_WHEEL_VENV="$HOME/.venvs/opencv5-wheel"
  OPENCV_WHEEL_PYTHON="$OPENCV_WHEEL_VENV/bin/python"
  TESTED_OPENCV_WHEEL_VERSION=5.0.0.93

  test -f "$OPENCV_WHEEL_VENV/pyvenv.cfg"
  test -x "$OPENCV_WHEEL_PYTHON"
  OPENCV_WHEEL_VENV_REAL=$(realpath -e -- "$OPENCV_WHEEL_VENV")
  OPENCV_WHEEL_PREFIX_REAL=$(realpath -e -- \
    "$("$OPENCV_WHEEL_PYTHON" -c 'import sys; print(sys.prefix)')")
  if [ "$OPENCV_WHEEL_PREFIX_REAL" != "$OPENCV_WHEEL_VENV_REAL" ]; then
    printf 'Refusing pip mutation outside %s\n' "$OPENCV_WHEEL_VENV_REAL" >&2
    exit 2
  fi

  "$OPENCV_WHEEL_PYTHON" -m pip uninstall -y \
    opencv-python \
    opencv-contrib-python \
    opencv-python-headless \
    opencv-contrib-python-headless

  "$OPENCV_WHEEL_PYTHON" -m pip install --only-binary=:all: \
    "opencv-contrib-python-headless==$TESTED_OPENCV_WHEEL_VERSION"

  "$OPENCV_WHEEL_PYTHON" -m pip list | grep -E \
    '^opencv-(contrib-)?python(-headless)?[[:space:]]'
)

switch_opencv_wheel_variant

The final command should print exactly one OpenCV wheel package. Re-run cv2.__version__, cv2.__file__, and the capability tests from Section 3.3 afterward.

9.2 Native source-build lifecycle

Install each release into its own prefix:

$HOME/.local/opencv/5.0.0
$HOME/.local/opencv/5.1.0

Build and validate the new prefix before changing OpenCV_DIR, PKG_CONFIG_PATH, Python .pth files, containers, or service definitions. Rollback then becomes a configuration change rather than a repair operation.

Keep install_manifest.txt. Before retiring a source build, remove the .pth file that exposes it to its virtual environment:

remove_opencv_selector() (
  set -o errexit
  set -o nounset

  OPENCV_VERSION=5.0.0
  OPENCV_PROFILE=cpu  # change to cuda for the CUDA environment

  case "$OPENCV_PROFILE" in
    cpu)
      OPENCV_RETIRE_VENV="$HOME/.venvs/opencv-$OPENCV_VERSION"
      OPENCV_PTH_BASENAME=opencv5-prefix.pth
      ;;
    cuda)
      OPENCV_RETIRE_VENV="$HOME/.venvs/opencv-${OPENCV_VERSION}-cuda12.9"
      OPENCV_PTH_BASENAME=opencv5-cuda-prefix.pth
      ;;
    *)
      printf 'OPENCV_PROFILE must be cpu or cuda, not %s\n' \
        "$OPENCV_PROFILE" >&2
      exit 2
      ;;
  esac

  OPENCV_RETIRE_PYTHON="$OPENCV_RETIRE_VENV/bin/python"
  if [ ! -x "$OPENCV_RETIRE_PYTHON" ]; then
    printf 'Refusing removal: missing interpreter %s\n' \
      "$OPENCV_RETIRE_PYTHON" >&2
    exit 3
  fi

  OPENCV_RETIRE_VENV_REAL=$(realpath -e -- "$OPENCV_RETIRE_VENV")
  test -f "$OPENCV_RETIRE_VENV_REAL/pyvenv.cfg"
  OPENCV_RETIRE_SITE=$("$OPENCV_RETIRE_PYTHON" -c \
    'import site; print(site.getsitepackages()[0])')
  OPENCV_RETIRE_SITE_REAL=$(realpath -e -- "$OPENCV_RETIRE_SITE")

  OPENCV_RETIRE_PARENT_REAL=$(realpath -e -- "$HOME/.venvs")
  case "$OPENCV_RETIRE_VENV_REAL/" in
    "$OPENCV_RETIRE_PARENT_REAL"/*) ;;
    *)
      printf 'Refusing removal: venv resolves outside %s: %s\n' \
        "$OPENCV_RETIRE_PARENT_REAL" "$OPENCV_RETIRE_VENV_REAL" >&2
      exit 4
      ;;
  esac

  case "$OPENCV_RETIRE_SITE_REAL/" in
    "$OPENCV_RETIRE_VENV_REAL"/*) ;;
    *)
      printf 'Refusing removal: site-packages is outside the venv: %s\n' \
        "$OPENCV_RETIRE_SITE_REAL" >&2
      exit 5
      ;;
  esac

  OPENCV_PTH_FILE="$OPENCV_RETIRE_SITE_REAL/$OPENCV_PTH_BASENAME"
  printf 'Removing OpenCV path file: %s\n' "$OPENCV_PTH_FILE"

  if [ -e "$OPENCV_PTH_FILE" ] || [ -L "$OPENCV_PTH_FILE" ]; then
    rm -- "$OPENCV_PTH_FILE"
  else
    printf 'Selector was already absent.\n'
  fi
)

remove_opencv_selector

Verify that no consumer still selects the old prefix. If the dedicated prefix and virtual environment contain only that one OpenCV build, they can then be retired as units according to the system’s normal removal policy. Never remove a broad shared directory such as /usr/local, and never mix OpenCV 4 and 5 native libraries in one process. C++ applications and native Python extensions must be rebuilt for the OpenCV 5 ABI. OpenCV 4-to-5 migration guide

10. OpenCV 5 Linux Installation FAQ

What is the fastest way to install OpenCV 5 on Linux?

For Python, create a virtual environment and install one pinned official wheel. It is the fastest verified CPU-only path.

Does apt install python3-opencv install OpenCV 5?

Not necessarily. On the tested Ubuntu 24.04.4 repository, it selected OpenCV 4.6.0. Always inspect the candidate version first.

Which Python wheel should I use?

Choose contrib only when required symbols live there. Choose a non-headless wheel only when the process opens windows through OpenCV HighGUI; an application that renders through PyQt, PySide, GTK, a browser, or another GUI toolkit should normally choose headless OpenCV. Install exactly one of the four wheel variants.

Can I install opencv-python and opencv-contrib-python together?

No. They both own the cv2 namespace and are alternative builds, not layers or plugins.

Do OpenCV Python wheels include CUDA?

The official wheels are CPU-only builds. A visible cv2.cuda namespace does not prove CUDA support; use a custom source build and execute a GPU operation.

Can OpenCV 4 and OpenCV 5 coexist on one Linux machine?

Yes, when they use separate Python environments and native prefixes. Do not load both native ABIs into the same process.

Why is Ubuntu 24.04 used instead of Ubuntu 26.04?

Ubuntu 24.04 remains supported through May 2029 and is explicitly listed in the CUDA 12.9 host matrix, making it a strong full-stack CUDA baseline. Ubuntu 26.04 is the newer LTS and is a useful secondary CPU test, but it should not replace a CUDA-qualified primary environment until the chosen NVIDIA toolkit supports the exact host stack. Ubuntu release cycle · CUDA 12.9 Linux installation guide

How do I know whether FFmpeg, GStreamer, contrib, or CUDA is enabled?

Use cv2.getBuildInformation() or cv::getBuildInformation(), inspect the module summary, then run a representative runtime test. Do not infer capabilities from the release number.

11. Conclusion

There is no single OpenCV 5 installation command that is correct for every Linux project.

The most important verification habit is to separate identity from capability. 5.0.0 confirms the OpenCV release. The import path, CMake package, linked libraries, build information, and runtime smoke tests confirm which OpenCV 5 installation the application will actually use. The platform router makes this guidance Linux-wide without pretending that Ubuntu dependencies, glibc wheels, or desktop NVIDIA instructions are universal.

From idea to working model to real-time deployment

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

12. References

Similar Posts

Leave a Reply