实现C++ opencv提取轮廓并对轮廓进行平滑处理

实现C++ opencv提取轮廓并对轮廓进行平滑处理

提取轮廓

OpenCV是一个非常强大的计算机视觉库,可以帮助我们在C++中轻松地处理图像。为了实现提取轮廓并对轮廓进行平滑处理,您可以按照以下步骤进行操作:

首先,请确保您已经安装了OpenCV库。然后,创建一个新的C++项目,并在代码文件中包含所需的OpenCV头文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
// 1. 读取图片
Mat src = imread(argv[1], IMREAD_COLOR);
if (src.empty())
{
cout << "Error: Can't load image" << endl;
return -1;
}

// 2. 转换为灰度图像
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);

// 3. 二值化
Mat binary;
threshold(gray, binary, 100, 255, THRESH_BINARY);

// 4. 查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

// 5. 平滑轮廓(可选:使用approxPolyDP函数进行多边形逼近)
vector<vector<Point>> smoothed_contours(contours.size());
double epsilon = 2.0; // 控制平滑程度的参数,数值越大,平滑程度越高
for (size_t i = 0; i < contours.size(); i++)
{
approxPolyDP(contours[i], smoothed_contours[i], epsilon, true);
}

// 6. 绘制平滑后的轮廓
Mat result = src.clone();
for (size_t i = 0; i < smoothed_contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
drawContours(result, smoothed_contours, (int)i, color, 2, LINE_8, hierarchy, 0, Point());
}

// 7. 显示结果
imshow("Original Image", src);
imshow("Smoothed Contours", result);
waitKey(0);

return 0;
}


这段代码首先读取输入图片并将其转换为灰度图像。然后,它使用阈值进行二值化处理,以便于查找轮廓。接下来,代码使用findContours函数查找轮廓。为了平滑轮廓,我们使用approxPolyDP函数对轮廓进行多边形逼近。最后,将平滑后的轮廓绘制在原始图像上,并显示结果。

如果您对平滑轮廓的程度不满意,可以调整approxPolyDP函数中的epsilon参数。较大的epsilon值会使轮廓更平滑,而较小的值会保留更多的原始轮廓细节。您可以根据自己的需求进行调整。

平滑轮廓

此外,还可以尝试其他平滑轮廓的方法。例如,使用GaussianBlur对二值化图像进行模糊处理,然后再查找轮廓,可以使轮廓看起来更加平滑。以下是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ...

// 3. 二值化
Mat binary;
threshold(gray, binary, 100, 255, THRESH_BINARY);

// 3.1. 使用高斯模糊平滑二值化图像(可选)
Size ksize(5, 5); // 高斯滤波器大小,根据需要调整
GaussianBlur(binary, binary, ksize, 0, 0);

// 4. 查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

// ...


这种方法对于某些图像可能更有效,但对于其他图像可能效果不佳。您可以根据实际情况进行尝试。

如果您对轮廓平滑和处理有更多需求,可以尝试以下几种方法:

  1. 中值滤波器:

中值滤波器是一种非线性滤波器,它可以很好地消除噪声,同时保留边缘。您可以在查找轮廓之前对二值化图像应用中值滤波。

1
2
3
4
5
6
7
8
9
10
11
// 3. 二值化
Mat binary;
threshold(gray, binary, 100, 255, THRESH_BINARY);

// 3.1. 使用中值滤波器平滑二值化图像(可选)
int ksize = 5; // 滤波器大小,根据需要调整
medianBlur(binary, binary, ksize);

// 4. 查找轮廓
// ...

  1. 形态学操作:

形态学操作,如膨胀和腐蚀,可以帮助您填补轮廓中的空洞或消除不连续区域。在查找轮廓之前,您可以对二值化图像进行形态学操作。

1
2
3
4
5
6
7
8
9
10
11
// 3. 二值化
Mat binary;
threshold(gray, binary, 100, 255, THRESH_BINARY);

// 3.1. 应用形态学操作(可选)
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
morphologyEx(binary, binary, MORPH_CLOSE, kernel); // 闭运算:先膨胀,后腐蚀

// 4. 查找轮廓
// ...

  1. 轮廓筛选:

根据轮廓的面积、周长、形状等属性筛选出您感兴趣的轮廓。这可以使结果更符合预期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ...
// 5. 筛选轮廓(可选)
vector<vector<Point>> filtered_contours;
for (size_t i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > min_area && area < max_area) // 根据轮廓面积进行筛选
{
filtered_contours.push_back(contours[i]);
}
}

// 6. 平滑筛选后的轮廓
// ...

选取提取的轮廓面积最大的轮廓

为了在C++中使用OpenCV库选取提取的轮廓中面积最大的轮廓,可以在找到所有轮廓之后,遍历它们并计算每个轮廓的面积,然后选择具有最大面积的轮廓。以下是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
// 1. 读取图片
Mat src = imread(argv[1], IMREAD_COLOR);
if (src.empty())
{
cout << "Error: Can't load image" << endl;
return -1;
}

// 2. 转换为灰度图像
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);

// 3. 二值化
Mat binary;
threshold(gray, binary, 100, 255, THRESH_BINARY);

// 4. 查找轮廓
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

// 5. 找到面积最大的轮廓
int largest_contour_index = 0;
double largest_area = 0;
for (size_t i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > largest_area)
{
largest_area = area;
largest_contour_index = i;
}
}

// 6. 绘制面积最大的轮廓
Mat result = src.clone();
Scalar color = Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
drawContours(result, contours, largest_contour_index, color, 2, LINE_8, hierarchy, 0, Point());

// 7. 显示结果
imshow("Original Image", src);
imshow("Largest Contour", result);
waitKey(0);

return 0;
}


这段代码首先读取输入图片并将其转换为灰度图像。接着进行二值化处理,然后查找轮廓。遍历找到的所有轮廓并计算它们的面积。记录最大面积及其对应的轮廓索引。最后将具有最大面积的轮廓绘制在原始图像上,并显示结果。


实现C++ opencv提取轮廓并对轮廓进行平滑处理
https://qiangsun89.github.io/2023/04/20/实现C-opencv提取轮廓并对轮廓进行平滑处理/
作者
Qiang Sun
发布于
2023年4月20日
许可协议