Jetson OpenCV 4.14 CUDA Python Wheel 编译指南

Jetson OpenCV 4.14 CUDA Python Wheel 编译指南

本文记录在 NVIDIA Jetson Orin Nano / ARM64 / Ubuntu 24.04 环境下,从 OpenCV 源码编译带 CUDA、cuDNN、contrib、FFmpeg/GStreamer 支持的 Python wheel。

当前验证环境:

Jetson Orin Nano
Jetpack 7.2.1
Ubuntu 24.04
Python 3.12
CUDA 13.2
cuDNN 9.20
OpenCV 4.14.0
OpenCV-contrib 4.14.0
Python architecture: aarch64
CUDA architecture: 8.7

最终生成:

opencv_contrib_python-4.14.0+<revision>-cp312-cp312-linux_aarch64.whl

1. 原理和最终结果

最终的 Python 环境结构:

Conda / venv
│
├── Python 3.12
├── NumPy 2.x
│
└── cv2
     │
     ├── OpenCV 4.14
     ├── OpenCV contrib
     ├── CUDA
     ├── cuDNN
     ├── NPP
     ├── cuBLAS
     ├── cuFFT
     ├── FFmpeg
     ├── GStreamer
     └── 其他 OpenCV 模块

OpenCV 本身使用:

BUILD_SHARED_LIBS=OFF

因此 OpenCV 的核心代码会静态链接进:

cv2.abi3.so

但 CUDA、cuDNN、FFmpeg、GStreamer 等系统库仍然是动态依赖。

所以这个 wheel 是:

Python 包 + Jetson 系统动态库依赖

而不是一个完全自包含的 OpenCV。


2. 目录结构

推荐使用:

~/dev/opencv-wheel/opencv-python/

最终目录类似:

opencv-python/
├── opencv/
├── opencv_contrib/
├── opencv_extra/
├── cv2/
├── setup.py
├── pyproject.toml
├── find_version.py
├── patches/
├── scripts/
└── ...

其中:

opencv/

是 OpenCV 源码。

opencv_contrib/

是 OpenCV contrib 源码。

二者版本必须匹配。

例如:

OpenCV          4.14.0
OpenCV-contrib  4.14.0

3. 获取源码

准备一个版本,例如 OpenCV 4.14(已验证):

mkdir ~/dev

mkdir -p ~/dev/opencv-wheel
cd ~/dev/opencv-wheel

git clone --recursive https://github.com/opencv/opencv-python.git
cd opencv-python

git submodule status

会看到类似:

-xxxxxxxx opencv
-xxxxxxxx opencv_contrib

把4.14.0 源码接进去

cd ~/dev/opencv-wheel/opencv-python

rm -rf opencv opencv_contrib

git clone --branch 4.14.0 --depth 1 \
    https://github.com/opencv/opencv.git opencv

git clone --branch 4.14.0 --depth 1 \
    https://github.com/opencv/opencv_contrib.git opencv_contrib

如果源码里面存在:

opencv/build/

删除:

rm -rf ~/dev/opencv-wheel/opencv-python/opencv/build

原因是 CMake cache 可能保存旧版本、旧路径和旧编译器配置。


4. 准备 Python 环境

说明:本文使用 venv 作为 wheel 的构建环境;生成的 wheel 与具体虚拟环境无关,之后可以安装到 Conda 或 venv 环境中。本文第 14 节使用 Conda 进行独立安装验证。

sudo apt install -y python3.12-venv
python3 -m venv ~/venvs/opencv-wheel
source ~/venvs/opencv-wheel/bin/activate

安装 NumPy:

python -m pip install -U pip
python -m pip install numpy

确认:

python -c "import numpy; print(numpy.__version__)"

推荐 NumPy 2.x。


5. 安装编译依赖

根据当前 Jetson Ubuntu 系统安装基础工具:

sudo apt update

sudo apt install -y \
    build-essential \
    cmake \
    ninja-build \
    pkg-config \
    git \
    python3-dev \
    python3-pip \
    libgtk-3-dev \
    libavcodec-dev \
    libavformat-dev \
    libavutil-dev \
    libswscale-dev \
    libgstreamer1.0-dev \
    libgstreamer-plugins-base1.0-dev \
    libv4l-dev

Jetson 上优先使用 NVIDIA JetPack 提供的系统 CUDA。

检查:

nvcc --version

例如:

CUDA 13.2

检查 CUDA:

ls -ld /usr/local/cuda*

6. 使用 opencv-python packaging repository

进入:

cd ~/dev/opencv-wheel/opencv-python

确认:

ls

应该能看到:

opencv
opencv_contrib
setup.py
pyproject.toml
cv2

7. 关键环境变量

7.1 启用 contrib

export ENABLE_CONTRIB=1

setup.py 会自动加入:

-DOPENCV_EXTRA_MODULES_PATH=<opencv-python>/opencv_contrib/modules

因此:

不要自己再次添加 OPENCV_EXTRA_MODULES_PATH。


8. 防止 setup.py 修改你的源码

因为源码是手工放进去的,并不是 packaging repository 自己管理的 OpenCV submodule,所以设置:

export OPENCV_PYTHON_SKIP_GIT_COMMANDS=1

非常重要。

否则 setup.py 可能尝试:

git submodule sync
git submodule update

从而影响你自己准备的源码。


9. CMake 参数

推荐配置:

export CMAKE_ARGS="\
-DWITH_CUDA=ON \
-DWITH_CUBLAS=ON \
-DENABLE_FAST_MATH=ON \
-DCUDA_FAST_MATH=ON \
-DCUDA_ARCH_BIN=8.7 \
-DWITH_FFMPEG=ON \
-DWITH_GSTREAMER=ON \
-DWITH_V4L=ON \
-DWITH_OPENGL=ON \
-DWITH_QT=OFF \
-DOPENCV_ENABLE_NONFREE=ON \
-DBUILD_EXAMPLES=OFF \
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_CXX_STANDARD_REQUIRED=ON"

参数说明

CUDA

-DWITH_CUDA=ON

启用 OpenCV CUDA。

-DWITH_CUBLAS=ON

启用 cuBLAS。

-DENABLE_FAST_MATH=ON
-DCUDA_FAST_MATH=ON

启用 CUDA fast math。


CUDA 架构

Orin Nano:

SM 8.7

因此:

-DCUDA_ARCH_BIN=8.7

如果以后换 Jetson 型号,需要根据 GPU Compute Capability 修改。


10. 不建议把 CUDA Toolkit 放进 wheel

不要为了“让 wheel 自包含”而把:

/usr/local/cuda-13.2/

整个打包进去。

Jetson 上应该使用系统 NVIDIA CUDA:

/usr/local/cuda-13.2/lib64

以及 JetPack 提供的:

cuDNN
NPP
CUDA driver
TensorRT

这样 wheel 才适合在同一 JetPack 软件栈的多个 Python 环境中复用。


11. 清理旧构建

每次换 OpenCV 版本、换 JetPack、换关键编译参数,都建议清理:

cd ~/dev/opencv-wheel/opencv-python

rm -rf _skbuild
rm -rf build
rm -rf dist

如果源码目录里面存在:

opencv/build

也删除:

rm -rf opencv/build

12. 开始构建 wheel

执行:

cd ~/dev/opencv-wheel/opencv-python

export ENABLE_CONTRIB=1

export OPENCV_PYTHON_SKIP_GIT_COMMANDS=1

export CMAKE_ARGS="\
-DWITH_CUDA=ON \
-DWITH_CUBLAS=ON \
-DENABLE_FAST_MATH=ON \
-DCUDA_FAST_MATH=ON \
-DCUDA_ARCH_BIN=8.7 \
-DWITH_FFMPEG=ON \
-DWITH_GSTREAMER=ON \
-DWITH_V4L=ON \
-DWITH_OPENGL=ON \
-DWITH_QT=OFF \
-DOPENCV_ENABLE_NONFREE=ON \
-DBUILD_EXAMPLES=OFF \
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_CXX_STANDARD_REQUIRED=ON"

rm -rf _skbuild build dist

python -m pip wheel . --verbose

成功后:

ls -lh *.whl

例如:

opencv_contrib_python-4.14.0+031876b-cp312-cp312-linux_aarch64.whl

13. wheel 文件名怎么理解

例如:

opencv_contrib_python-4.14.0+031876b-cp312-cp312-linux_aarch64.whl

拆解:

opencv_contrib_python
        │
        └── OpenCV Python contrib package

4.14.0+031876b
        │
        └── OpenCV 版本 + 源码 revision

cp312
        │
        └── Python 3.12

cp312
        │
        └── CPython ABI

linux_aarch64
        │
        └── ARM64 Linux

因此它主要面向:

Python 3.12
Linux ARM64

14. 安装 wheel

例如conda:

conda create -n cv-test python=3.12
conda activate cv-test

安装 NumPy:

python -m pip install -U pip
python -m pip install numpy

安装自己的 wheel:

pip install ~/dev/opencv-wheel/opencv-python/opencv_contrib_python-4.14.0+031876b-cp312-cp312-linux_aarch64.whl

15. 基础验证

一定要在 OpenCV 源码目录之外测试。

例如:

cd /tmp

然后:

python - <<'PY'
import sys
import cv2
import numpy as np

print("Python   :", sys.version)
print("OpenCV   :", cv2.__version__)
print("NumPy    :", np.__version__)
print("cv2 path :", cv2.__file__)
print("CUDA dev :", cv2.cuda.getCudaEnabledDeviceCount())
print("CUDA build info contains 13.2:", "13.2" in cv2.getBuildInformation())
PY

预期:

OpenCV   : 4.14.0
NumPy    : 2.x
CUDA dev : 1
CUDA build info contains 13.2: True

16. 检查 OpenCV 编译配置

执行:

python - <<'PY'
import cv2
print(cv2.getBuildInformation())
PY

重点确认:

NVIDIA CUDA: YES
cuDNN: YES
NVIDIA GPU arch: 87
FFMPEG: YES
GStreamer: YES
Python 3: YES

17. 检查动态库依赖

找到 cv2:

python - <<'PY'
import cv2
print(cv2.__file__)
PY

例如:

/home/jetson/miniforge3/envs/cv/lib/python3.12/site-packages/cv2/cv2.abi3.so

然后:

ldd /home/jetson/miniforge3/envs/cv/lib/python3.12/site-packages/cv2/cv2.abi3.so

检查是否存在:

not found

可以直接:

ldd "$(python -c 'import cv2; print(cv2.__file__)')" | grep "not found"

没有任何输出才是正常的。


18. 检查 CUDA 实际运行

仅仅:

cv2.cuda.getCudaEnabledDeviceCount()

只能证明 OpenCV 找到了 CUDA。

还应该进行实际 CUDA 运算。

测试:

cd /tmp

python - <<'PY'
import cv2
import numpy as np
import time

print("OpenCV:", cv2.__version__)
print("NumPy :", np.__version__)
print("CUDA devices:", cv2.cuda.getCudaEnabledDeviceCount())

img = np.random.randint(
    0, 256, (1080, 1920, 3), dtype=np.uint8
)

gpu = cv2.cuda_GpuMat()
gpu.upload(img)

gauss = cv2.cuda.createGaussianFilter(
    cv2.CV_8UC3,
    cv2.CV_8UC3,
    (15, 15),
    0
)

for _ in range(10):
    result = gauss.apply(gpu)

cv2.cuda.Stream_Null().waitForCompletion()

t0 = time.perf_counter()

for _ in range(100):
    result = gauss.apply(gpu)

cv2.cuda.Stream_Null().waitForCompletion()

t1 = time.perf_counter()

print(f"100 CUDA Gaussian filters: {(t1-t0)*1000:.2f} ms")
print(f"Average: {(t1-t0)*10:.3f} ms/frame")

out = result.download()

print("Output shape:", out.shape)
print("Output dtype:", out.dtype)
print("CUDA test: OK")
PY

当前 Jetson Orin Nano 的实际测试结果:

CUDA devices: 1
100 CUDA Gaussian filters: 456.14 ms
Average: 4.561 ms/frame
Output shape: (1080, 1920, 3)
Output dtype: uint8
CUDA test: OK

19. auditwheel 检查

安装:

pip install auditwheel

然后:

auditwheel show \
~/dev/opencv-wheel/opencv-python/opencv_contrib_python-4.14.0+031876b-cp312-cp312-linux_aarch64.whl

实际标签取决于本机 glibc、系统库和 wheel 元数据,可能显示较高版本的 Linux ARM64 平台要求;请以 auditwheel show 的实际输出为准。

这并不意味着 wheel 有问题。

这是一个:

Jetson / JetPack 特定 wheel

而不是一个准备上传 PyPI、兼容所有 ARM64 Linux 的通用 wheel。

不要为了得到 manylinux wheel 而强行 auditwheel repair。

CUDA、cuDNN、NPP、GStreamer、FFmpeg 等 Jetson 系统依赖本来就不应该被随意塞进 wheel。


20. RPATH / RUNPATH 检查

检查:

readelf -d \
/home/jetson/miniforge3/envs/cv/lib/python3.12/site-packages/cv2/cv2.abi3.so \
| grep -E 'RPATH|RUNPATH'

当前构建可能看到类似:

/home/jetson/dev/opencv-wheel/opencv-python/_skbuild/linux-aarch64-3.12/cmake-install/lib:
/usr/local/cuda-13.2/lib64

其中:

/usr/local/cuda-13.2/lib64

是合理的 Jetson CUDA 路径。

而:

~/dev/opencv-wheel/opencv-python/_skbuild/...

是构建机路径。

如果:

ldd "$(python -c 'import cv2; print(cv2.__file__)')"

能够正常解析所有依赖,那么这个路径通常不是运行所必需的。

后续如果要制作更干净的 wheel,可以进一步优化 RUNPATH,但不要在第一次成功构建时急着修改。


21. OpenCV GUI检查

OpenCV 最终应该是 GTK HighGUI。cv2.imshow() 需要图形桌面和有效的 DISPLAY;在 SSH 或无显示器环境中失败,不一定表示 CUDA 构建失败。

检查 GUI:

cd /tmp

python - <<'PY'
import cv2

info = cv2.getBuildInformation()
start = info.find("GUI:")
print(info[start:start+500])
PY

确认存在:

GTK+: YES

然后:

python - <<'PY'
import cv2
import numpy as np

img = np.zeros((480, 800, 3), dtype=np.uint8)

cv2.putText(
    img,
    "OpenCV 4.14 + CUDA",
    (100, 220),
    cv2.FONT_HERSHEY_SIMPLEX,
    1.5,
    (0, 255, 0),
    3,
    cv2.LINE_AA,
)

cv2.imshow("OpenCV Test", img)

print("Press any key in the window to exit.")
cv2.waitKey(0)
cv2.destroyAllWindows()
PY

22. 如果JetPack 后续升级

例如:

JetPack 7.2
        ↓
JetPack 7.2.2

或者:

JetPack 7.2
        ↓
JetPack 7.3

JetPack 升级后可以先在新系统中测试已有 wheel;如果出现动态库 ABI 不兼容、CUDA/cuDNN 版本不匹配或 Python 版本变化,再重新编译。

23. Conda 使用注意事项

推荐:

conda create -n cv python=3.12
conda activate cv

python -m pip install -U pip
python -m pip install numpy
pip install opencv_contrib_python-....whl

不要同时:

conda install opencv

因为这样可能引入另外一套 OpenCV。

也不建议为了这个 wheel:

conda install cudatoolkit

Jetson 上 CUDA 应该优先使用 JetPack 系统提供的 CUDA。

最终:

Conda
 ├── Python
 ├── NumPy
 └── OpenCV wheel
        │
        └── JetPack 系统
              ├── CUDA
              ├── cuDNN
              ├── NPP
              ├── cuBLAS
              ├── cuFFT
              ├── NVIDIA Driver
              ├── GStreamer
              └── FFmpeg

24. 命令汇总

如果源码已经准备好,实际上核心命令就是:

cd ~/dev/opencv-wheel/opencv-python

sudo apt install -y python3.12-venv
python3 -m venv ~/venvs/opencv-wheel
source ~/venvs/opencv-wheel/bin/activate

python -m pip install -U pip
python -m pip install -U pip wheel scikit-build setuptools

export ENABLE_CONTRIB=1
export OPENCV_PYTHON_SKIP_GIT_COMMANDS=1

export CMAKE_ARGS="\
-DWITH_CUDA=ON \
-DWITH_CUBLAS=ON \
-DENABLE_FAST_MATH=ON \
-DCUDA_FAST_MATH=ON \
-DCUDA_ARCH_BIN=8.7 \
-DWITH_FFMPEG=ON \
-DWITH_GSTREAMER=ON \
-DWITH_V4L=ON \
-DWITH_OPENGL=ON \
-DWITH_QT=OFF \
-DOPENCV_ENABLE_NONFREE=ON \
-DBUILD_EXAMPLES=OFF \
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_CXX_STANDARD_REQUIRED=ON"

rm -rf _skbuild build dist
rm -rf opencv/build

python -m pip wheel . --verbose

然后:

ls -lh *.whl

安装:

pip install \
~/dev/opencv-wheel/opencv-python/opencv_contrib_python-4.14.0+031876b-cp312-cp312-linux_aarch64.whl

验证:

cd /tmp

python - <<'PY'
import cv2
import numpy as np

print("OpenCV:", cv2.__version__)
print("NumPy :", np.__version__)
print("CUDA  :", cv2.cuda.getCudaEnabledDeviceCount())
print("CUDA build info contains 13.2:", "13.2" in cv2.getBuildInformation())
PY

最后:

ldd $(python -c 'import cv2; print(cv2.__file__)') \
| grep "not found"

以及 CUDA 实际运算测试(GUI测试可选)。


posted @ 2026-10-08 11:55  icebooy  阅读(13)  评论(0)    收藏  举报