开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天
1 查找并绘制轮廓
一个轮廓一般对应一系列的点,也就是图像中的一条曲线。其表示方法可能根据不同的情况而有所不同。在OpenCV中,可以用findContours()函数从二值图像中查找轮廓.
2 寻找轮廓:findContours0函数
findContours()函数用于在二值图像中寻找轮廓。
C++:
void findContours(InputOutputArray image, OutputArrayofArrays contours, OutputArray hierarchy, int mode,int method, Point offset=Point());
- 第一个参数,InputArray类型的image,输入图像,即源图像,填Mat类的对象即可,且需为8位单通道图像。图像的非零像素被视为1,0像素值被保留为0,所以图像为二进制。我们可以使用 compare()、inrange()、threshold()、adaptivethreshold()、cannyO等函数由灰度图或彩色图创建二进 制图像。此函数会在提取图像轮廓的同时修改图像的内容。
- 第二个参数,OutputArrayOfArrays 类型的 contours、检测到的轮廓、函数 调用后的运算结果存在这里。每个轮廓存储为一个点向量,即用point类型的vector表示。
- 第三个参数,OutputArray类型的hierarchy,可选的输出向量,包含图像的拓扑信息。其作为轮廓数量的表示,包含了许多元素。每个轮廓 contours[i]对应4个hierarchy元素hierarchy[i][0]~hierarchy[i][3],分别表示后一 个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果没有对应项,对应的hierarchy[i]值设置为负数。
- 第四个参数,int类型的mode,轮廓检索模式,取值如表所示。
- 第五个参数,int类型的method,为轮廓的近似办法,取值如表所示。
- 第六个参数,Point 类型的 offset,每个轮廓点的可选偏移量,有默认值Point()。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数便可排上用场。
findContours 经常与drawContours配合使用一使用用 findContours()函数检测 到图像的轮廓后,便可以用drawContours()函数将检测到的轮廓绘制出来。接下来,让我们一起看看drawContoursO函数的用法。
3 绘制轮廓:drawContours()函数
drawContours()函数用于在图像中绘制外部或内部轮廓。
C++:
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())
- 第一个参数,InputArray类型的image,目标图像,填Mat类的对象即可。
- 第二个参数,InputArrayOfArrays类型的 contours,所有的输入轮廓。每个 轮廓存储为一个点向量,即用point类型的vector表示。
- 第三个参数,int类型的contourldx,轮廓绘制的指示变量。如果其为负值,则绘制所有轮廓。
- 第四个参数,const Scalar&类型的color,轮廓的颜色。
- 第五个参数,int thickness,轮廓线条的粗细度,有默认值1。如果其为负值(如thickness= cv_filled),便会绘制在轮廓的内部。可选为FILLED宏 (OpenCV2版为CV_FILLED)。
- 第六个参数,int类型的lineType,线条的类型,有默认值8。取值类型如表所示。
- 第七个参数,InputArray 类型的 hierarchy,可选的层次结构信息,有默认值noArray0)。
- 第八个参数,int类型的maxLevel,表示用于绘制轮廓的最大等级,有默认值INT_MAX。
- 第九个参数,Point 类型的 offset,可选的轮廓偏移参数,用指定的偏移量offset=(dx,dy)偏移需要绘制的轮廓,有默认值Point()。
4 示例
代码:
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//---------------------------------------------------------------------------------------
#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");
// 加载源图像
g_srcImage = imread( "1.jpg", 1 );
if(!g_srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }
// 转成灰度并模糊化降噪
cvtColor( g_srcImage, g_grayImage, CV_BGR2GRAY );
blur( g_grayImage, g_grayImage, Size(3,3) );
// 创建窗口
namedWindow( WINDOW_NAME1, CV_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, CV_RETR_TREE, CV_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 );
}
效果图:
原图
效果图