第8章 图像轮廓与图像分割修复
8.1 查找并绘制轮廓
8.1.1 寻找轮廓:findContours()函数
1.作用:在二值图像中寻找轮廓
2.函数原型:
void findcontours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
3.参数说明:
(1)输入图像,8位单通道图像,图像非零像素被保留为0,所以图像为二进制,可以使用cmopare()、inrange()、threshold()、adaptivethreshold()、canny()等函数由灰度图或彩色图创建二进制图像
(2)检测到的轮廓,每个轮廓存储为一个点向量,即用point类型的vector表示
(3)可选的输出向量,包含图像的拓扑信息,作为轮廓数量的表示,每个轮廓contours[i]对应4个hierarchy元素,contours[i][0]~contours[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,对应的hierarchy[i]值设为负数
(4)轮廓检索模式,取值:
(5)轮廓近似办法,取值:
(6)每个轮廓点的可选偏移量,默认值Point()
4.示例:
vector<vector<Point>>contours;
findContours(image,contours,RETR_EXTERNAL,CHAIN_APPROX_NONE);
8.1.2 绘制轮廓:drawContours()函数
1.作用:用于在图像中绘制外部或内部轮廓
2.函数原型:
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point())
3.参数说明:
(1)目标图像
(2)输入轮廓
(3)轮廓绘制指示变量,负值表示绘制所有轮廓
(4)轮廓颜色
(5)轮廓线条粗细度,默认值1,负值会绘制在轮廓内部
(6)线条类型,默认8,可取值:
(7)可选的层次结构信息,默认值noArray()
(8)用于绘制轮廓的最大等级,默认值INT_MAX
(9)可选的轮廓偏移参数,默认Point()
4.示例:
Mat result(image.size(),CV_8U,cv::Scalar(255));
drawContours(result,contours,-1,Scalar(0),3,8);
8.1.3 示例程序
1.查找并绘制轮廓示例
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
//以二值图模式载入并显示原图
Mat srcImage = imread("girl.jpg",0);
if (!srcImage.data)
{
printf("图片载入失败~!\n");
return false;
}
imshow("【原始图】", srcImage);
//srcImage取大于阈值119的部分
srcImage = srcImage > 119;
imshow("取阈值后的原始图", srcImage);
//初始化结果图
Mat dstImage;
dstImage=Mat::zeros(srcImage.rows,srcImage.cols, CV_8UC3);
//定义轮廓和层次结构
vector<vector<Point>>contours;
vector<Vec4i>hierarchy;
//查找轮廓
findContours(srcImage, contours, hierarchy,RETR_CCOMP, CHAIN_APPROX_SIMPLE);
//遍历所有顶层轮廓,以随机颜色绘制每个连接组件颜色
for (int index = 0; index >= 0; index = hierarchy[index][0])//令index等于当前轮廓的下一个轮廓来更新循环,没有下一轮廓时,hierarchy[i][0]为负值,循环结束
{
Scalar color(rand() & 255, rand() & 255, rand() & 255);
drawContours(dstImage, contours, index,color, FILLED, 8,hierarchy);
}
//显示轮廓图
imshow("【轮廓图】", dstImage);
waitKey(0);
return 0;
}
运行效果:
2.综合示例
/*
效果:利用图像平滑和边缘检测,根据滑动条调节,动态检测图形轮廓
*/
#include<opencv2/opencv.hpp>
#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,g_grayImage,g_cannyMat_output,g_dstImage;
int g_nThresh = 80;
int g_nThresh_max = 255;
RNG g_rng(12345);
vector<vector<Point>>g_vContours;
vector<Vec4i>g_vHierarchy;
//全局函数
static void ShowHelpText();
static void on_ThreshChange(int, void*);
int main()
{
//改变console字体颜色
system("color 1F");
ShowHelpText();
//载入原图
g_srcImage = imread("girl.jpg", 1);
if (!g_srcImage.data)
{
printf("图片载入失败~!\n");
return false;
}
//创建窗口
namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE);
imshow(WINDOW_NAME1, g_srcImage);
//转成灰度图并模糊化降噪
cvtColor(g_srcImage, g_grayImage, CV_BGR2GRAY);
blur(g_grayImage, g_grayImage, Size(3, 3));
//创建滚动条并初始化
createTrackbar("canny阈值", WINDOW_NAME2, &g_nThresh, g_nThresh_max, on_ThreshChange);
on_ThreshChange(0, 0);
waitKey(0);
return 0;
}
static 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 g_dstImage = Mat::zeros(g_srcImage.rows, g_srcImage.cols, CV_8UC3);
//或 for (int index = 0; index >= 0; index = g_vHierarchy[index][0])
for (int index = 0; index < g_vContours.size(); index++)
{
Scalar color(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));//任意值
drawContours(g_dstImage, g_vContours, index, color, 2, 8, g_vHierarchy, 0, Point());
}
//显示效果图
imshow(WINDOW_NAME2, g_dstImage);
}
static void ShowHelpText()
{
printf("\n\n\t欢迎来到【在图形中寻找轮廓】示例程序!\n\n");
printf("\n\n\t操作说明:\n\n");
printf("\t\t键盘任意键-退出程序\n\n");
printf("\t\t滑动滚动条-改变canny阈值\n");
}
运行效果: