轮廓
Opencv实战系列,前文:
文章目录
- 轮廓
- (1).查找绘制
- 1.findContours()
- 2.drawContours()
- (2).层级结构
- (3).筛选轮廓
- (4).凸包
(1).查找绘制
预处理:
- 灰度化:使用cv::cvtColor()
- 图像去噪:使用高斯滤波cv::Gaussian()
- 二值化:使用cv::Threshold()
- 形态学处理:cv::morphologyEx()
其中灰度化可以将3通道图像转化为单通道图像,以便进行二值化门限分割;去噪可以有效剔除图像中的异常独立噪点;二值化是为轮廓查找函数提供单通道图像;形态学的某些处理通常可以剔除细小轮廓,联通断裂的轮廓。
1.findContours()
void cv::findContours(InputOutputArray image,OutputArrayOfArrays contours,OutputArray hierarchy,int mode,int method, Point offset = Point())
- contours : 输出:检测到的轮廓。由若干个cv::Point类型的点组成了单个轮廓std :: vector < cv :: Point >,再由若干个轮廓组成输入图像中的全部轮廓std::vector<std :: vector < cv :: Point >>
- hierarchy : 输出:轮廓级别信息。Hierarchy为可选输出变量,是std::vector< cv::Vec4i>类型的向量(每个元素都是一个4个int值构成的向量)。它具有与轮廓数量一样多的元素。例如,第i个轮廓, hierarchy[i][0],hierarchy[i][0],hierarchy[i][2]和hierarchy[i][3]依次为:第i个轮廓的[Next, Previous, First_Child, Parent],即轮廓i相同等级的下一轮廓、前一轮廓,第一个子轮廓和父轮廓(上一级轮廓)的索引号(即contours向量中的轮廓序号)。如果轮廓i没有下一个,前一个,父级或嵌套轮廓,则层次结构[i]的相应元素将为负数。
2.drawContours()
void cv::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() )
- contouridx : 待绘制轮廓序号
(2).层级结构
OpenCV中每个轮廓都有自己的信息,关于它是什么层次结构,谁是它的子轮廓,谁是它的父轮廓等.OpenCV将它表示为四个int值的数组,类型为cv::Vec4i(4个int值):
[Next,Previous,First_Child,Parent]
hierarchy
数组在OpenCV中是一个四维数组,hierarchy数组的四维结构为
[轮廓索引, 轮廓信息, 关系信息]
- Next
Next表示同一级别的下一个轮廓索引。例如,在我们的图片中取出轮廓-0。同一水平的下一个轮廓是轮廓-1。 所以简单地说Next = 1。类似地,对于轮廓-1,next是轮廓-2。 所以Next = 2。
轮廓-2的同一级别没有下一个轮廓,所以轮廓-2的Next = -1。轮廓-4呢?它与轮廓-5处于同一水平。所以它的下一个轮廓是轮廓-5,所以轮廓-4的Next = 5。
- Previous
Previous表示同一级别的上一个轮廓索引。例如,轮廓-1的上一个轮廓在同一级别中为轮廓-0。 类似地,对于轮廓-2,它的上一个轮廓是轮廓-1。而对于轮廓-0,没有先前的,所以把它的Previous = -1。
- First_Child
First_Child表示当前轮廓的第一个子轮廓索引。例如,对于轮廓-2,子轮廓是轮廓-2a。因此轮廓-2的First_Child为轮廓-2a的相应索引值。轮廓-3a呢?它有两个子轮廓。但hierarchy参数只记录第一个子轮廓,因此它是轮廓-4的索引值。因此,对于轮廓-3a,First_Child = 4。
- Parent
Parent表示当前轮廓的父轮廓索引。对于轮廓-4和轮廓-5,它们的父轮廓都是轮廓-3a。对于轮廓-3a,它的父轮廓是轮廓-3,依此类推。
(3).筛选轮廓
首次查找轮廓有许多中间有空洞的轮廓不符合要求,下面就通过遍历每一个轮廓的hierarchy级别参数的第3第4个参数来找到那些有子轮廓或者有父轮廓的轮廓,并删除之。注意向量迭代器的使用,删除后会返回下一个向量的指针;此外,contours与hierarchy元素需要同步删除和并递增迭代器,以保持编号对应关系,否则会删错。
std::vector<std::vector<cv::Point>>::iterator itc = contours.begin();
std::vector<cv::Vec4i>::iterator itc_hierarchy = hierarchy.begin();
int i = 0;
while(itc_hierarchy != hierarchy.end())
{if (hierarchy[i][2] > 0 || hierarchy[i][3] > 0){itc = contours.erase(itc);itc_hierarchy = hierarchy.erase(itc_hierarchy);}else{++i;++itc;++itc_hierarchy;}
}
(4).凸包
凸包外观看起来与轮廓逼近相似,但并非如此(在某些情况下两者可能提供相同的结果)。一般而言,凸曲线是始终凸出或至少平坦的曲线。如果在内部凸出,则称为凸度缺陷。例如,检查下面的手的图像。红线显示手的凸包。双向箭头标记显示凸度缺陷,这是船体与轮廓线之间的局部最大偏差。
void cv::convexHull (InputArray points,OutputArray hull,bool clockwise = false,bool returnPoints = true )
- clockwise:方向标记。如果为True,则输出凸包为顺时针方向。否则,其方向为逆时针方向。
- returnPoints:默认情况下为True。然后返回船体点的坐标。如果为False,则返回与船体点相对应的轮廓点的索引。