OpenCV4入门系列教程82:图像轮廓发现

索引地址:系列教程索引地址

上一篇:OpenCV4入门系列教程81:强度变换

轮廓即是以某种方式表示图像中的曲线的点的列表。这种表示可以根据实际的情形不同而不同。表示一条曲线的方式有很多种。OpenCV中,轮廓是由STL风格的vector<>模板对象表示的,其中vector中的每个元素都编码了曲线上的下一点的位置信息。

轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法。所以边缘提取的阈值选定会影响最终轮廓发现结果。

查找的轮廓后,可以涉及树形层次、编码方式、最小外接矩形、面积与周长

API介绍

findContours 发现轮廓

用于查找二值图像中的轮廓。

该函数使用算法@cite Suzuki85从二值图像中检索轮廓。轮廓是用于形状分析以及对象检测和识别的有用工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void findContours(
InputArray image,
OutputArrayOfArrays contours,
OutputArray hierarchy,
int mode,
int method,
Point offset = Point()
);


void findContours(
InputArray image,
OutputArrayOfArrays contours,
int mode,
int method,
Point offset = Point()
);

参数

  • image 原图像,一个8位的单通道图像。非零像素被认为为1,0像素为任然保留为0。所以图像被当做二值图像。您可以使用compare、inRange、threshold、adaptiveThreshold、Canny等来创建灰度或彩色的二值图像。如果mode等于RETR_CCOMP或RETR_FLOODFILL,则输入也可以是32位整数的标签图像(CV_32SC1)。
  • contours 检测到的轮廓。每个轮廓都存储为点向量(例如std :: vector)。
  • hierarchy 可选的输出向量(例如std :: vector ),包含有关图像拓扑的信息。它具有与轮廓数量一样多的元素。对于每个第i个轮廓轮廓[i],元素等级[i] [0],等级[i] [1],等级[i] [2]和等级[i] [3]均设置为0-在相同的层次级别上,基于下一个和上一个轮廓的轮廓的索引,分别是第一个子轮廓和父轮廓。如果对于轮廓i,没有下一个,上一个,父级或嵌套的轮廓,则hierarchy [i]的相应元素将为负。
  • mode 轮廓检索模式,请参阅RetrievalModes。
  • method 轮廓近似方法,请参见ContourApproximationModes
  • offset 每个轮廓点移动的可选偏移量。如果从图像ROI中提取轮廓,然后在整个图像上下文中对其进行分析,这将非常有用。

drawContours 绘制轮廓

绘制轮廓或者填充轮廓。

1
2
3
4
5
6
7
8
9
10
11
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 所有输入轮廓,每个轮廓都存储为点向量。
  • contourIdx 指示要绘制轮廓的参数。若为负,则绘制所有轮廓。
  • color 轮廓的颜色。
  • hickness 绘制轮廓的线的粗细。如果为负数,则绘制轮廓内部。
  • lineType 表示线型。
  • hierarchy 表示有关层次结构的可选信息。
  • maxLevel 表示绘制轮廓的最大级别。 如果为0,则仅绘制指定的轮廓。 如果为1,则该函数绘制轮廓和所有嵌套轮廓。 如果为2,则该函数绘制轮廓,所有嵌套轮廓,所有嵌套到嵌套的轮廓,等等。 仅当有可用的层次结构时才考虑此参数。
  • offset 表示可选的轮廓偏移参数,该参数可按指定的方式移动所有绘制的轮廓。

测试代码

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
//--------------------------------------【程序说明】-------------------------------------------
// 程序说明:《OpenCV3编程入门》OpenCV3版书本配套示例程序69
// 程序描述:基础轮廓查找——findContours+drawContours
// 2014年11月 Created by @浅墨_毛星云
// 2014年12月 Revised by @浅墨_毛星云
//------------------------------------------------------------------------------------------------

//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

//-----------------------------------【main( )函数】--------------------------------------------

// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-------------------------------------------------------------------------------------------------
int main(int argc, char **argv) {
// 【1】载入原始图,且必须以二值图模式载入
Mat srcImage = imread("1.jpg", 0);
imshow("原始图", srcImage);

//【2】初始化结果图
Mat dstImage = Mat::zeros(srcImage.rows, srcImage.cols, CV_8UC3);

//【3】srcImage取大于阈值119的那部分
srcImage = srcImage > 119;
imshow("取阈值后的原始图", srcImage);

//【4】定义轮廓和层次结构
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;

//【5】查找轮廓
//此句代码的OpenCV2版为:
// findContours( srcImage, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
//此句代码的OpenCV3版为:
findContours(srcImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

// 【6】遍历所有顶层的轮廓, 以随机颜色绘制出每个连接组件颜色
int index = 0;
for (; index >= 0; index = hierarchy[ index ][ 0 ]) {
Scalar color(rand() & 255, rand() & 255, rand() & 255);
//此句代码的OpenCV2版为:
// drawContours( dstImage, contours, index, color, CV_FILLED, 8, hierarchy );
//此句代码的OpenCV3版为:
drawContours(dstImage, contours, index, color, FILLED, 8, hierarchy);
}

//【7】显示最后的轮廓图
imshow("轮廓图", dstImage);

waitKey(0);
}

综合测试

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//--------------------------------------【程序说明】-------------------------------------------
// 程序说明:《OpenCV3编程入门》OpenC3版书本配套示例程序70
// 程序描述:查找并绘制轮廓综合示例
// 2014年11月 Created by @浅墨_毛星云
// 2014年12月 Revised by @浅墨_毛星云
//------------------------------------------------------------------------------------------------

//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;

//-----------------------------------【宏定义部分】--------------------------------------------
// 描述:定义一些辅助宏
//------------------------------------------------------------------------------------------------
#define WINDOW_NAME1 "【原始图窗口】" //为窗口标题定义的宏
#define WINDOW_NAME2 "【轮廓图】" //为窗口标题定义的宏

//-----------------------------------【全局变量声明部分】--------------------------------------
// 描述:全局变量的声明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage;
Mat g_grayImage;
int g_nThresh = 80;
int g_nThresh_max = 255;
RNG g_rng(12345);
Mat g_cannyMat_output;
vector<vector<Point>> g_vContours;
vector<Vec4i> g_vHierarchy;

//-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数的声明
//-----------------------------------------------------------------------------------------------
static void ShowHelpText();
void on_ThreshChange(int, void *);

//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main(int argc, char **argv) {
//【0】改变console字体颜色
system("color 1F");

//【0】显示欢迎和帮助文字
ShowHelpText();

// 加载源图像
g_srcImage = imread("1.jpg", 1);
if (!g_srcImage.data) {
printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n");
return false;
}

// 转成灰度并模糊化降噪
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
blur(g_grayImage, g_grayImage, Size(3, 3));

// 创建窗口
namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
imshow(WINDOW_NAME1, g_srcImage);

//创建滚动条并初始化
createTrackbar("canny阈值", WINDOW_NAME1, &g_nThresh, g_nThresh_max, on_ThreshChange);
on_ThreshChange(0, 0);

waitKey(0);
return (0);
}

//-----------------------------------【on_ThreshChange( )函数】------------------------------
// 描述:回调函数
//----------------------------------------------------------------------------------------------
void on_ThreshChange(int, void *) {

// 用Canny算子检测边缘
Canny(g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh * 2, 3);

// 寻找轮廓
findContours(g_cannyMat_output, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE,
Point(0, 0));

// 绘出轮廓
Mat drawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3);
for (int i = 0; i < g_vContours.size(); i++) {
Scalar color =
Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255)); //任意值
drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point());
}

// 显示效果图
imshow(WINDOW_NAME2, drawing);
}

//-----------------------------------【ShowHelpText( )函数】----------------------------------
// 描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
static void ShowHelpText() {
//输出欢迎信息和OpenCV版本
printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
printf("\n\n\t\t\t此为本书OpenCV3版的第70个配套示例程序\n");
printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION);
printf("\n\n ----------------------------------------------------------------------------\n");

//输出一些帮助信息
printf("\n\n\t欢迎来到【在图形中寻找轮廓】示例程序~\n\n");
printf("\n\n\t按键操作说明: \n\n"
"\t\t键盘按键任意键- 退出程序\n\n"
"\t\t滑动滚动条-改变阈值\n");
}

下一篇:OpenCV4入门系列教程83:图像连通组件分析(中心位置、外接矩形)