使用 C/C++的OpenCV 进行轮廓提取
轮廓可以简单地描述为连接所有具有相同颜色或强度的连续点(沿着边界)的曲线。轮廓是形状分析以及对象检测和识别的有用工具。OpenCV 提供了非常方便的函数来查找和绘制轮廓。
本文将指导您完成使用 C++ 和 OpenCV 库从图像中提取轮廓的过程。
预备知识
在开始之前,请确保您已经安装了 OpenCV,并且您的 C++ 开发环境已经配置好可以链接 OpenCV 库。
通常,我们需要包含以下头文件:
#include <opencv2/opencv.hpp> // 包含所有核心和contrib模块
#include <iostream>
#include <vector> // 用于存储轮廓
为方便起见,我们也会使用 cv
和 std
命名空间:
using namespace cv;
using namespace std;
轮廓提取的步骤
轮廓提取通常涉及以下步骤:
- 加载图像:读取源图像。
- 预处理:
- 转换为灰度图:轮廓检测通常在灰度图像上进行。
- 高斯模糊 (可选):减少噪声,有助于获得更清晰的轮廓。
- 二值化:将灰度图像转换为二值图像(黑白图像)。这是
findContours
函数所必需的。常用的方法有简单阈值处理、自适应阈值处理或 Canny 边缘检测。
- 查找轮廓:使用
cv::findContours
函数。 - 绘制轮廓 (可选):使用
cv::drawContours
函数在图像上可视化找到的轮廓。
1. 加载图像和预处理
首先,我们加载图像并进行必要的预处理,将其转换为二值图像。
int main(int argc, char** argv) {
// 1. 加载图像
// const char* filename = argc >= 2 ? argv[1] : "shapes.png"; // 从命令行参数或默认读取
Mat src = imread("your_image.png", IMREAD_COLOR); // 请替换为您的图片路径
if (src.empty()) {
cout << "无法加载图像: " << "your_image.png" << endl;
return -1;
}
imshow("Original Image", src);
// 2. 转换为灰度图
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("Grayscale Image", gray);
// 3. (可选)高斯模糊以减少噪声
Mat blurred;
GaussianBlur(gray, blurred, Size(5, 5), 0); // 使用5x5的核
imshow("Blurred Image", blurred);
// 4. 二值化图像
// 方法一:使用阈值处理
Mat binary_thresh;
// threshold(blurred, binary_thresh, 100, 255, THRESH_BINARY_INV); // 根据图像调整阈值,THRESH_BINARY_INV使物体为白色,背景为黑色
// 或者使用Otsu's方法自动确定阈值
threshold(blurred, binary_thresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
imshow("Threshold Binary Image", binary_thresh);
// 方法二:使用Canny边缘检测(Canny的输出已经是二值图像)
Mat canny_output;
Canny(blurred, canny_output, 50, 150); // 调整低阈值和高阈值
imshow("Canny Edges", canny_output);
// 为 findContours 选择一个二值图像作为输入
// 通常,如果物体是实心的,阈值图像效果好;如果只关心边缘,Canny图也可以。
// 注意:findContours会修改输入的二值图像,所以如果之后还需要它,请传递一个副本。
Mat input_for_contours = binary_thresh.clone(); // 或者 canny_output.clone();
重要提示:cv::findContours
函数会修改输入的二值图像。如果您希望保留原始的二值图像,请传递它的一个副本(例如,使用 .clone()
)。
2. 查找轮廓 (cv::findContours
)
一旦有了二值图像,就可以使用 cv::findContours
函数来查找轮廓。
cv::findContours
函数原型:
void findContours( InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset = Point());
image
: 输入的单通道8位二值图像。此图像会被函数修改。contours
: 检测到的轮廓。每个轮廓都是一个std::vector<cv::Point>
。因此,contours
是一个std::vector<std::vector<cv::Point>>
。hierarchy
: 可选的输出向量 (std::vector<cv::Vec4i>
),包含图像的拓扑结构。每个元素hierarchy[i]
对应于contours[i]
,包含4个索引:[Next, Previous, First_Child, Parent]
。mode
: 轮廓检索模式。RETR_EXTERNAL
: 只检索最外层的轮廓。RETR_LIST
: 检索所有轮廓,但不建立任何层次关系(所有轮廓都在同一级别)。RETR_CCOMP
: 检索所有轮廓并将它们组织成两级层次结构:外部边界和内部孔洞边界。RETR_TREE
: 检索所有轮廓并重建完整的层次结构。
method
: 轮廓逼近方法。CHAIN_APPROX_NONE
: 存储轮廓上的所有点。CHAIN_APPROX_SIMPLE
: 压缩水平、垂直和对角线段,只保留它们的端点。例如,矩形的轮廓将只由4个点组成。CHAIN_APPROX_TC89_L1
,CHAIN_APPROX_TC89_KCOS
: 应用Teh-Chin链逼近算法中的一种。
offset
: 可选的轮廓点偏移量。如果您的轮廓是从图像的ROI中提取的,然后希望在整个图像上下文中分析它们,这将非常有用。
// ... 接上文 input_for_contours
vector<vector<Point>> contours; // 用于存储所有轮廓
vector<Vec4i> hierarchy; // 用于存储轮廓的层级信息
// 查找轮廓
findContours(input_for_contours, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
// 使用 RETR_EXTERNAL 如果你只想找最外层轮廓
// findContours(input_for_contours, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
cout << "找到的轮廓数量: " << contours.size() << endl;
3. 理解轮廓层级 (hierarchy
)
hierarchy
向量中的每个元素 Vec4i
对应于 contours
中的一个轮廓,并存储了4个整数:
hierarchy[i] = [Next_contour_idx, Previous_contour_idx, First_child_contour_idx, Parent_contour_idx]
Next_contour_idx
: 与当前轮廓在同一层级上的下一个轮廓的索引。Previous_contour_idx
: 与当前轮廓在同一层级上的上一个轮廓的索引。First_child_contour_idx
: 当前轮廓的第一个子轮廓的索引。Parent_contour_idx
: 当前轮廓的父轮廓的索引。
如果对应的轮廓不存在,则索引值为 -1。
例如,使用 RETR_TREE
可以帮助您理解嵌套的轮廓(例如,一个形状内部的孔洞)。
4. 绘制轮廓 (cv::drawContours
)
找到轮廓后,通常希望将它们可视化。cv::drawContours
函数用于此目的。
cv::drawContours
函数原型:
void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color,
int thickness = 1, int lineType = LINE_8,
InputArray hierarchy = noArray(), int maxLevel = INT_MAX,
Point offset = Point() );
image
: 要在其上绘制轮廓的目标图像(通常是原始彩色图像的副本或新的空白图像)。contours
: 所有找到的轮廓,从findContours
获得。contourIdx
: 指示要绘制哪个轮廓的参数。如果为负数(例如 -1),则绘制所有轮廓。color
: 轮廓的颜色。thickness
: 轮廓线的粗细。如果为负数或FILLED
,则轮廓内部被填充。lineType
: 线的连接类型 (例如LINE_8
,LINE_AA
抗锯齿线)。hierarchy
: 可选的层级信息,如果只想绘制特定层级的轮廓。maxLevel
: 仅当提供了hierarchy
时才考虑此参数。0 表示只绘制指定的轮廓,1 表示绘制指定轮廓及其所有子轮廓,依此类推。offset
: 可选的轮廓点偏移量。
// ... 接上文 contours 和 hierarchy
// 创建一个空白图像用于绘制轮廓,或在原图副本上绘制
Mat drawing = Mat::zeros(input_for_contours.size(), CV_8UC3); // 创建一个黑色的画布
// 或者在原图上绘制
// Mat drawing = src.clone();
for (size_t i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rand() % 256, rand() % 256, rand() % 256); // 随机颜色
// 绘制单个轮廓
// drawContours(drawing, contours, static_cast<int>(i), color, 2, LINE_8, hierarchy, 0);
// 或者绘制所有轮廓
}
// 绘制所有轮廓,使用一种颜色
drawContours(drawing, contours, -1, Scalar(0, 255, 0), 2, LINE_8, hierarchy, 100);
imshow("Contours", drawing);
5. 轮廓分析 (简介)
找到轮廓后,可以进行各种分析:
- 计算轮廓面积:
double area = contourArea(contours[i]);
- 计算轮廓周长:
double perimeter = arcLength(contours[i], true);
(true表示轮廓是闭合的) - 轮廓近似:
approxPolyDP(contours[i], approx_curve, epsilon, true);
将轮廓逼近为具有较少顶点的多边形。 - 获取边界框:
Rect bounding_rect = boundingRect(contours[i]);
- 获取最小外接圆:
Point2f center; float radius; minEnclosingCircle(contours[i], center, radius);
- 形状匹配, 凸包, 几何矩 等。
这些分析可以用于过滤轮廓(例如,只保留面积大于某个阈值的轮廓)或识别特定形状。
完整示例代码
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
// 1. 加载图像
const char* filename = "your_image.png"; // 请替换为您的图片路径
Mat src = imread(filename, IMREAD_COLOR);
if (src.empty()) {
cout << "无法加载图像: " << filename << endl;
return -1;
}
imshow("Original Image", src);
// 2. 转换为灰度图
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
// 3. 高斯模糊
Mat blurred;
GaussianBlur(gray, blurred, Size(5, 5), 0);
// 4. 二值化 (Otsu's 方法)
Mat binary_thresh;
threshold(blurred, binary_thresh, 0, 255, THRESH_BINARY | THRESH_OTSU); // 物体为白色,背景黑色
// 如果findContours需要白色物体在黑色背景上,请确保二值化正确
// 如果物体是黑色的,背景是白色的,可能需要 THRESH_BINARY_INV
// 或者确保Canny的输出是白色边缘在黑色背景上
imshow("Binary Image for Contours", binary_thresh);
// --- 查找轮廓 ---
// 注意:findContours会修改输入的二值图像binary_thresh,所以如果之后还需要它,请使用它的副本
Mat binary_copy = binary_thresh.clone();
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
// RETR_EXTERNAL 只查找最外层轮廓
// RETR_TREE 查找所有轮廓并建立完整的层级结构
findContours(binary_copy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
cout << "找到的轮廓数量: " << contours.size() << endl;
// --- 绘制轮廓 ---
// 创建一个彩色图像用于绘制轮廓 (在原始图像的副本上绘制)
Mat drawing = src.clone();
// 或者在一个新的黑色画布上绘制
// Mat drawing = Mat::zeros(binary_thresh.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++) {
Scalar color;
// 根据层级给内外轮廓不同颜色 (简单示例)
if (hierarchy[i][3] == -1) { // 最外层轮廓 (没有父轮廓)
color = Scalar(0, 0, 255); // 红色
} else {
color = Scalar(0, 255, 0); // 绿色 (内部轮廓)
}
drawContours(drawing, contours, static_cast<int>(i), color, 2, LINE_8, hierarchy, 0);
}
// 或者一次性绘制所有轮廓,用统一颜色
// drawContours(drawing, contours, -1, Scalar(255,0,0), 2);
imshow("Contours", drawing);
// --- 轮廓分析示例:过滤小轮廓并绘制其边界框 ---
Mat drawing_filtered = src.clone();
for (size_t i = 0; i < contours.size(); i++) {
double area = contourArea(contours[i]);
if (area > 100) { // 过滤掉面积小于100的轮廓
Scalar color = Scalar(rand() % 256, rand() % 256, rand() % 256);
drawContours(drawing_filtered, contours, static_cast<int>(i), color, 2);
Rect bounding_rect = boundingRect(contours[i]);
rectangle(drawing_filtered, bounding_rect.tl(), bounding_rect.br(), Scalar(0,255,255), 2);
}
}
imshow("Filtered Contours with Bounding Boxes", drawing_filtered);
cout << "按任意键退出..." << endl;
waitKey(0);
destroyAllWindows();
return 0;
}
编译与运行
假设您已正确安装 OpenCV,可以使用 g++ 编译上述代码:
g++ your_code_file.cpp -o contour_extraction $(pkg-config --cflags --libs opencv4)
./contour_extraction your_image.png
(如果 pkg-config --libs opencv4
不起作用,请根据您的 OpenCV 版本和安装方式调整链接器标志,例如 opencv
或特定模块如 opencv_core opencv_imgproc opencv_highgui opencv_imgcodecs
)。确保替换 "your_image.png"
为您要测试的图像路径。
总结
轮廓提取是计算机视觉中的一项基础且强大的技术。通过结合适当的预处理步骤和 cv::findContours
函数,您可以有效地从图像中分离出感兴趣的对象或区域的边界。cv::drawContours
使得可视化这些轮廓变得简单。此外,OpenCV 还提供了丰富的函数来分析这些轮廓的属性,为更高级的图像分析任务(如形状识别、对象跟踪等)奠定了基础。
```markdown
# 使用 C++/OpenCV 进行轮廓提取
轮廓可以简单地描述为连接所有具有相同颜色或强度的连续点(沿着边界)的曲线。轮廓是形状分析以及对象检测和识别的有用工具。OpenCV 提供了非常方便的函数来查找和绘制轮廓。
本文将指导您完成使用 C++ 和 OpenCV 库从图像中提取轮廓的过程。
## 预备知识
在开始之前,请确保您已经安装了 OpenCV,并且您的 C++ 开发环境已经配置好可以链接 OpenCV 库。
通常,我们需要包含以下头文件:
```cpp
#include <opencv2/opencv.hpp> // 包含所有核心和contrib模块
#include <iostream>
#include <vector> // 用于存储轮廓
为方便起见,我们也会使用 cv
和 std
命名空间:
using namespace cv;
using namespace std;
轮廓提取的步骤
轮廓提取通常涉及以下步骤:
- 加载图像:读取源图像。
- 预处理:
- 转换为灰度图:轮廓检测通常在灰度图像上进行。
- 高斯模糊 (可选):减少噪声,有助于获得更清晰的轮廓。
- 二值化:将灰度图像转换为二值图像(黑白图像)。这是
findContours
函数所必需的。常用的方法有简单阈值处理、自适应阈值处理或 Canny 边缘检测。
- 查找轮廓:使用
cv::findContours
函数。 - 绘制轮廓 (可选):使用
cv::drawContours
函数在图像上可视化找到的轮廓。
1. 加载图像和预处理
首先,我们加载图像并进行必要的预处理,将其转换为二值图像。
int main(int argc, char** argv) {
// 1. 加载图像
// const char* filename = argc >= 2 ? argv[1] : "shapes.png"; // 从命令行参数或默认读取
Mat src = imread("your_image.png", IMREAD_COLOR); // 请替换为您的图片路径
if (src.empty()) {
cout << "无法加载图像: " << "your_image.png" << endl;
return -1;
}
imshow("Original Image", src);
// 2. 转换为灰度图
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("Grayscale Image", gray);
// 3. (可选)高斯模糊以减少噪声
Mat blurred;
GaussianBlur(gray, blurred, Size(5, 5), 0); // 使用5x5的核
imshow("Blurred Image", blurred);
// 4. 二值化图像
// 方法一:使用阈值处理
Mat binary_thresh;
// threshold(blurred, binary_thresh, 100, 255, THRESH_BINARY_INV); // 根据图像调整阈值,THRESH_BINARY_INV使物体为白色,背景为黑色
// 或者使用Otsu's方法自动确定阈值
threshold(blurred, binary_thresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
imshow("Threshold Binary Image", binary_thresh);
// 方法二:使用Canny边缘检测(Canny的输出已经是二值图像)
Mat canny_output;
Canny(blurred, canny_output, 50, 150); // 调整低阈值和高阈值
imshow("Canny Edges", canny_output);
// 为 findContours 选择一个二值图像作为输入
// 通常,如果物体是实心的,阈值图像效果好;如果只关心边缘,Canny图也可以。
// 注意:findContours会修改输入的二值图像,所以如果之后还需要它,请传递一个副本。
Mat input_for_contours = binary_thresh.clone(); // 或者 canny_output.clone();
重要提示:cv::findContours
函数会修改输入的二值图像。如果您希望保留原始的二值图像,请传递它的一个副本(例如,使用 .clone()
)。
2. 查找轮廓 (cv::findContours
)
一旦有了二值图像,就可以使用 cv::findContours
函数来查找轮廓。
cv::findContours
函数原型:
void findContours( InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset = Point());
image
: 输入的单通道8位二值图像。此图像会被函数修改。contours
: 检测到的轮廓。每个轮廓都是一个std::vector<cv::Point>
。因此,contours
是一个std::vector<std::vector<cv::Point>>
。hierarchy
: 可选的输出向量 (std::vector<cv::Vec4i>
),包含图像的拓扑结构。每个元素hierarchy[i]
对应于contours[i]
,包含4个索引:[Next, Previous, First_Child, Parent]
。mode
: 轮廓检索模式。RETR_EXTERNAL
: 只检索最外层的轮廓。RETR_LIST
: 检索所有轮廓,但不建立任何层次关系(所有轮廓都在同一级别)。RETR_CCOMP
: 检索所有轮廓并将它们组织成两级层次结构:外部边界和内部孔洞边界。RETR_TREE
: 检索所有轮廓并重建完整的层次结构。
method
: 轮廓逼近方法。CHAIN_APPROX_NONE
: 存储轮廓上的所有点。CHAIN_APPROX_SIMPLE
: 压缩水平、垂直和对角线段,只保留它们的端点。例如,矩形的轮廓将只由4个点组成。CHAIN_APPROX_TC89_L1
,CHAIN_APPROX_TC89_KCOS
: 应用Teh-Chin链逼近算法中的一种。
offset
: 可选的轮廓点偏移量。如果您的轮廓是从图像的ROI中提取的,然后希望在整个图像上下文中分析它们,这将非常有用。
// ... 接上文 input_for_contours
vector<vector<Point>> contours; // 用于存储所有轮廓
vector<Vec4i> hierarchy; // 用于存储轮廓的层级信息
// 查找轮廓
findContours(input_for_contours, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
// 使用 RETR_EXTERNAL 如果你只想找最外层轮廓
// findContours(input_for_contours, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
cout << "找到的轮廓数量: " << contours.size() << endl;
3. 理解轮廓层级 (hierarchy
)
hierarchy
向量中的每个元素 Vec4i
对应于 contours
中的一个轮廓,并存储了4个整数:
hierarchy[i] = [Next_contour_idx, Previous_contour_idx, First_child_contour_idx, Parent_contour_idx]
Next_contour_idx
: 与当前轮廓在同一层级上的下一个轮廓的索引。Previous_contour_idx
: 与当前轮廓在同一层级上的上一个轮廓的索引。First_child_contour_idx
: 当前轮廓的第一个子轮廓的索引。Parent_contour_idx
: 当前轮廓的父轮廓的索引。
如果对应的轮廓不存在,则索引值为 -1。
例如,使用 RETR_TREE
可以帮助您理解嵌套的轮廓(例如,一个形状内部的孔洞)。
4. 绘制轮廓 (cv::drawContours
)
找到轮廓后,通常希望将它们可视化。cv::drawContours
函数用于此目的。
cv::drawContours
函数原型:
void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color,
int thickness = 1, int lineType = LINE_8,
InputArray hierarchy = noArray(), int maxLevel = INT_MAX,
Point offset = Point() );
image
: 要在其上绘制轮廓的目标图像(通常是原始彩色图像的副本或新的空白图像)。contours
: 所有找到的轮廓,从findContours
获得。contourIdx
: 指示要绘制哪个轮廓的参数。如果为负数(例如 -1),则绘制所有轮廓。color
: 轮廓的颜色。thickness
: 轮廓线的粗细。如果为负数或FILLED
,则轮廓内部被填充。lineType
: 线的连接类型 (例如LINE_8
,LINE_AA
抗锯齿线)。hierarchy
: 可选的层级信息,如果只想绘制特定层级的轮廓。maxLevel
: 仅当提供了hierarchy
时才考虑此参数。0 表示只绘制指定的轮廓,1 表示绘制指定轮廓及其所有子轮廓,依此类推。offset
: 可选的轮廓点偏移量。
// ... 接上文 contours 和 hierarchy
// 创建一个空白图像用于绘制轮廓,或在原图副本上绘制
Mat drawing = Mat::zeros(input_for_contours.size(), CV_8UC3); // 创建一个黑色的画布
// 或者在原图上绘制
// Mat drawing = src.clone();
for (size_t i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rand() % 256, rand() % 256, rand() % 256); // 随机颜色
// 绘制单个轮廓
// drawContours(drawing, contours, static_cast<int>(i), color, 2, LINE_8, hierarchy, 0);
// 或者绘制所有轮廓
}
// 绘制所有轮廓,使用一种颜色
drawContours(drawing, contours, -1, Scalar(0, 255, 0), 2, LINE_8, hierarchy, 100);
imshow("Contours", drawing);
5. 轮廓分析 (简介)
找到轮廓后,可以进行各种分析:
- 计算轮廓面积:
double area = contourArea(contours[i]);
- 计算轮廓周长:
double perimeter = arcLength(contours[i], true);
(true表示轮廓是闭合的) - 轮廓近似:
approxPolyDP(contours[i], approx_curve, epsilon, true);
将轮廓逼近为具有较少顶点的多边形。 - 获取边界框:
Rect bounding_rect = boundingRect(contours[i]);
- 获取最小外接圆:
Point2f center; float radius; minEnclosingCircle(contours[i], center, radius);
- 形状匹配, 凸包, 几何矩 等。
这些分析可以用于过滤轮廓(例如,只保留面积大于某个阈值的轮廓)或识别特定形状。
完整示例代码
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
// 1. 加载图像
const char* filename = "your_image.png"; // 请替换为您的图片路径
Mat src = imread(filename, IMREAD_COLOR);
if (src.empty()) {
cout << "无法加载图像: " << filename << endl;
return -1;
}
imshow("Original Image", src);
// 2. 转换为灰度图
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
// 3. 高斯模糊
Mat blurred;
GaussianBlur(gray, blurred, Size(5, 5), 0);
// 4. 二值化 (Otsu's 方法)
Mat binary_thresh;
threshold(blurred, binary_thresh, 0, 255, THRESH_BINARY | THRESH_OTSU); // 物体为白色,背景黑色
// 如果findContours需要白色物体在黑色背景上,请确保二值化正确
// 如果物体是黑色的,背景是白色的,可能需要 THRESH_BINARY_INV
// 或者确保Canny的输出是白色边缘在黑色背景上
imshow("Binary Image for Contours", binary_thresh);
// --- 查找轮廓 ---
// 注意:findContours会修改输入的二值图像binary_thresh,所以如果之后还需要它,请使用它的副本
Mat binary_copy = binary_thresh.clone();
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
// RETR_EXTERNAL 只查找最外层轮廓
// RETR_TREE 查找所有轮廓并建立完整的层级结构
findContours(binary_copy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
cout << "找到的轮廓数量: " << contours.size() << endl;
// --- 绘制轮廓 ---
// 创建一个彩色图像用于绘制轮廓 (在原始图像的副本上绘制)
Mat drawing = src.clone();
// 或者在一个新的黑色画布上绘制
// Mat drawing = Mat::zeros(binary_thresh.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++) {
Scalar color;
// 根据层级给内外轮廓不同颜色 (简单示例)
if (hierarchy[i][3] == -1) { // 最外层轮廓 (没有父轮廓)
color = Scalar(0, 0, 255); // 红色
} else {
color = Scalar(0, 255, 0); // 绿色 (内部轮廓)
}
drawContours(drawing, contours, static_cast<int>(i), color, 2, LINE_8, hierarchy, 0);
}
// 或者一次性绘制所有轮廓,用统一颜色
// drawContours(drawing, contours, -1, Scalar(255,0,0), 2);
imshow("Contours", drawing);
// --- 轮廓分析示例:过滤小轮廓并绘制其边界框 ---
Mat drawing_filtered = src.clone();
for (size_t i = 0; i < contours.size(); i++) {
double area = contourArea(contours[i]);
if (area > 100) { // 过滤掉面积小于100的轮廓
Scalar color = Scalar(rand() % 256, rand() % 256, rand() % 256);
drawContours(drawing_filtered, contours, static_cast<int>(i), color, 2);
Rect bounding_rect = boundingRect(contours[i]);
rectangle(drawing_filtered, bounding_rect.tl(), bounding_rect.br(), Scalar(0,255,255), 2);
}
}
imshow("Filtered Contours with Bounding Boxes", drawing_filtered);
cout << "按任意键退出..." << endl;
waitKey(0);
destroyAllWindows();
return 0;
}
编译与运行
假设您已正确安装 OpenCV,可以使用 g++ 编译上述代码:
g++ your_code_file.cpp -o contour_extraction $(pkg-config --cflags --libs opencv4)
./contour_extraction your_image.png
(如果 pkg-config --libs opencv4
不起作用,请根据您的 OpenCV 版本和安装方式调整链接器标志,例如 opencv
或特定模块如 opencv_core opencv_imgproc opencv_highgui opencv_imgcodecs
)。确保替换 "your_image.png"
为您要测试的图像路径。
总结
轮廓提取是计算机视觉中的一项基础且强大的技术。通过结合适当的预处理步骤和 cv::findContours
函数,您可以有效地从图像中分离出感兴趣的对象或区域的边界。cv::drawContours
使得可视化这些轮廓变得简单。此外,OpenCV 还提供了丰富的函数来分析这些轮廓的属性,为更高级的图像分析任务(如形状识别、对象跟踪等)奠定了基础。