1. 项目概述:从零搭建你的第一个OpenCV4 C++视觉项目
如果你刚接触计算机视觉,或者已经用Python玩过OpenCV但想深入C++底层,这个系列就是为你准备的。我做了十多年工业视觉和嵌入式开发,深知很多教程一上来就扔一堆代码,却不讲清楚“为什么”要这么做。今天,我们就从最基础的“项目搭建”开始,我会带你用C++和OpenCV4,从环境配置到第一个可运行的视觉程序,把每一步的原理和踩过的坑都讲透。
计算机视觉听起来高大上,但核心就是让计算机“看懂”图像。OpenCV作为行业事实标准,提供了从图像处理到深度学习的一整套工具。用C++写OpenCV项目,尤其是在嵌入式、高性能或跨平台场景下,是很多一线项目的标配。这个系列的第一篇,目标很明确:帮你搭建一个坚实、可复现的C++开发环境,并写出第一个能读图、显示、处理图像的程序。别小看这一步,环境配置和基础框架的坑,足以劝退一半的新手。
2. 环境搭建与工具链选择:为什么选它们?
2.1 操作系统与编译器:Windows、Linux还是macOS?
首先得决定在哪开发。我的建议是: 优先考虑你的部署环境 。
- Linux (Ubuntu/Debian) :这是计算机视觉开发的“主场”。库依赖管理方便(apt-get),社区支持好,很多算法库原生为Linux优化。如果你做机器人、自动驾驶或服务器端应用,无脑选Ubuntu。我会以Ubuntu 20.04/22.04 LTS为例,因为它足够稳定,生态也成熟。
- Windows :如果你的团队或客户环境主要是Windows,或者你需要用Visual Studio的强大调试工具,那就选它。注意,在Windows上配置OpenCV的C++环境稍显繁琐,需要自己编译或找预编译包。
- macOS :开发体验不错,但某些工业相机驱动或特定硬件加速库可能支持不如前两者。用Homebrew管理依赖也很方便。
我的选择与理由 :这个系列我会以 Ubuntu 22.04 为主要环境进行讲解。原因有三:1) 开源生态完整;2) 编译和依赖问题最少;3) 便于后续引入CUDA等加速库。但关键步骤我会补充Windows (Visual Studio 2022) 下的差异点。
2.2 安装OpenCV4:源码编译才是王道
很多人图省事,直接用
apt-get install libopencv-dev
。对于快速验证可以,但对于严肃项目,我
强烈建议从源码编译
。理由如下:
- 可控性 :可以精确选择需要的模块(如不装GPU模块以减小体积)。
- 优化 :可以针对你的CPU指令集(如AVX2)进行编译优化,提升性能。
- 调试 :可以编译Debug版本,方便打断点跟踪OpenCV内部逻辑。
- 版本 :apt仓库的版本往往滞后,源码编译总能用到最新稳定版。
实操步骤(Ubuntu) :
# 1. 安装依赖库(这些是OpenCV编译和基础功能所必需的)
sudo apt update
sudo apt install -y build-essential cmake git pkg-config
sudo apt install -y libjpeg-dev libtiff-dev libpng-dev libavcodec-dev libavformat-dev libswscale-dev
sudo apt install -y libgtk-3-dev libcanberra-gtk3-dev
sudo apt install -y libxvidcore-dev libx264-dev
sudo apt install -y python3-dev python3-numpy
sudo apt install -y libtbb2 libtbb-dev libdc1394-22-dev
sudo apt install -y libv4l-dev v4l-utils
sudo apt install -y libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev
sudo apt install -y libopenblas-dev libatlas-base-dev libblas-dev
sudo apt install -y liblapack-dev liblapacke-dev libeigen3-dev
sudo apt install -y libhdf5-dev libprotobuf-dev protobuf-compiler
sudo apt install -y libgoogle-glog-dev libgflags-dev
# 2. 下载OpenCV4源码(以4.8.0为例,可替换为最新稳定版)
cd ~
git clone https://github.com/opencv/opencv.git
cd opencv
git checkout 4.8.0
# 如果需要OpenCV贡献库(包含SIFT、FaceDetector等额外算法)
cd ~
git clone https://github.com/opencv/opencv_contrib.git
cd opencv_contrib
git checkout 4.8.0
# 3. 使用CMake配置编译选项
cd ~/opencv
mkdir build && cd build
# 关键配置项解析:
# -D CMAKE_BUILD_TYPE=RELEASE:发布模式,优化性能。调试时改为DEBUG。
# -D CMAKE_INSTALL_PREFIX=/usr/local:安装路径,通常放这里。
# -D OPENCV_EXTRA_MODULES_PATH:指定贡献模块路径,如果你需要那些额外算法。
# -D WITH_CUDA=OFF:如果你有NVIDIA GPU并需要CUDA加速,改为ON并设置ARCH。
# -D BUILD_EXAMPLES=OFF:不编译例子,节省时间。
# -D BUILD_opencv_world=ON:将所有库打包成一个.so/.dll,链接时方便,但文件大。
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \
-D WITH_CUDA=OFF \
-D BUILD_EXAMPLES=OFF \
-D BUILD_opencv_world=ON \
..
# 4. 编译并安装(-j$(nproc) 使用所有CPU核心加速编译)
make -j$(nproc)
sudo make install
sudo ldconfig # 更新动态链接库缓存
注意 :编译过程可能持续30分钟到数小时,取决于你的机器性能。如果中途报错,大概率是某个依赖没装全,根据错误信息用
apt search和apt install补全即可。
Windows下的关键差异 :
- 需要先安装 Visual Studio 2022 (社区版即可),并勾选“使用C++的桌面开发”。
- 安装 CMake GUI 工具。
- 用CMake GUI指定源码路径和构建路径,点击“Configure”,选择“Visual Studio 17 2022”和“x64”,然后点击“Generate”。
-
在构建路径下打开生成的
OpenCV.sln,在VS里选择Release|x64,然后“生成”->“生成解决方案”,最后在“CMakeTargets”文件夹下右键“INSTALL”->“仅用于项目”->“仅生成INSTALL”。
2.3 IDE与构建系统:CMake是唯一推荐
对于C++项目,构建系统能帮你管理编译、链接、依赖,是项目可维护性的基石。
- 纯命令行 (g++/cl.exe) :适合微型单文件测试,项目稍大就难以管理。
- Visual Studio 项目文件 (.vcxproj) :仅限Windows,跨平台协作困难。
- CMake : 行业标准 ,跨平台(Windows/Linux/macOS),语法相对清晰,与OpenCV自身构建系统一致。
为什么是CMake?
它用一份
CMakeLists.txt
文件,就能生成对应平台的构建文件(Linux的Makefile,Windows的VS工程,macOS的Xcode工程)。你的队友用什么系统都能一键编译。
一个最基础的、用于本项目的
CMakeLists.txt
文件如下:
cmake_minimum_required(VERSION 3.10)
project(MyFirstCVProject)
# 设置C++标准(OpenCV4需要C++11以上,建议用C++14/17)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 寻找OpenCV包, REQUIRED表示找不到就报错
find_package(OpenCV REQUIRED)
# 包含OpenCV的头文件目录
include_directories(${OpenCV_INCLUDE_DIRS})
# 添加你的可执行文件,并链接OpenCV库
add_executable(visual_demo main.cpp)
target_link_libraries(visual_demo ${OpenCV_LIBS})
把这个文件放在你的项目根目录,然后在同一目录下执行:
mkdir build && cd build
cmake ..
make
就会生成可执行文件
visual_demo
。
2.4 验证安装:第一个“Hello, OpenCV!”程序
创建一个
main.cpp
文件,写入以下代码:
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 1. 打印OpenCV版本,验证链接是否正确
std::cout << "OpenCV version: " << CV_VERSION << std::endl;
// 2. 创建一个简单的黑色图像
cv::Mat image(300, 600, CV_8UC3, cv::Scalar(0, 0, 0));
// 3. 在图像上绘制文字
std::string text = "Hello, OpenCV4 with C++!";
int fontFace = cv::FONT_HERSHEY_SIMPLEX;
double fontScale = 1.0;
int thickness = 2;
cv::Size textSize = cv::getTextSize(text, fontFace, fontScale, thickness, nullptr);
// 计算文字位置使其居中
cv::Point textOrg((image.cols - textSize.width) / 2, (image.rows + textSize.height) / 2);
cv::putText(image, text, textOrg, fontFace, fontScale, cv::Scalar(0, 255, 0), thickness);
// 4. 显示图像
cv::imshow("First OpenCV Window", image);
// 5. 等待按键,否则窗口会一闪而过
std::cout << "Press any key in the window to exit..." << std::endl;
cv::waitKey(0);
return 0;
}
在项目
build
目录下编译并运行:
cd build
cmake .. # 只需第一次运行或CMakeLists.txt改变时运行
make
./visual_demo
如果弹出一个绿色文字的窗口,恭喜你,环境搭建成功!
3. 核心代码结构解析:不只是“Hello World”
上面的验证程序虽然简单,但已经包含了OpenCV C++程序的几个核心要素。我们来深入拆解:
3.1
cv::Mat
:图像的基石
cv::Mat
是OpenCV中存储和操作图像(乃至矩阵)的
绝对核心类
。理解它,就理解了OpenCV的一半。
cv::Mat image(300, 600, CV_8UC3, cv::Scalar(0, 0, 0));
这行代码创建了一个Mat对象:
-
300:图像高度(行数)。 -
600:图像宽度(列数)。 -
CV_8UC3:这是 数据类型和通道数 。这是最容易混淆的地方。-
CV_:前缀。 -
8U:深度。8表示8位,U表示无符号(unsigned)。还有S(有符号)、F(浮点)。例如CV_32F是32位浮点数。 -
C3:通道数。3表示3通道,即常见的BGR彩色图像(注意OpenCV默认是BGR顺序,不是RGB!)。C1表示单通道(灰度图)。
-
-
cv::Scalar(0, 0, 0):初始化值。对于CV_8UC3,Scalar的三个值分别对应B、G、R通道,这里都是0,所以是纯黑色。
重要特性 :
-
内存管理
:
cv::Mat使用引用计数,类似智能指针。当你image2 = image1;时,不会发生深拷贝,只是增加引用计数。修改image2会影响image1!如果需要真正的拷贝,必须用image1.copyTo(image2);或cv::Mat image2 = image1.clone();。 -
连续存储
:大多数情况下,Mat的数据在内存中是连续的,可以用
image.isContinuous()检查,用image.ptr<uchar>(row)快速访问行指针。
3.2 图像IO:读、写、显示
-
读取图像
:
cv::imread("path/to/image.jpg", cv::IMREAD_COLOR);-
第二个参数是加载标志。
IMREAD_COLOR强制转为3通道BGR,IMREAD_GRAYSCALE转为灰度,IMREAD_UNCHANGED保留原样(包括Alpha通道)。 务必检查返回值 :if(image.empty()) { std::cerr << "Could not open image!" << std::endl; return -1; }
-
第二个参数是加载标志。
-
显示图像
:
cv::imshow("Window Title", image);- 窗口标题必须唯一。它会自动根据图像大小创建窗口。
-
cv::waitKey(0);是 必须的 ,它处理GUI事件循环。参数是等待毫秒数,0表示无限等待直到有按键。即使你不显示图像,在某些后端下也需要调用它来释放GUI资源。
-
保存图像
:
cv::imwrite("output.png", image);- 格式由文件后缀决定。可以指定质量参数(如对于JPEG)。
3.3 基础绘图与文字
cv::putText
只是绘图API之一。OpenCV提供了丰富的绘图函数,在调试和可视化时极其有用:
-
cv::circle(img, center, radius, color, thickness):画圆。 -
cv::rectangle(img, pt1, pt2, color, thickness):画矩形。 -
cv::line(img, pt1, pt2, color, thickness):画线。 -
cv::ellipse,cv::polylines等。
一个实用技巧 :在循环中绘制检测框或关键点时,可以创建原图的一个拷贝来绘制,避免污染原始数据。
cv::Mat displayImage = image.clone(); // 创建副本用于显示
cv::rectangle(displayImage, cv::Rect(100, 100, 200, 150), cv::Scalar(0, 0, 255), 2);
cv::imshow("Detection", displayImage);
4. 第一个完整的图像处理Demo:灰度化、模糊与边缘检测
现在,我们写一个稍复杂的程序,它完成一个经典的图像处理流水线:读取图片 -> 转灰度 -> 高斯模糊 -> Canny边缘检测 -> 显示结果。
#include <opencv2/opencv.hpp>
#include <iostream>
int main(int argc, char** argv) {
// 1. 处理命令行参数
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <Image_Path>" << std::endl;
return -1;
}
// 2. 读取图像
cv::Mat originalImage = cv::imread(argv[1], cv::IMREAD_COLOR);
if (originalImage.empty()) {
std::cerr << "Error: Could not load image '" << argv[1] << "'" << std::endl;
return -1;
}
std::cout << "Image loaded. Size: " << originalImage.cols << "x" << originalImage.rows
<< ", Channels: " << originalImage.channels() << std::endl;
// 3. 转换为灰度图
cv::Mat grayImage;
cv::cvtColor(originalImage, grayImage, cv::COLOR_BGR2GRAY);
std::cout << "Converted to grayscale." << std::endl;
// 4. 应用高斯模糊去噪
cv::Mat blurredImage;
int kernelSize = 5; // 高斯核大小,必须是正奇数
double sigmaX = 1.5; // X方向标准差,0表示根据核大小自动计算
cv::GaussianBlur(grayImage, blurredImage, cv::Size(kernelSize, kernelSize), sigmaX);
std::cout << "Applied Gaussian blur with kernel size " << kernelSize << "." << std::endl;
// 5. Canny边缘检测
cv::Mat edgeImage;
double lowThreshold = 50; // 低阈值
double highThreshold = 150; // 高阈值,一般为低阈值的2-3倍
int apertureSize = 3; // Sobel算子孔径大小,必须是1, 3, 5, 7
cv::Canny(blurredImage, edgeImage, lowThreshold, highThreshold, apertureSize);
std::cout << "Canny edge detection applied." << std::endl;
// 6. 显示所有结果
cv::imshow("1. Original Color Image", originalImage);
cv::imshow("2. Grayscale Image", grayImage);
cv::imshow("3. After Gaussian Blur", blurredImage);
cv::imshow("4. Canny Edges", edgeImage);
// 7. 保存结果
cv::imwrite("gray_image.jpg", grayImage);
cv::imwrite("edges.jpg", edgeImage);
std::cout << "Intermediate results saved as 'gray_image.jpg' and 'edges.jpg'." << std::endl;
// 8. 等待退出
std::cout << "Press any key to exit..." << std::endl;
cv::waitKey(0);
return 0;
}
编译与运行 :
cd build
make
./visual_demo /path/to/your/photo.jpg
4.1 关键步骤原理解析
-
灰度化 (
cv::cvtColor) :将BGR三通道图像转换为单通道灰度图。常用公式是Gray = 0.299*R + 0.587*G + 0.114*B(亮度公式)。OpenCV内部会优化这个计算。 -
高斯模糊 (
cv::GaussianBlur) :一种线性平滑滤波器,用于消除图像中的高频噪声(如椒盐噪声)。它用一个高斯函数生成的权值矩阵(卷积核)去扫描图像,每个像素的新值是其邻域像素的加权平均。sigmaX控制权重分布,值越大图像越模糊。 为什么先模糊再找边缘? 因为原始图像可能有噪声,噪声点也会产生高强度梯度,被误判为边缘。先平滑可以抑制噪声。 -
Canny边缘检测 (
cv::Canny) :经典的边缘检测算法,包含多步:- 计算梯度 :用Sobel算子计算图像在X和Y方向的梯度幅值和方向。
- 非极大值抑制 :沿着梯度方向,只保留梯度幅值最大的点,细化边缘。
-
双阈值滞后连接
:用
lowThreshold和highThreshold过滤。高于高阈值的为强边缘,保留;低于低阈值的丢弃;在两者之间的为弱边缘,只有当它们连接到强边缘时才保留。这保证了边缘的连续性和抗噪性。
4.2 参数调优与常见问题
- 高斯核大小 :必须是正奇数(如3,5,7)。越大越模糊,计算越慢。通常5或7是个不错的起点。
-
Canny阈值
:这是
最需要调参
的地方。没有万能值。
- 太高 :只检测出最明显的边缘,可能断裂。
- 太低 :检测出太多边缘,包括噪声。
-
调试技巧
:可以设计一个滑动条实时调整,或者用大津法(Otsu‘s Method)自动计算阈值。对于
cv::Canny,可以先用cv::threshold或cv::adaptiveThreshold观察合适的阈值范围。
-
图像路径问题
:如果传入中文路径或带空格的路径,
imread可能会失败。在Windows上尤其要注意。可以用cv::String或std::string正确构造路径。
5. 项目结构规划与代码组织
一个可维护的视觉项目,不能把所有代码都堆在
main.cpp
里。我推荐这样的目录结构(在项目根目录下):
MyFirstCVProject/
├── CMakeLists.txt # 项目主CMake文件
├── main.cpp # 程序入口
├── include/ # 自己写的头文件
│ └── ImageProcessor.hpp
├── src/ # 自己写的源文件
│ ├── ImageProcessor.cpp
│ └── utils.cpp
├── data/ # 测试数据、图片、模型
│ └── test_image.jpg
├── build/ # 编译输出目录(.gitignore忽略)
├── scripts/ # 辅助脚本(如一键编译、清理)
└── README.md # 项目说明
对应的
CMakeLists.txt
需要升级:
cmake_minimum_required(VERSION 3.10)
project(MyFirstCVProject VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找OpenCV
find_package(OpenCV REQUIRED)
# 包含头文件目录:自己的include和OpenCV的
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include ${OpenCV_INCLUDE_DIRS})
# 添加可执行文件,并链接所有源文件
add_executable(visual_demo
main.cpp
src/ImageProcessor.cpp
src/utils.cpp
)
# 链接OpenCV库
target_link_libraries(visual_demo ${OpenCV_LIBS})
# 可选:设置输出目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
这样,你就可以把图像处理的逻辑(比如灰度化、模糊、Canny)封装到
ImageProcessor
类中,
main.cpp
只负责流程控制。这是迈向大型项目的第一步。
6. 调试与性能分析初步
6.1 使用GDB/LLDB调试
在Linux/macOS下,编译Debug版本并调试:
# 修改CMakeLists.txt,或在cmake命令中指定
# -D CMAKE_BUILD_TYPE=Debug
cd build
cmake -D CMAKE_BUILD_TYPE=Debug ..
make
gdb ./visual_demo
# 在gdb中:break main, run, next, print variable_name
在Windows下,直接用Visual Studio的调试器,设置断点、监视变量非常方便。
6.2 简单的性能测量
OpenCV提供了
cv::TickMeter
来方便地测量代码段耗时。
#include <opencv2/core/utility.hpp> // 包含TickMeter
cv::TickMeter tm;
tm.start();
// ... 你的耗时操作,比如循环处理1000次模糊
for(int i = 0; i < 1000; ++i) {
cv::GaussianBlur(src, dst, cv::Size(5,5), 1.5);
}
tm.stop();
std::cout << "Elapsed time: " << tm.getTimeMilli() << " ms" << std::endl;
6.3 内存泄漏检查
虽然
cv::Mat
有引用计数管理,但如果你用
new
创建了
cv::Mat*
,或者使用了
cv::Mat::create
后没有正确释放,也可能泄漏。对于简单项目,确保遵循RAII原则,尽量使用栈对象而非堆对象。对于复杂项目,可以使用Valgrind(Linux)或Visual Studio Diagnostic Tools(Windows)进行检测。
7. 常见问题与排查技巧实录
这里记录了我早期和带新人时最常遇到的坑:
问题1:编译时找不到
opencv2/opencv.hpp
头文件。
-
原因
:CMake没有正确找到OpenCV,或者
find_package失败。 -
排查
:
-
检查OpenCV是否安装成功:
pkg-config --modversion opencv4(Linux) 或查看/usr/local/include/opencv4(Linux) /C:\opencv\build\include(Windows) 是否存在。 - 检查CMake输出,看是否找到了OpenCV以及路径是否正确。
-
在
CMakeLists.txt中,find_package后添加message(STATUS "OpenCV lib status: ${OpenCV_LIBS}")打印信息。
-
检查OpenCV是否安装成功:
问题2:运行时错误:
error while loading shared libraries: libopencv_core.so.408: cannot open shared object file
- 原因 :动态链接库路径没被系统识别。
-
解决(Linux)
:
-
永久方案:将OpenCV的lib路径(如
/usr/local/lib)加入/etc/ld.so.conf或创建.conf文件到/etc/ld.so.conf.d/,然后执行sudo ldconfig。 -
临时方案:运行程序前设置
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH。
-
永久方案:将OpenCV的lib路径(如
-
解决(Windows)
:将OpenCV的
bin目录(包含.dll文件)添加到系统的PATH环境变量中。
问题3:图像显示窗口一闪而过,或者
imshow
后没有显示。
-
原因
:缺少
cv::waitKey()。waitKey不仅是等待按键,它还是OpenCV HighGUI模块处理窗口事件(刷新、重绘)的必要调用。即使你不关心按键,也需要一个短暂的waitKey(1)放在循环里,或者用waitKey(0)阻塞。
问题4:处理视频或摄像头时,程序占用CPU 100%,且很卡顿。
-
原因
:在读取帧的循环里没有适当的延迟,或者
imshow开销太大。 -
解决
:
cv::VideoCapture cap(0); // 打开摄像头 while(true) { cv::Mat frame; cap >> frame; if(frame.empty()) break; // ... 处理frame ... cv::imshow("Video", frame); char c = cv::waitKey(30); // 等待约30ms,对应~33FPS,同时处理按键 if(c == 27) break; // ESC退出 }- 如果处理很耗时,导致实际FPS远低于摄像头帧率,考虑将 采集线程 和 处理线程 分离。
问题5:
cv::Mat
赋值后,修改副本影响了原图。
-
原因
:忘记了
cv::Mat的浅拷贝特性。 -
解决
:需要深拷贝时,务必使用
.clone()或.copyTo()。cv::Mat img1 = cv::imread("test.jpg"); cv::Mat img2 = img1; // 浅拷贝,img2和img1共享数据! cv::Mat img3 = img1.clone(); // 深拷贝,独立数据 img2.setTo(cv::Scalar(0)); // 这会清空img1和img2! // img3不受影响
问题6:在树莓派等嵌入式设备上编译OpenCV速度极慢,且容易内存不足。
-
解决
:
-
增加交换空间:
sudo dphys-swapfile swapoff && sudo dphys-swapfile set-swap-size 2048 && sudo dphys-swapfile setup && sudo dphys-swapfile swapon(将交换文件设为2GB)。 -
只编译最需要的模块:在CMake时,禁用不需要的
BUILD_opencv_*选项,如-D BUILD_opencv_python=OFF -D BUILD_opencv_java=OFF -D BUILD_opencv_dnn=OFF等。 -
使用
make -j2或make -j1减少并行编译任务,避免内存爆掉。
-
增加交换空间:
8. 下一步做什么?项目扩展思路
第一个项目跑通后,你可以沿着这些方向深入:
-
处理视频流
:将
cv::imread换成cv::VideoCapture,参数可以是摄像头索引(如0)或视频文件路径。 -
加入交互
:使用
cv::createTrackbar创建滑动条,实时调整Canny阈值、模糊强度等参数,直观感受算法效果。 -
尝试更多基础算法
:
-
阈值分割
:
cv::threshold,cv::adaptiveThreshold -
形态学操作
:
cv::erode,cv::dilate,cv::morphologyEx(用于去噪、连接断边)。 -
轮廓查找
:
cv::findContours,这是物体分割和形状分析的基础。
-
阈值分割
:
-
为下一篇做准备
:下一篇我们将深入图像预处理、分割和特征提取,最终搭建一个简单的物体分类器。你可以尝试用
cv::findContours找出当前边缘图像中的所有轮廓,并计算它们的面积、周长、外接矩形,这是从边缘到“物体”的关键一步。
环境搭建和第一个程序是万里长征的第一步,但也是最容易让人放弃的一步。一旦跨过去,后面就是广阔的计算机视觉世界。记住,遇到问题多查OpenCV官方文档(https://docs.opencv.org/4.x/),多调试,多动手试。



8063

被折叠的 条评论
为什么被折叠?



