1. 环境准备与系统基础配置
1.1 为什么要先处理系统和软件源
说实话,在 Ubuntu 20.04 上搭建 OpenCV 4.5.0 和 C++ 环境,很多人一上来就急着下载源码、敲 cmake,结果编译到一半各种报错,要么缺依赖、要么网络超时、要么版本冲突。我早期也踩过不少这种坑,后来才明白一个道理:环境搭建这件事,九成的问题都出在准备工作没做到位。
Ubuntu 20.04 默认的 apt 软件源在国内访问速度通常不太理想,尤其是编译 OpenCV 需要安装大量的依赖包,如果源的速度慢或者不稳定,光是 apt install 这一步就能耗掉你大半个小时。所以在开始之前,我建议先把软件源更换为国内镜像源,比如清华、阿里或者中科大的源。这一步不复杂,但收益非常明显。
具体操作方式是备份原有的 sources.list 文件,然后用新的镜像源内容替换。这里以清华源为例:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list sudo sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list sudo apt update换完源之后,apt update的速度会明显改善。这里有个细节,Ubuntu 20.04 对应的代号是 focal,如果你用的镜像源配置不对,可能会出现“仓库没有 Release 文件”之类的报错,这时候检查一下 sources.list 里的代号是否正确就行。
1.2 安装编译工具链与核心依赖
接下来是安装编译工具链。OpenCV 4.5.0 的源码编译需要 CMake、GCC、G++ 等工具,这些可以通过 apt 直接安装:
sudo apt install -y build-essential cmake git pkg-configbuild-essential 这个包会一次性装好 gcc、g++、make 等基础编译工具,是 Ubuntu 上做 C/C++ 开发绕不开的组件。cmake 用于生成编译配置,pkg-config 用于管理库的编译参数。这些都是最基础的工具,但很多人容易漏掉 pkg-config,导致后续在编译 OpenCV 程序时找不到库文件路径,报出一堆奇怪的链接错误。
然后是图形和图像处理相关的依赖库。OpenCV 的很多功能模块依赖外部库,比如处理 PNG、JPEG 图片需要 libpng、libjpeg,读取摄像头视频流需要 libavcodec、libavformat,GUI 显示需要 GTK 或者 Qt。建议一次性把常用的依赖都装齐:
sudo apt install -y libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev sudo apt install -y libxvidcore-dev libx264-dev libjpeg-dev libpng-dev libtiff-dev sudo apt install -y libatlas-base-dev gfortran python3-dev python3-numpy这一堆依赖里,libgtk-3-dev 是用来支持 OpenCV 的 highgui 模块显示窗口的,libavcodec-dev、libavformat-dev、libswscale-dev 是视频编解码的核心库,libv4l-dev 则是摄像头采集的底层支持。如果你后续要用到摄像头,这些一个都不能少。
注意:很多人编译 OpenCV 时报错“No package 'gtk+-3.0' found”,就是因为在 CMake 配置之前没有安装 libgtk-3-dev。这类问题最好的解决办法就是提前把依赖装齐,别等到报错再回头补。
另外,如果你需要在 Python 中调用 OpenCV,就需要安装 python3-dev 和 python3-numpy。虽然这篇文章主要讲 C++ 环境,但 OpenCV 编译时检测到 Python 开发环境会自动生成对应的 Python 绑定,属于锦上添花的操作。
2. OpenCV 4.5.0 源码编译与安装
2.1 源码下载与目录规划
依赖装好之后,就可以开始下载 OpenCV 4.5.0 的源码了。这里我不建议直接去 GitHub 页面手动下载 zip 包,一方面解压麻烦,另一方面后续更新和查看版本信息都比较别扭。用 git clone 的方式会更干净:
cd ~ mkdir -p opencv_build && cd opencv_build git clone --branch 4.5.0 --depth 1 https://github.com/opencv/opencv.git这里使用了--branch 4.5.0指定分支,--depth 1做浅克隆,只拉取最新的一次提交,可以大大减少下载时间。如果你在中国大陆,GitHub 的访问速度可能不太稳定,可以考虑在 clone 时使用代理镜像,或者直接用 release 页面的压缩包下载链接配合下载工具。
如果后续要用到 SIFT、SURF 这些非免费模块,还需要额外克隆 opencv_contrib 仓库,并在 CMake 配置时指定 contrib 模块路径。对于大部分初学者来说,主线版本的 OpenCV 已经足够用了,不建议一开始就引入 contrib,等有明确需求时再加也不迟。
目录规划方面,我习惯把源码和编译目录分开。OpenCV 官方推荐的构建方式是在源码目录外单独建一个 build 目录,这样做的好处是后续重新编译时可以直接删除 build 目录,不会污染源码。我自己的习惯是:
cd opencv_build/opencv mkdir -p build && cd build这样最终的编译产物都会集中在 build 目录里,路径清晰,方便后续排查问题。
2.2 CMake 配置参数详解
进入了 build 目录之后,核心的一步就是执行 CMake 配置命令。很多教程直接复制一大段命令,跑完就完事,但完全不解释每个参数的含义,导致读者遇到问题根本不知道从哪里改起。我这里把参数拆开讲一讲。
先看一条最常用的完整配置命令:
cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D WITH_TBB=ON \ -D WITH_V4L=ON \ -D WITH_QT=OFF \ -D WITH_GTK=ON \ -D OPENCV_GENERATE_PKGCONFIG=ON \ -D OPENCV_ENABLE_NONFREE=ON \ -D BUILD_EXAMPLES=OFF \ -D BUILD_opencv_python3=ON \ -D PYTHON_EXECUTABLE=$(which python3) \ -D WITH_OPENGL=ON ..逐个看关键参数:
CMAKE_BUILD_TYPE=RELEASE:编译发布版本,编译器会做优化,运行效率更高。如果只是为了调试,可以改成 DEBUG,但日常使用建议 RELEASE。CMAKE_INSTALL_PREFIX=/usr/local:指定安装路径。/usr/local 是 Linux 下第三方软件的标准安装目录,头文件装在 /usr/local/include,库文件装在 /usr/local/lib,默认路径下让后面的编译更容易找到 OpenCV。WITH_TBB=ON:TBB 是 Intel 的并行计算库,开启后可以提升 OpenCV 在多核 CPU 上的运行效率。前提是你已经安装了 libtbb-dev,如果没有安装,CMake 配置时不会报错,会自动禁用。WITH_V4L=ON:开启 Video4Linux 支持,摄像头采集必备。WITH_QT=OFF / WITH_GTK=ON:highgui 模块需要一个 GUI 工具包来显示图像窗口。GTK 和 Qt 选一个即可,GTK 在 Ubuntu 上兼容性更好,装上之后显示窗口不会出问题。如果两个都装了,CMake 会默认优先使用 Qt,但 Qt 在纯命令行的 Ubuntu 服务器上经常缺少依赖,所以我个人建议直接用 GTK。OPENCV_GENERATE_PKGCONFIG=ON:这是 OpenCV 4.5.0 比较重要的一个参数。OpenCV 4 从某个版本开始不再默认生成 opencv.pc 文件,导致使用 pkg-config 方式编译 C++ 程序时找不到库路径。加上这个参数后,编译安装完成会自动生成 opencv4.pc,省去手动配置的麻烦。OPENCV_ENABLE_NONFREE=ON:开启非免费模块,会包含 SIFT、SURF 等专利算法。这个参数是可选的,如果只是做基础图像处理,不涉及特征提取,可以不开。BUILD_EXAMPLES=OFF:不编译 OpenCV 自带的示例程序,节省大量编译时间。示例代码在源码的 samples 目录里可以直接查看,没必要编译成可执行文件。BUILD_opencv_python3=ON和PYTHON_EXECUTABLE=$(which python3):这两个参数配合使用,让 OpenCV 同时生成 Python 3 的接口库。如果你确定只用 C++,可以不加;但加上也不影响 C++ 的使用,反而多一条路。
提示:如果你的服务器内存比较小,比如 2GB 的云主机,建议在 CMake 配置时加上
-D BUILD_TESTING=OFF,同时在后续的 make 编译阶段限制并行任务数,避免编译时内存耗尽被系统 kill 掉。
2.3 编译、安装与路径配置
CMake 配置成功之后,输出信息的最后会列出一份完整的配置摘要,包括检测到的库、启用的模块、安装路径等。建议花一分钟扫一眼,确认第三方的库有没有被正确检测到,比如 GTK、V4L 等。如果GUI: GTK+显示为 NO,那说明 libgtk-3-dev 没装好,后续程序里调用imshow会直接崩溃。
确认配置无误后,开始编译。这一步我是强烈建议用多线程的,OpenCV 源码体量不小,单线程编译在普通机器上可能要一两个小时,多线程能省不少时间:
make -j$(nproc)nproc命令会返回 CPU 的核心数,make -j$(nproc)表示用全部核心并行编译。如果是在虚拟机里,内存不是特别充足,建议手动指定一个保守的数值,比如make -j4。编译过程中会刷大量的日志,如果你发现某个 .cpp 文件编译报错,先不要慌,记录下报错信息,绝大多数情况都是依赖库缺少或者版本不匹配导致的,排查思路我在后面专门讲。
编译完成后就是安装:
sudo make install sudo ldconfigmake install会把头文件、库文件、CMake 配置文件安装到之前设置的路径下。ldconfig的作用是更新动态链接库缓存,让系统能够找到新安装的库文件。漏掉ldconfig的话,编译时链接库的阶段会报“cannot find -lopencv_core”之类的错误。
安装完成之后,验证一下是否成功:
pkg-config --modversion opencv4如果能够输出版本号 4.5.0,说明安装没问题了。这里要注意,OpenCV 4 的 pkg-config 名称是 opencv4,不是 opencv。如果你习惯了旧版本的命令,很容易在这一步搞混。
3. C++ 开发环境搭建
3.1 编译器与 CMake 版本检查
OpenCV 装好之后,接下来要准备 C++ 的开发环境。Ubuntu 20.04 自带的 GCC 版本是 9.x,CMake 版本是 3.16.x,对于 OpenCV 4.5.0 来说都够用。不过还是建议先确认一下版本:
g++ --version cmake --version如果 GCC 版本过低,编译 OpenCV 程序时可能会报“不支持 C++ 11 标准”之类的错误。Ubuntu 20.04 的默认工具链不存在这个问题,但如果你是从旧的 Ubuntu 版本升级上来的,保险起见还是检查一下。
开发 C++ 程序有两种常见方式:直接用 g++ 命令行编译,或者用 CMake 管理工程。命令行编译适合单文件的小测试,比如:
g++ -std=c++11 test.cpp $(pkg-config --cflags --libs opencv4) -o testpkg-config --cflags --libs opencv4会自动展开 OpenCV 头文件路径和库链接参数。这种方式简单粗暴,但项目文件一多就不好维护了。正式的工程项目,我建议用 CMake 组织。
3.2 VS Code 配置 C/C++ 开发环境
编辑器方面,VS Code 是当前比较主流的选择,插件生态丰富,配置也灵活。在 Ubuntu 20.04 上装 VS Code 可以用微软官方源,或者直接去官网下载 .deb 包安装。这里推荐用 deb 包,因为可以顺便把微软的软件源注册好,以后能直接用 apt upgrade 升级。
打开 VS Code 之后,首先安装 C/C++ 扩展插件。这个插件由微软官方维护,提供 IntelliSense 智能提示、代码补全、调试功能。具体的配置点在.vscode/c_cpp_properties.json里,核心的配置项是includePath,也就是头文件搜索路径。你要把 OpenCV 的 include 目录加进去:
{ "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/usr/local/include/opencv4" ], "defines": [], "compilerPath": "/usr/bin/g++", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "gcc-x64" } ], "version": 4 }很多人在 VS Code 里写 OpenCV 代码时,发现头文件下面会有红色波浪线,提示找不到 opencv2/opencv.hpp,大多数原因就是 includePath 没配好。还有个容易忽略的点是,如果你系统里装了多个版本的 OpenCV,includePath 里配了旧版本的路径,IntelliSense 可能会给出错误的提示。建议固定使用 /usr/local/include/opencv4 这个路径,和 CMake 的搜索路径保持一致。
另外,VS Code 的 C/C++ 插件的智能提示搜索路径是有优先级的,includePath里配置的顺序会直接影响提示的准确性。把项目目录路径放在前面,系统路径放在后面,这样能够避免被其他的头文件干扰。
3.3 用 CMake 组织工程
CMake 是目前 C++ 工程最主流的构建系统,OpenCV 官方也是用 CMake 编译的,所以用 CMake 来构建自己的项目是最自然而然的选择。一个最简单的 CMakeLists.txt 长这样:
cmake_minimum_required(VERSION 3.10) project(HelloOpenCV) set(CMAKE_CXX_STANDARD 11) find_package(OpenCV REQUIRED) add_executable(hello main.cpp) target_link_libraries(hello ${OpenCV_LIBS})这里有三个关键点需要说明:
第一,find_package(OpenCV REQUIRED)的核心原理是查找 OpenCV 安装时生成的OpenCVConfig.cmake文件。如果你在 CMake 配置时报错“Could not find OpenCV”,大概率是因为这个文件不在默认搜索路径里。解决办法是在 CMake 命令中手动指定路径:
cmake -D OpenCV_DIR=/usr/local/lib/cmake/opencv4 ..第二,${OpenCV_LIBS}变量会被展开为一长串 OpenCV 库名称。理论上手动逐个链接也可以,但用这个变量最省事。CMake 会帮你把所有必需的库全部链接上,不用担心漏掉。
第三,CMakeLists.txt 里用find_package找到的 OpenCV 版本和路径可以通过下面的方式打印出来,方便排查问题:
message(STATUS "OpenCV version: ${OpenCV_VERSION}") message(STATUS "OpenCV libraries: ${OpenCV_LIBS}")编译运行:
mkdir build && cd build cmake .. make ./hello整个过程清爽干净,后续增加源文件只需要在 CMakeLists.txt 里加一行add_executable或者target_sources就行。
4. 编写并运行第一个 OpenCV 程序
4.1 读取并显示图片
环境都搭建好之后,我们来写第一个练手程序。目标很简单:读取一张图片,在窗口里显示出来,然后保存一份灰度图副本。这个流程覆盖了 OpenCV 最核心的几个模块:core(数据结构)、imgcodecs(图片读写)、imgproc(图像处理)、highgui(界面显示)。
先写代码,保存为 display.cpp:
#include <opencv2/opencv.hpp> #include <iostream> int main(int argc, char** argv) { if (argc != 2) { std::cout << "Usage: ./display <image_path>" << std::endl; return -1; } cv::Mat image = cv::imread(argv[1], cv::IMREAD_COLOR); if (image.empty()) { std::cerr << "Could not read the image: " << argv[1] << std::endl; return -1; } std::cout << "Image width: " << image.cols << ", height: " << image.rows << std::endl; cv::Mat gray; cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY); cv::imshow("Original Image", image); cv::imshow("Gray Image", gray); cv::imwrite("gray_output.jpg", gray); cv::waitKey(0); cv::destroyAllWindows(); return 0; }这段代码有几个细节值得注意:
第一,image.empty()的判断非常关键。很多人第一次运行时,图片路径写错了或者权限不足,导致 imread 返回空矩阵,但程序不报错,直到后面 imshow 的时候才崩溃。提前判断 empty 能够让问题在早期暴露出来,比后面排查崩溃要省事得多。
第二,cv::waitKey(0)的作用是等待用户按键输入,参数 0 表示无限等待。没有这一行的话,窗口可能会一闪而过甚至直接卡死。在高版本的 OpenCV 中,因为 GTK 的事件循环机制,waitKey是让窗口正常显示和响应的前提。
第三,cv::cvtColor中的cv::COLOR_BGR2GRAY是 OpenCV 的颜色空间转换标志。OpenCV 默认读图的通道顺序是 BGR 而不是 RGB,这个概念在刚入门时很容易搞混,我记得后来做项目的时候,图像颜色偏蓝偏红基本都是这里出了问题。
编译运行的方式按之前的 CMake 工程来,把 main.cpp 换成 display.cpp,在 build 目录里重新 cmake 和 make,然后用一张测试图片执行:
./display /path/to/your/test.jpg如果一切顺利,会弹出两个窗口,一个彩色一个灰度。按任意键窗口关闭,当前目录下会生成 gray_output.jpg。
4.2 打开摄像头实时画面
图片处理只是入门,摄像头实时采集才是 OpenCV 在视觉项目里最常见的应用场景。这里写一个简单的程序,打开默认摄像头,实时显示画面,并实时计算帧率。相关热词里提到 ubuntu 20.04 网络配置和摄像头,正好可以拿这个例子串起来。
先写代码,保存为 webcam.cpp:
#include <opencv2/opencv.hpp> #include <iostream> #include <chrono> int main() { cv::VideoCapture cap(0); if (!cap.isOpened()) { std::cerr << "Error: Could not open camera." << std::endl; return -1; } cv::Mat frame; auto start_time = std::chrono::steady_clock::now(); int frame_count = 0; while (true) { cap >> frame; if (frame.empty()) { break; } cv::imshow("Webcam", frame); frame_count++; auto now = std::chrono::steady_clock::now(); double elapsed = std::chrono::duration<double>(now - start_time).count(); if (elapsed >= 1.0) { double fps = frame_count / elapsed; std::cout << "FPS: " << fps << std::endl; frame_count = 0; start_time = now; } if (cv::waitKey(30) == 27) { // ESC key break; } } cap.release(); cv::destroyAllWindows(); return 0; }这段代码里最关键的是cv::VideoCapture cap(0)。参数 0 表示打开系统中第一个可用的摄像头设备,对应 /dev/video0。如果电脑同时插了多个摄像头,可以在 0、1、2 之间切换来尝试不同的设备。
使用摄像头时,请一定检查一下设备节点是否存在以及当前用户是否有访问权限:
ls -l /dev/video*有时候摄像头节点存在,但当前用户不在 video 组里,程序会报权限不足打不开设备。这时候把当前用户加入 video 组,重新登录即可:
sudo usermod -aG video $USER另外在虚拟机中使用 USB 摄像头,需要先在虚拟机的设置里把 USB 设备直通给虚拟机,否则宿主机能看到设备,VM 里却完全无感知。这个坑我在 VMware 里踩过很多次,win 下没问题,换到 Ubuntu 虚拟机就发现摄像头图标是灰的,排查半天发现是 USB 直通没做。
5. 常见问题与排查实录
5.1 编译报错与路径问题速查表
我在多个版本、多台机器上搭过 OpenCV 环境,把遇到的典型问题按出错场景整理成了表格,方便你对照排查。
| 出错场景 | 错误现象 | 根本原因 | 解决办法 |
|---|---|---|---|
| cmake 配置阶段 | Could not find OpenCV | OpenCV_DIR 指向不对或者没安装 | 手动指定 -D OpenCV_DIR=/usr/local/lib/cmake/opencv4 |
| cmake 配置阶段 | No package 'gtk+-3.0' found | 缺少 GTK 开发库 | sudo apt install -y libgtk-3-dev 后重新配置 |
| make 编译阶段 | fatal error: X11/Xlib.h: No such file or directory | 缺少 X11 开发头文件 | sudo apt install -y libx11-dev libxext-dev |
| make 编译阶段 | undefined reference topng_set_chunk_malloc_max | libpng 版本冲突 | 更新 libpng-dev 后重新编译,必要时先 make clean |
| make 安装后 | pkg-config 找不到 opencv4 | 编译时未开 OPENCV_GENERATE_PKGCONFIG | 重新 cmake 时加上 -D OPENCV_GENERATE_PKGCONFIG=ON |
| g++ 链接阶段 | cannot find -lopencv_core | ldconfig 未运行或库路径未注册 | sudo ldconfig,或设置 LD_LIBRARY_PATH=/usr/local/lib |
| g++ 链接阶段 | /usr/bin/ld: cannot find -lstdc++ | 未安装 build-essential | sudo apt install -y build-essential |
| 运行阶段 | error while loading shared libraries: libopencv_core.so.4.5 | 动态链接库路径未配置 | 检查 /etc/ld.so.conf 包含 /usr/local/lib,然后 sudo ldconfig |
| 运行阶段 | Gtk-WARNING: cannot open display | 没有图形界面环境 | 使用远程桌面或切换 tty,或改用无 GUI 模式(imwrite 代替 imshow) |
| 运行阶段 | camera failed to open | 权限不足或设备不存在 | 检查 /dev/video* 与用户组权限 |
5.2 我总结的几个避坑经验
结合这些年的实际经验,我额外分享几条常规文档不会写但非常实用的经验。
第一,编译前确认磁盘空间充足。OpenCV 编译过程中,build 目录会占用 4-6GB 的空间,如果是树莓派或者小容量分区,很容易在编译到一半时因磁盘写满而失败。编译之前用df -h瞄一眼剩余空间,确保在 10GB 以上比较稳妥。
第二,尽量不要用 apt 安装系统自带的 libopencv-dev。Ubuntu 20.04 的官方软件源里其实有 OpenCV 4.2 版本,命令行apt install libopencv-dev一条命令就能装好,看起来很省事。但版本既然是 4.5.0,说明你对版本有特定要求,而且自己编译能够控制编译参数,比如开启 TBB 优化、Nonfree 模块等。如果你在图省事装了系统自带版本,后续 pkg-config 的模块名、头文件路径都会跟你编译出来的版本混在一起,很难排查。要么全用 apt 版本,要么全用源码编译版本,别混用。
第三,make -j 的并行数不是越大越好。很多人看到推荐make -j$(nproc),觉得核心数越多越好。如果你的内存比较小,并行编译时每个编译进程都会占用几百 MB 内存,N 个进程同时跑,内存很快就爆了,最终 OOM 被系统杀掉,反而更浪费时间。我的建议是内存 16GB 以上可以拉满,8GB 用-j4,4GB 用-j2,稳字当头。
第四,同一个终端会话里多跑几次 source 和 export,不如直接修改系统配置文件。如果你在配置动态库路径时用了export LD_LIBRARY_PATH=/usr/local/lib,这条命令只在当前终端有效,新开终端又没了。正确做法是写入配置文件:
echo 'export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc5.3 终极验证:用 CMake 一步到位跑通项目
前面所有的配置和排查,最终都要落到“能跑起来一个真正的 C++ 工程”才算数。这里我提供一个最小的完整工程模板,你复制过去改成自己的代码就能用:
my_opencv_project/ ├── CMakeLists.txt ├── main.cpp └── build/CMakeLists.txt 用我之前写的模板,main.cpp 放一张图片读取的程序。然后:
cd my_opencv_project mkdir build && cd build cmake -D CMAKE_BUILD_TYPE=RELEASE .. make -j4 ./my_opencv_project ../test.jpg这一步跑通了,说明你的 Ubuntu 20.04 + OpenCV 4.5.0 + C++ 环境已经完全可用。
从个人体会来看,环境搭建最大的障碍其实不在 OpenCV 本身,而在于 Linux 的依赖管理体系。库与库之间的版本要求、头文件与动态库的查找路径、编译时与运行时的环境差异,这些才是真正耗时的地方。但只要理解了 apt、CMake、pkg-config、ldconfig 这几个关键环节的原理,大部分问题都能快速定位。这套环境装好之后,后面不管是做图像处理、视觉算法入门,还是跑深度学习的推理前处理,都能省下很多折腾的时间。