1. 为什么要在 Jetson Nano 上源码编译 OpenCV
1.1 直接装 pip 包为什么不够用
很多刚拿到 Jetson Nano 的开发者第一反应是pip install opencv-python,几秒钟装完,import cv2也能跑通,看起来一切正常。但只要你的项目稍微往深走一步,问题就来了:想调用 CUDA 加速的cv2.cuda模块,报错;想用 SIFT、SURF 这类特征点算法,报错说专利模块没编进去;想跑一些光流、追踪、人脸识别的扩展算法,发现cv2.ximgproc、cv2.face这些命名空间根本不存在。
原因很简单:PyPI 上那个opencv-python是官方为了通用 x86 平台预编译好的 wheel 包,它默认不带 CUDA,也不带 contrib 扩展模块。而 Jetson Nano 这块板子最大的价值恰恰在于它那颗 128 核 Maxwell GPU 和完整的 CUDA 支持。你花几百块买它,不就是为了让图像处理跑在 GPU 上吗?如果只用 CPU 版本的 OpenCV,那和在一块普通树莓派上跑没什么本质区别,甚至因为 Nano 的 CPU 主频不高(四核 A57 @1.43GHz),纯 CPU 处理反而更慢。
所以结论很明确:要在 Jetson Nano 上发挥 OpenCV 的真正实力,必须源码编译,并且要带上 CUDA 和 contrib 模块。这不是折腾,这是刚需。
1.2 版本选择:为什么锁定 4.1.1
OpenCV 版本迭代很快,4.x 系列从 4.0 一路走到 4.10+,为什么偏偏选 4.1.1?这里有几个现实考量。
第一,Jetson Nano 官方镜像(不管是早期的 JetPack 4.2/4.3 还是后来的 4.4/4.5/4.6)自带的 CUDA 版本是 10.2,cuDNN 是 8.x。OpenCV 4.1.1 这个版本对 CUDA 10.2 的兼容性经过了大量社区验证,编译时踩的坑最少。更新的 OpenCV 版本(比如 4.5+)对 CMake 版本、GCC 版本要求更高,在 Nano 的 ARM 环境下容易出现各种链接错误。
第二,4.1.1 和 contrib 4.1.1 是配套发布的,版本号必须严格一致。OpenCV 主仓库和 contrib 仓库如果版本对不上,编译时会出现头文件找不到、符号未定义等一堆问题。很多人图省事,主仓库拉 4.1.1,contrib 拉 master,结果编译到一半就崩了。
第三,4.1.1 是很多经典教程和项目的基准版本,社区里针对这个版本的编译问题、解决方案最丰富。你遇到问题时,搜到的答案大概率能直接套用。
注意:如果你用的是 JetPack 5.x(CUDA 11.x),那 4.1.1 可能不是最优选择,建议考虑 4.5.x 系列。本文所有内容基于 JetPack 4.x + CUDA 10.2 的环境。
1.3 编译前必须搞清楚的三件事
在动手之前,有三个概念必须先理清楚,否则后面会一头雾水。
第一,主仓库和 contrib 的关系。OpenCV 主仓库(opencv)包含核心模块,比如core、imgproc、highgui、videoio、cuda等。而 contrib 仓库(opencv_contrib)包含的是"额外"模块,比如face(人脸识别)、ximgproc(扩展图像处理)、tracking(目标追踪)、aruco(AR 标记)、text(文字检测)等。这些模块因为专利、稳定性或维护状态等原因,没有被放进主仓库。编译时要把 contrib 的路径传给主仓库的 CMake,让它一起编进去。
第二,CUDA 架构号。Jetson Nano 的 GPU 是 Maxwell 架构,计算能力(Compute Capability)是 5.3。编译时必须指定CUDA_ARCH_BIN=5.3,否则编译出来的 CUDA 代码可能无法在 Nano 上运行,或者性能大打折扣。这个数字写错是新手最常见的错误之一。
第三,swap 空间。Nano 只有 4GB 内存(实际可用约 3.8GB),而编译 OpenCV 是个内存消耗大户,尤其是链接阶段,很容易因为内存不足被系统 kill 掉。所以编译前必须扩大 swap 分区,通常建议加到 4GB 以上。这一步不做,编译到 90% 崩掉,前功尽弃。
2. 编译环境准备与依赖安装
2.1 系统更新与基础工具链
拿到一块刚刷好系统的 Nano,第一件事不是急着编译,而是把基础环境理顺。先更新软件源和已安装的包:
sudo apt-get update sudo apt-get upgrade -y这一步在 Nano 上可能要跑十几分钟,因为 ARM 平台的包下载和安装速度都不快。耐心等它跑完。
接着安装编译必需的工具链:
sudo apt-get install -y build-essential cmake git pkg-config sudo apt-get install -y cmake-qt-guibuild-essential提供了 gcc、g++、make 等核心工具。cmake是 OpenCV 的构建系统,版本要求至少 3.5 以上,JetPack 4.x 自带的 cmake 通常是 3.10 左右,够用。pkg-config用于管理库的编译和链接参数。cmake-qt-gui是可选的图形化 CMake 工具,如果你习惯命令行可以跳过,但装上它在排查配置问题时有时候挺方便。
2.2 图像与视频编解码依赖
OpenCV 要能读写各种格式的图片和视频,需要一堆编解码库的支持。这些依赖不装,编译出来的 OpenCV 可能连 JPEG 都读不了:
sudo apt-get install -y libjpeg-dev libpng-dev libtiff-dev sudo apt-get install -y libavcodec-dev libavformat-dev libswscale-dev libv4l-dev sudo apt-get install -y libxvidcore-dev libx264-dev sudo apt-get install -y libgtk2.0-dev libgtk-3-dev sudo apt-get install -y libcanberra-gtk-module libcanberra-gtk3-module这里解释几个关键项。libjpeg-dev、libpng-dev、libtiff-dev分别对应 JPEG、PNG、TIFF 格式支持。libavcodec-dev这一组是 FFmpeg 的库,负责视频编解码,没有它们cv2.VideoCapture读视频文件会失败。libv4l-dev是 Video4Linux 的开发库,关系到 USB 摄像头的采集。libgtk系列是cv2.imshow显示窗口的依赖,如果你只做无头处理(headless)可以不装,但调试阶段有个窗口看结果会方便很多。
2.3 Python 环境与科学计算库
Jetson Nano 的 JetPack 自带 Python 3.6,我们就在这个基础上操作。安装 numpy 和其他科学计算依赖:
sudo apt-get install -y python3-dev python3-numpy python3-pip sudo apt-get install -y python3-matplotlib python3-scipy sudo apt-get install -y libatlas-base-dev gfortranpython3-dev提供 Python 头文件,编译 Python 绑定时必需。python3-numpy是 OpenCV Python 接口的硬依赖,没有它import cv2会直接报错。libatlas-base-dev和gfortran是线性代数优化的依赖,OpenCV 内部很多矩阵运算会用到。
实操心得:如果你打算同时支持 Python 2 和 Python 3,还需要装
python-dev和python-numpy。但 2024 年了,没必要再管 Python 2,直接只编 Python 3 的绑定,能省不少编译时间。
2.4 扩大 swap 分区(关键步骤)
这一步是很多教程会一笔带过、但实际最要命的地方。Nano 的 4GB 内存编译 OpenCV 4.1.1 基本不够用,尤其是链接libopencv_cuda相关库的时候,内存峰值能到 5GB 以上。不扩 swap,编译到 80%-90% 被 OOM Killer 干掉是常态。
先检查当前 swap:
free -h你会看到 swap 那一行通常是 0 或者很小。接下来创建一个 4GB 的 swap 文件:
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile然后把它设为开机自动挂载,编辑/etc/fstab:
sudo cp /etc/fstab /etc/fstab.bak echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab再确认一下:
free -h现在 swap 那一行应该显示 4.0G 了。
注意:
fallocate在某些文件系统上可能不生效,如果创建出来的 swapfile 大小不对,改用sudo dd if=/dev/zero of=/swapfile bs=1M count=4096来创建,速度慢一些但更可靠。
2.5 下载源码:版本必须严格对齐
找一个空间充足的目录,比如/home/你的用户名/opencv_build,然后克隆两个仓库:
mkdir -p ~/opencv_build && cd ~/opencv_build git clone --branch 4.1.1 https://github.com/opencv/opencv.git git clone --branch 4.1.1 https://github.com/opencv/opencv_contrib.git这里--branch 4.1.1是关键,两个仓库都必须锁定 4.1.1 这个 tag。克隆完成后可以进去确认一下:
cd opencv && git describe --tags cd ../opencv_contrib && git describe --tags两个都应该输出4.1.1。如果哪个不对,删掉重新克隆。
踩过的坑:GitHub 在国内访问可能不稳定,克隆大仓库容易断。如果断了,可以试试
git clone --depth 1 --branch 4.1.1只拉最新一次提交,能大幅减少下载量。但注意--depth 1拉下来的仓库没有完整历史,某些依赖 git 历史的构建步骤可能出问题,如果编译报奇怪的错,换回完整克隆。
3. CMake 配置:参数逐个拆解
3.1 创建构建目录与基础命令
OpenCV 官方推荐 out-of-source 构建,也就是在源码目录之外单独建一个 build 目录,这样不会污染源码树,出问题了好清理:
cd ~/opencv_build/opencv mkdir build && cd build然后是核心的 cmake 命令。这条命令很长,我把它拆成几段来解释,先给出完整版:
cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D INSTALL_PYTHON_EXAMPLES=ON \ -D INSTALL_C_EXAMPLES=OFF \ -D OPENCV_ENABLE_NONFREE=ON \ -D OPENCV_EXTRA_MODULES_PATH=~/opencv_build/opencv_contrib/modules \ -D PYTHON3_EXECUTABLE=/usr/bin/python3 \ -D PYTHON3_INCLUDE_DIR=/usr/include/python3.6m \ -D PYTHON3_PACKAGES_PATH=/usr/lib/python3/dist-packages \ -D PYTHON3_NUMPY_INCLUDE_DIRS=/usr/lib/python3/dist-packages/numpy/core/include \ -D BUILD_EXAMPLES=ON \ -D WITH_CUDA=ON \ -D WITH_CUDNN=ON \ -D OPENCV_DNN_CUDA=ON \ -D CUDA_ARCH_BIN=5.3 \ -D CUDA_ARCH_PTX= \ -D WITH_CUBLAS=ON \ -D WITH_LIBV4L=ON \ -D WITH_GSTREAMER=ON \ -D WITH_FFMPEG=ON \ -D BUILD_opencv_python3=ON \ -D BUILD_opencv_python2=OFF \ -D ENABLE_NEON=ON \ -D ENABLE_VFPV3=ON \ -D BUILD_TESTS=OFF \ -D BUILD_PERF_TESTS=OFF \ -D BUILD_DOCS=OFF \ ..3.2 关键参数背后的逻辑
CMAKE_BUILD_TYPE=RELEASE:编译类型选 Release,会开启 -O3 优化,编译出来的库运行速度快很多。Debug 版本虽然方便调试,但体积大、速度慢,生产环境没必要。
CMAKE_INSTALL_PREFIX=/usr/local:安装路径。装到/usr/local下,系统默认的库搜索路径能找到,Python 也能直接 import。如果你不想污染系统目录,可以改成/usr/local/opencv-4.1.1之类的自定义路径,但那样每次用都要手动设LD_LIBRARY_PATH和PYTHONPATH,麻烦。
OPENCV_ENABLE_NONFREE=ON:这个参数决定是否启用 SIFT、SURF 等受专利保护的算法。虽然 SIFT 专利已经过期,但在 4.1.1 这个版本里它还在 nonfree 模块中,不开这个选项就用不了。做特征点匹配的项目必须开。
OPENCV_EXTRA_MODULES_PATH:指向 contrib 的 modules 目录。这是把扩展模块编进去的关键,路径必须写对,写错了 cmake 不会报错,但编译出来的 OpenCV 就是没有 contrib 模块,你会在import cv2之后发现cv2.face不存在。
CUDA_ARCH_BIN=5.3:Jetson Nano 的 GPU 计算能力。这个数字必须准确,写高了编译出来的代码在 Nano 上跑不了,写低了性能发挥不出来。CUDA_ARCH_PTX=留空,表示不生成 PTX 中间代码,能减少编译时间和体积。
WITH_CUDNN=ON和OPENCV_DNN_CUDA=ON:启用 cuDNN 加速和 DNN 模块的 CUDA 后端。如果你要跑深度学习推理(比如 YOLO、SSD),这两个必须开,否则 DNN 模块只能跑 CPU,速度差好几倍。
ENABLE_NEON=ON和ENABLE_VFPV3=ON:ARM 平台的 SIMD 指令集优化。NEON 是 ARM 的向量化指令,VFPV3 是浮点运算指令。开了这两个,CPU 端的图像处理速度能提升 20%-40%。
BUILD_TESTS=OFF和BUILD_PERF_TESTS=OFF:关掉测试和性能测试的编译。这些测试代码量很大,编译它们会多花一两个小时,而且你大概率不会去跑。关掉能省大量时间。
3.3 配置结果检查
cmake 跑完后,会输出一大段配置摘要。不要急着往下走,仔细看这段输出,重点检查以下几项:
| 检查项 | 期望值 | 说明 |
|---|---|---|
| CUDA | YES | 没开 CUDA 等于白编 |
| CUDNN | YES | DNN 加速依赖 |
| CUDA_ARCH_BIN | 5.3 | 写错 GPU 跑不了 |
| NEON | YES | ARM 优化 |
| VFPV3 | YES | 浮点优化 |
| Python 3 | YES | Python 绑定 |
| Non-free algorithms | YES | SIFT/SURF 支持 |
| FFMPEG | YES | 视频读写 |
| GStreamer | YES | 摄像头流处理 |
| V4L/V4L2 | YES | USB 摄像头 |
如果 CUDA 显示 NO,说明 CUDA 路径没找到,检查/usr/local/cuda是否存在,以及环境变量PATH和LD_LIBRARY_PATH是否包含 CUDA 的 bin 和 lib64 目录。如果 Python 3 显示 NO,检查PYTHON3_EXECUTABLE等路径是否写对。
实操心得:cmake 输出里如果有
Could NOT find的警告,不一定是致命的。比如找不到某些可选的编解码库,OpenCV 会跳过对应功能继续编译。但 CUDA、cuDNN、Python 这三项如果找不到,必须解决,否则编出来的东西不符合预期。
4. 编译与安装:耐心是唯一的技巧
4.1 启动编译
配置无误后,开始编译:
make -j4-j4表示用 4 个线程并行编译。Nano 是四核 CPU,用 4 个线程能充分利用。但要注意,并行编译会同时占用更多内存,如果你 swap 没扩够,-j4反而更容易 OOM。内存紧张的话可以用-j2甚至-j1,代价是编译时间翻倍。
编译时间参考:-j4大约 2-3 小时,-j2大约 4-5 小时,-j1可能要 8 小时以上。建议晚上睡前启动,第二天早上来看结果。
编译过程中会刷大量[ xx%] Building CXX object ...的输出。如果中途报错,输出会停在错误位置。常见的错误和排查方法在下一节详细讲。
4.2 安装到系统
编译完成后,执行安装:
sudo make install sudo ldconfigmake install会把编译好的库、头文件、Python 模块复制到/usr/local下对应的目录。ldconfig刷新动态链接库缓存,让系统能找到新装的库。
安装完成后,验证一下:
pkg-config --modversion opencv4应该输出4.1.1。再验证 Python 绑定:
python3 -c "import cv2; print(cv2.__version__)"同样应该输出4.1.1。
4.3 验证 CUDA 和 contrib 是否生效
版本号对了不代表 CUDA 和 contrib 就编进去了。进一步验证:
python3 -c "import cv2; print(cv2.cuda.getCudaEnabledDeviceCount())"如果输出大于 0,说明 CUDA 生效了。如果输出 0 或者报错,说明 CUDA 没编进去。
再验证 contrib:
python3 -c "import cv2; print(hasattr(cv2, 'ximgproc')); print(hasattr(cv2, 'face'))"两个都应该输出True。
注意:如果
import cv2报ImportError: libopencv_core.so.4.1: cannot open shared object file,说明ldconfig没生效或者安装路径不在库搜索路径里。执行sudo ldconfig再试,还不行就检查/etc/ld.so.conf.d/下有没有包含/usr/local/lib的配置文件。
5. 常见编译错误与排查实录
5.1 内存不足被 kill
现象:编译到某个进度突然中断,终端显示Killed或者c++: internal compiler error: Killed (program cc1plus)。
原因:内存耗尽,OOM Killer 杀掉了编译进程。
解决:确认 swap 是否扩到 4GB 以上。如果已经扩了还是被 kill,降低并行度,用make -j2或make -j1。另外可以关掉一些不必要的东西,比如BUILD_EXAMPLES=OFF、BUILD_opencv_dnn=OFF(如果你不用 DNN 模块),能减少内存占用。
5.2 CUDA 相关链接错误
现象:链接阶段报undefined reference to 'cudaXXX'或者cannot find -lcudart。
原因:CUDA 库路径没找到,或者 CUDA 版本和 OpenCV 4.1.1 不兼容。
解决:确认/usr/local/cuda/lib64在LD_LIBRARY_PATH里,/usr/local/cuda/bin在PATH里。可以在~/.bashrc末尾加上:
export PATH=/usr/local/cuda/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH然后source ~/.bashrc再重新 cmake。
5.3 Python 绑定找不到 numpy
现象:cmake 输出里 Python 3 的 numpy 显示 NO,或者编译 Python 绑定时报numpy/arrayobject.h: No such file。
原因:PYTHON3_NUMPY_INCLUDE_DIRS路径写错,或者 numpy 没装。
解决:用python3 -c "import numpy; print(numpy.get_include())"查出 numpy 头文件的实际路径,填到 cmake 参数里。JetPack 4.x 上通常是/usr/lib/python3/dist-packages/numpy/core/include。
5.4 contrib 模块编译失败
现象:编译到某个 contrib 模块时报错,比如opencv_contrib/modules/xxx下的文件编译不过。
原因:contrib 版本和主仓库版本不一致,或者某个 contrib 模块依赖的第三方库没装。
解决:首先确认两个仓库都是 4.1.1。如果版本对,看具体是哪个模块报错。比如text模块依赖 Tesseract,aruco依赖某些数学库。可以在 cmake 时用-D BUILD_opencv_xxx=OFF关掉出问题的模块,先保证主体编译通过。
5.5 编译成功但 import 报错
现象:make install成功,但python3 -c "import cv2"报ImportError。
原因:Python 模块安装路径不在sys.path里,或者系统里存在多个 OpenCV 版本冲突。
解决:先python3 -c "import sys; print(sys.path)"看路径。OpenCV 的 Python 模块通常装在/usr/local/lib/python3.6/site-packages/或/usr/lib/python3/dist-packages/。如果不在,手动加个软链接或者设PYTHONPATH。另外检查是不是之前 pip 装过 opencv-python,有的话先pip3 uninstall opencv-python卸掉。
5.6 常见问题速查表
| 问题现象 | 可能原因 | 快速解决 |
|---|---|---|
| 编译中途 Killed | 内存不足 | 扩 swap,降 -j 并行度 |
| CUDA 显示 NO | 路径未配置 | 设 PATH 和 LD_LIBRARY_PATH |
| numpy 找不到 | 路径写错 | 用 numpy.get_include() 查实际路径 |
| contrib 模块缺失 | 版本不匹配 | 确认两仓库都是 4.1.1 |
| import cv2 报错 | 路径冲突 | 卸载 pip 版,ldconfig |
| cv2.cuda 报错 | CUDA 未编入 | 检查 cmake 输出 CUDA=YES |
| SIFT 不可用 | nonfree 未开 | 加 OPENCV_ENABLE_NONFREE=ON |
6. 编译后的性能调优与实用技巧
6.1 让 OpenCV 真正跑在 GPU 上
编译带 CUDA 只是第一步,代码里还得显式调用 CUDA 接口才能用上 GPU。比如图像滤波:
import cv2 img = cv2.imread('test.jpg') gpu_img = cv2.cuda_GpuMat() gpu_img.upload(img) gpu_gray = cv2.cuda.cvtColor(gpu_img, cv2.COLOR_BGR2GRAY) result = gpu_gray.download()注意upload和download是有开销的,数据在 CPU 和 GPU 之间来回拷贝会抵消一部分加速效果。所以正确的做法是尽量把整个处理链路都放在 GPU 上,最后只 download 一次结果。如果只是处理单张小图,GPU 加速可能还不如 CPU 快,因为传输开销占了大头。GPU 加速在批量处理、视频流、大分辨率图像上优势才明显。
6.2 DNN 模块的 CUDA 后端
如果你用 OpenCV 的 DNN 模块跑推理,编译时开了OPENCV_DNN_CUDA=ON之后,可以这样启用 GPU:
net = cv2.dnn.readNetFromDarknet('yolov3.cfg', 'yolov3.weights') net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)实测在 Nano 上跑 YOLOv3-tiny,CPU 后端大概 2-3 FPS,CUDA 后端能到 15-20 FPS,提升非常明显。但注意 Nano 的 GPU 只有 128 个 CUDA 核心,跑大模型(比如 YOLOv3 完整版)还是很吃力,建议用轻量模型。
6.3 编译产物的清理与重编
如果编译失败需要重来,不要直接删 build 目录重新 cmake,那样浪费时间。先试make clean,它只清理编译产物,保留 cmake 配置。如果 cmake 配置本身有问题,再删 build 目录从头来。
如果只是想改一两个 cmake 参数,可以在 build 目录里直接重新跑 cmake 命令(带上新参数),然后make会增量编译,只重编受影响的部分,比全量编译快很多。
6.4 多版本共存的注意事项
如果你系统里之前装过其他版本的 OpenCV(比如 apt 装的libopencv-dev),可能会和新编的 4.1.1 冲突。检查方法:
dpkg -l | grep opencv如果有 apt 装的 OpenCV,建议卸掉:
sudo apt-get remove -y libopencv-dev sudo apt-get autoremove -y然后重新sudo ldconfig,确保系统优先加载/usr/local/lib下的 4.1.1。
实操心得:编译 OpenCV 是个体力活,但一次编好能用很久。我的建议是编之前把所有依赖一次性装齐,cmake 参数一次性写对,然后让它跑一晚上。中途不要频繁改参数重编,那样反而更浪费时间。另外,编译成功后可以把 build 目录整个打包备份,以后换系统或者重刷镜像时,直接解压
make install就行,省去重新编译的几个小时。