声明:在寻找图像轮廓之前需要对图像进行阈值分割或者Canny、拉普拉斯等边缘检测算子处理。
寻找轮廓的算子:
findContours(InputOutputArray image, OutputArrayOfArrays contours,OutputArray hierarchy, int mode, int method, Point offset = Point());
(1)单通道图像,可以是灰度图,常用的是二值图像;
(2)contours是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素。其定义为“vector
(3)hierarchy也是一个向量,向量内每个元素保存了一个包含4个int整型的数组。其定义为“vector
hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第 i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果当前轮廓没有对应的后一个轮廓、前一个轮廓、父轮廓或内嵌轮廓的话,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为 默认值-1。
(4)轮廓的检索方式,opencv提供了四种方法,解释如下:
CV_RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略;
CV_RETR_LIST:检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所 以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到;
CV_RETR_CCOMP: 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层;
CV_RETR_TREE: 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
(5)轮廓的近似方法,opencv提供了四种方法,解释如下:
CV_CHAIN_APPROX_NONE:保存物体边界上所有连续的轮廓点到contours向量内;
CV_CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留;
CV_CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法;
CV_CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法。
(6)Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值!
详细解释可参考: findContours函数参数详解
绘制轮廓的算子:
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() );
(1)目标图像;
(2)所有的输入轮廓。每个轮廓都被存储为一个点向量,其定义与findContours的参数2相同;
(3)指示要绘制哪条轮廓的索引数。如果它是负的,就画出所有的轮廓线;
(4)指定绘制轮廓的颜色;
(5)指定轮廓线的粗细。如果它是负的,则绘制轮廓内部(经测试,为负值情况下轮廓内部完全被涂成设定的轮廓线颜色);
(6)指定轮廓线的类型,参考LineTypes,一般设定为8,即LINE_8;
(7)层次结构的可选信息。只在你想画某些指定的轮廓的时候才需要进行设置;
(8)绘制轮廓的最大数量。如果它是0,则只绘制指定的轮廓线。如果为1,则绘制轮廓线和所有嵌套轮廓线。如果它是2,则绘制轮廓线、所有嵌套轮廓线、所有嵌套到嵌套轮廓线等。该参数只有在存在层次结构时才被考虑在内。(与轮廓检索方式即findContours的参数4有关)
(9)轮廓点的偏移量。
说明:形状轮廓重心坐标的提取主要是利用图像的矩来计算。
什么是图像的矩?把图像的像素看做密度函数f(x,y),对该像素点求期望E,即是图像的矩(原点矩)。公式如下:
原点矩:
图像的矩主要表征了图像区域的几何特征,又称几何矩,由于具有旋转、平移、尺度等不变的特兴奋,所以又称为不变矩。一般来说,一阶矩和零阶矩可以计算某个形状的重心,二阶矩可以计算形状的方向。
关于矩以及图像矩的详细解释可以参考:矩、中心矩、质心、patch方向
提取重心坐标方法:
//声明一个图像的矩
Moments M;
//计算要绘制轮廓的矩
M = moments(contours[i]);
//求取轮廓重心的X坐标
double cX = double(M.m10 / M.m00);
//求取轮廓重心的Y坐标
double cY = double(M.m01 / M.m00);
关于m10、m01、m00的解释:
关于重心坐标的求解过程的详细解释可以参考: 中心距求解
示例代码:
Mat img = imread("C:\\Users\\SUNSONG\\Desktop\\图片\\Image_02.PNG");
Mat gray_img, thresh_img;
//灰度
cvtColor(img, gray_img, COLOR_BGR2GRAY);
threshold(gray_img, thresh_img, 0, 255, THRESH_TRIANGLE);
//开运算
Mat ellipse = getStructuringElement(MORPH_ELLIPSE, Size(13, 13));
morphologyEx(thresh_img, thresh_img, MORPH_OPEN, ellipse, Point(-1, -1), 2);
************以上为图像处理过程****************************************
//寻找轮廓
vector> contours;
vector hierarchy1;
findContours(thresh_img, contours, hierarchy1, RETR_LIST, CHAIN_APPROX_NONE, Point());
//获取某一轮廓重心点
Moments M;
M = moments(contours[0]);
double cX = double(M.m10 / M.m00);
double cY = double(M.m01 / M.m00);
//绘制轮廓
drawContours(img, contours, 0, Scalar(0, 255, 0), 2, 8, hierarchy1);
//显示轮廓重心并提取坐标点
circle(img, Point2d(cX, cY), 1, Scalar(0, 255, 0), 2, 8);
putText(img, "center", Point2d(cX - 20, cY - 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1, 8);
cout << "重心坐标:" << cX << " " << cY << endl << endl;
运行结果:
遇到的问题:
在寻找边界过程中,findContours算子寻找的轮廓中总是包含图像的整体轮廓(如下图所示),给寻找边界带来了很大干扰,目前还不知如何解决这个问题?????