本模块函数通过提取图像的边缘轮廓以及检测轮廓的几何形状识别图像中的可视模式。首先通过滤波函数将图像处理成可提取轮廓的边缘显示图,然后通过findconture函数提取图像中的轮廓并给出轮廓的嵌套关系(树)。而后就可以使用本模块中给出的函数对轮廓进行分析和检测,寻找可视轮廓的几何形状,以及识别图像中的指定模式。
在结构分析中,所有算法都采用了矩(Moments)的概念,由Moment类进行定义,包含空间矩,中心矩和归一化中心矩,并从这些矩中计算出多边形的不变矩Humoment。
cv::moment类
Moments ();//默认构造函数
Moments (double m00, double m10, double m01, double m20, double m11, double m02, double m30, double m21, double m12, double m03);//全构造函数
这是一个由vc::moment函数返回的结构。其中空间矩由下述公式计算:
从公式可知,对于二值图像,m00即为轮廓的面积(因为i=j=0,array(x,y)=像素值,二值图像则为1/0)。对于一般图像(灰度图):
m00 ---目标区域的质量(0阶矩)
m10,m01 ---目标区域的质心(1阶矩)
m02,m11,m20 ---目标区域的旋转半径(2阶矩)
m03,m12,m21,m30 ---目标区域的方位和斜度,反映目标的扭曲程度(3阶矩)
中心矩由下述公式计算:
其中:
为使矩具有平移不变形,利用质心坐标构造中心矩(保持轮廓的平移不变形)。
归一化中心矩由下述公式计算:
为抵消比例变化对中心矩的影响,用0阶中心矩对其余各阶中心矩进行归一化处理。
注意
mu00=m00,nu00=1,nu10=mu10=mu01=0,这些值没有存储在结构中。
上述定义的矩适合于图像,而轮廓的矩也是用相同的方法定义,但是计算则使用格林公式,由于受限于扫描分辨率,轮廓的矩在计算上与计算相同的栅格化轮廓稍有不同。
注意:由于轮廓的矩使用格林公式计算,对于自交叉轮廓形状有可能会获得奇怪的结果,例如蝴蝶形状的轮廓。
(1)、Moments cv::moments(InputArray array, bool binaryImage = false );
计算多边形或栅格形状直到3阶的全部矩。这个函数计算矢量形状或栅格形状的矩,直到3阶为止。结果在返回的cv::Moments结构中。
参数
array | 光栅图像(单通道,8-位或浮点2D数组)或2D点(Point or Point2f)数组(1×N或N×1)。 |
binaryImage | 如果为真,所有非0图像像素作为1。这个参数仅作用于图像。 |
返回:Moments。
(2)、void cv::findContours(InputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode, int method, Point offset = Point());
void cv::findContours(InputArray image,OutputArrayOfArrays contours,int mode,
int method, Point offset = Point());
查找二值图像的轮廓。这个函数使用算法(Suzuki算法)遍历二值图像的轮廓。轮廓是形状分析和目标检测与识别的有用工具。参见OpenCV示例目录下的squares.cpp源码。
注意:在opencv 3.2版本后,这个函数不再修改源图像。
参数
image | 源图像,8-位单通道图像。非0像素作为1,0像素保持0,图像处理成二值图。也可以使用compare,inRange,threshold,adaptiveThreshold,Canny,和其它函数从灰度或彩色图建立二值图。如果mode等于RETR_CCOMP或RETR_FLOODFILL,这个输入图像也可以是标志为(CV_32SC1)的32-位整数图像。 |
contours | 检测到的轮廓。每一个轮廓都存储成点矢量(数组)(即. std::vector<std::vector<cv::Point> >)。 |
hierarchy | 可选输出矢量(即. std::vector<cv::Vec4i>),包含轮廓相关的拓扑信息。其长度与contours一样长。对应每一个contours[i],元素hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],和hierarchy[i][3] 被设置为轮廓索引,分别是同层级的后一个轮廓,前一个轮廓,第一个子轮廓和父轮廓。如果轮廓i没有后一个轮廓,前一个轮廓,父轮廓,或子轮廓则对应的hierarchy[i]元素为负值。 |
mode | 轮廓的遍历模式,见RetrievalModes |
method | 轮廓逼近方法,见ContourApproximationModes |
offset | 可选的偏移值,每一个轮廓点的位移量。对于从图像ROI抽取轮廓并进行整个图像的关联分析,这个参数是有用的。 |
注:RetrievalModes包含:
RETR_EXTERNAL 只返回最外层contour(除边界contour外)。
RETR_LIST 返回所有contour,安查找顺序列表给出,没有层次关系。
RETR_CCOMP 返回两层结构层次关系的contour,对于多层结构也都以两层形式表示 (第三层变为外层,第四层为二层…)。
RETR_TREE 树结构表示contours的层次关系。
RETR_FLOODFILL 没有解释,一种用于计算contour的填充法,“漫灌填充”法,从左上 到右下对角式填充。
注:ContourApproximationModes包含:(contour的拟合方法)
CHAIN_APPROX_NONE 没有拟合
CHAIN_APPROX_SIMPLE 仅对直线段进行拟合(水平、垂直、斜线)
CHAIN_APPROX_TC89_L1 使用 Teh-Chin chain拟合算法
CHAIN_APPROX_TC89_KCOS 使用 Teh-Chin chain拟合算法
(3)、void cv::HuMoments (const Moments &moments, double hu[7]);
void cv::HuMoments (const Moments &m, OutputArray hu);
计算7个Hu不变性参数。
这个函数计算7个Hu 不变形参数,定义为:
其中ηji 表示Moments::nuji 。
这些值提供了图像在比例,旋转,映射时的不变性特征,除第7个参数在映射时有符号变化外。这个不变性特征是假定在无限图像分辨率条件下证明的。对于栅格图像原始图像和变换图像的Hu不变性计算稍有不同。
参数
moments | 输入的图像矩由vc::moments函数计算. |
hu | 输出的Hu不变性特征值. |
参见:matchShapes
(4)、void cv::approxPolyDP (InputArray curve, OutputArray approxCurve,
double epsilon, bool closed);
用特定的精度逼近多边形曲线。这个函数使用另一条具有较少顶点的曲线或多边形逼近给定曲线/多边形,求得两者之间的距离小于或等于给定的精度。这个函数使用道格拉斯-普克算法(Douglas-Peucker,简称DP)。
参数
curve | 输入2D点的矢量,存储形式为std::vector或Mat |
approxCurve | 逼近的结果曲线。类型匹配于输入曲线的类型 |
epsilon | 指定的逼近精度参数这是原始曲线与逼近曲线之间的最大距离 |
closed | 如果为真,逼近曲线是闭合的(其首尾顶点相连接)。否则是非闭合的 |
注意:调用函数时epsilon的选取很重要,越小表示精度越高,得到的拟合曲线越逼近contour,曲线的顶点也越多,反之拟合曲线只是大致描绘contour,或以直线拟合曲线。
(5)、double cv::arcLength (InputArray curve, bool closed);
计算轮廓的周长或曲线的长度。这个函数计算曲线的长度或闭合轮廓的周长。
参数
curve | 输入2D点矢量,存储形式为std::vector或Mat |
closed | 表示曲线是否闭合的标志 |
(6)、Rect cv::boundingRect (InputArray array);
计算点集或灰度图像非0像素的正外接矩形。这个函数返回指定点集或灰度图像非0像素的最小正外接矩形。正外接矩形是由左上右下描述的矩形,对应的有转角矩形,给出矩形的中心和宽高,可计算出矩形的四个顶点。
参数
array | 输入的灰度图像或2D点集,存储形式为std::vector或Mat |
(7)、void cv::boxPoints (RotatedRect box, OutputArray points);
查找转角矩形的4个顶点,用于绘出旋转矩形。这个函数找出转角矩形的4个顶点。对于绘制转角矩形这个函数是有用的。在C++中,除了这个函数,也可以直接使用RotatedRect::points 方法。
参数
box | 输入的旋转矩形。也可以作为输出 |
points | 矩形的四个顶点输出数组 |
(8)、int cv::connectedComponents (InputArray image, OutputArray labels,
int connectivity, int ltype, int ccltype);
int cv::connectedComponents (InputArray image, OutputArray labels,
int connectivity=8, int ltype=CV_32S);
计算二值图像连通域(部件)的标记图像。Image是具有4或8路连通性图像 – 函数返回N为标记数(对于每一个连通域,使用标记数替代像素灰度值填写labels图像)labels [0, N-1],其中0 表示背景标记。 ltype 指定输出的标记图像类型,需要重点考虑的是标记总数或源图的像素数。Ccltype表示函数使用的连通域标记算法,当前仅支持Grana 的(BBDT) 和Wu 的(SAUF)算法,见ConnectedComponentsAlgorithmsTypes。注意SAUF算法标记强制要求行主序(行主序是行元素优先存放,第一行各列元素存放,第二行各列元素…),而BBDT则不要求。如果至少有一个允许并行的架构并且图像的行数至少两倍于函数getNumberOfCPUs返回值,则这个函数使用并行版本的Grana 和Wu 算法。
参数
image | 要被标记的8位单通道图像 |
labels | 目标标记图像(使用标记值填写的图像) |
connectivity | 8 或 4,分别表示8-路或4-路连通性 |
ltype | 输出图像的标记类型。当前仅支持CV_32S和CV_16U |
ccltype | 连通域算法类型 |
(9)、int cv::connectedComponentsWithStats (InputArray image,OutputArray labels,
OutputArray stats,OutputArray centroids,int connectivity,int ltype,int ccltype);
int cv::connectedComponentsWithStats (InputArray image, OutputArray labels,
OutputArray stats, OutputArray centroids, int connectivity=8, int ltype=CV_32S);
计算二值图像的连通域(部件)的标记图像并生成每一个标记的统计输出。image 是具有4 或8 路连通性图像 – 函数返回N为标记数(对于每一个连通域,使用标记数替代像素灰度值填写labels图像)labels [0, N-1],其中0 为背景标记。ltype 指定输出的标记图像类型,需要重点考虑的是标记总数或源图的像素数。ccltype表示函数使用的连通域标记算法,当前仅支持Grana的(BBDT)和Wu的(SAUF),见ConnectedComponentsAlgorithmsTypes。注意SAUF算法标记强制要求行主序,而BBDT则不要求。如果至少有一个允许并行的架构并且图像的行数至少两倍于函数getNumberOfCPUs返回值,则这个函数使用并行版本的Grana 和Wu 算法。
Parameters
image | 要被标记的8-位单通道图像 |
labels | 目标标记图像 |
stats | 每个标记的统计输出,包括背景标记,下面是可用统计值描述。使用函数stats(label, COLUMN)访问统计值,其中COLUMN为 ConnectedComponentsTypes之一。数据类型为CV_32S |
centroids | 标记联通域的重心输出,包括背景标记。使用函数centroids(label, 0)访问重心标记的x,而centroids(label, 1) 访问重心标记的y。数据类型是CV_64F |
connectivity | 8 或 4 分别表示8-路或4-路连通性 |
ltype | 输出图像标记的类型。当前仅支持CV_32S和CV_16U |
ccltype | 连通域算法类型 |
(10)、double cv::contourArea (InputArray contour, bool oriented=false);
计算轮廓的面积。这个函数计算轮廓的面积。类似于特征矩,面积的计算也是使用格林公式。因而,其返回的面积与非0像素数,在使用drawContours 或fillPoly 来绘制轮廓时,可能有所不同。再有就是对于自交叉轮廓,这个函数最有可能给出错误结果。
例:
vector<Point> contour;
contour.push_back(Point2f(0, 0));
contour.push_back(Point2f(10, 0));
contour.push_back(Point2f(10, 10));
contour.push_back(Point2f(5, 4));
double area0 = contourArea(contour);
vector<Point> approx;
approxPolyDP(contour, approx, 5, true);
double area1 = contourArea(approx);
cout << "area0 =" << area0 << endl <<
"area1 =" << area1 << endl <<
"approx poly vertices" << approx.size() << endl;
Parameters
contour | 输入的2D点矢量(轮廓顶点),以std::vector或Mat形式存储 |
oriented | 面积朝向标志,如果为真,函数返回有符号面积值,符号依赖于轮廓的朝向(顺时针或逆时针)。使用这个属性可以通过面积的符号确定轮廓的朝向。默认,这个参数时假,标识返回的是绝对值。 |
(11)、void cv::convexHull (InputArray points, OutputArray hull, bool clockwise=false, bool returnPoints=true);
查找点集的凸包。函数cv::convexHull 使用Sklansky算法查找2D点集的凸包,当前实现的这个算法有O(N logN)阶复杂性。
参数
Points | 输入的2D 点集,以std::vector 或 Mat形式存储 |
Hull | 输出的凸包。或者是整数索引的矢量或者是点矢量。头一种情况,凸包元素是从0开始的凸包点在原始数组中的索引(因为凸包点集是原始点集的子集)。第二种情况,凸包元素就是凸包点本身。 |
Clockwise | 朝向标志。如果为真,输出凸包顺时针朝向。否则,逆时针朝向。这里假设坐标系的x轴指向右,y轴指向上。 |
returnPoints | 操作标志。在矩阵情况下,当这个标志为真,函数返回凸包点,否则返回凸包点的索引。在输出数组为std::vector时,不考虑这个标志,输出依赖于适当的类型std::vector<int>等价于returnPoints=false, std::vector<Point> 等价于returnPoints=true。 |
(12)、void cv::convexityDefects (InputArray contour, InputArray convexhull,
OutputArray convexityDefects);
查找轮廓的凸缺陷点(凹点)。下面这个手的轮廓图显示了凸缺陷状态:
图像
参数
contour | 输入轮廓 |
convexhull | 由函数convexHull 获得的凸包点,它包含凸点在轮廓中的点索引 |
convexityDefects | 输出的凸缺陷点矢量。每一个凸缺陷点表示为一个4元素整数矢量(Vec4i):开始索引,终止索引,最远点索引,深度,其中凸缺陷点的开始、终止、最远点的索引都是从0开始的原始轮廓索引,深度则是最远点凹陷近似距离(具有8位小数的定点数,其表示为一个整数),即,要获得深度的浮点值,需要fixpt_depth / 256.0。 |
(13)、RotatedRect cv::fitEllipse (InputArray points);
查找匹配2D点集的外接椭圆(以椭圆的外接矩形给出,注意,是一个转角矩形,长轴和短轴的方向为矩形的长和宽)。这个函数计算匹配于2D点集的椭圆(使用最小二乘法)。它返回一个转角矩形,外接于椭圆。使用的是在[73] 中描述的算法。开发者必须时刻记住返回的椭圆/转角矩形数据包含负值,这是因为数据点靠近Mat元素的边界时出现超出图像范围的现象。
Parameters
points | 输入的2D 点集,存储形式为std::vector<> 或Mat |
(14)、RotatedRect cv::fitEllipseAMS (InputArray points);
查找包容2D点集的椭圆。这个函数计算匹配2D点集的椭圆。返回一个内接椭圆的转角矩形。采用Gabriel Taubin 在平面曲线评估法中提出的均方差法计算。
对于椭圆,基本集为,此处集合有六个系数 。然而要定义一个椭圆只需要五个系数即可;长轴和短轴的长度 (a,b),位置 (x0,y0),和朝向 θ。这是因为基本集包含了直线,二次函数,抛物线和双曲线以及椭圆函数,都需要尽可能的匹配。如果找到一个抛物线或双曲线函数的匹配则使用标准的fitEllipse 方法。AMS算法通过引入条件来约束匹配到抛物线,双曲线和椭圆。其中矩阵Dx 和Dy 涉及矩阵D相对于x和y的偏导数。这个矩阵形式上逐行应用到基本集的每一点,如下:
AMS方法的最小成本函数为:
最小成本通过求解一般特征值问题获得。
参数
points | 输入的2D点集,存储形式为std::vector<> 或Mat |
(15)、RotatedRect cv::fitEllipseDirect (InputArray points);
查找包容2D点集的椭圆。这个函数计算匹配2D点集的椭圆。返回内接椭圆的转角矩形。使用直接最小二乘法(Direct)。对于椭圆,基本集为,此处集合有六个系数。然而要定义一个椭圆只需要五个系数即可;长轴和短轴的长度 (a,b),位置 (x0,y0),和朝向 θ。这是因为基本集包含了直线,二次函数,抛物线和双曲线以及椭圆函数,都需要尽可能的匹配。直接最小二乘法通过保证来约束匹配到椭圆。这个条件的引入是 满足不等式,以及系数可以任意缩放而没有过度的限制。
最小成本通过求解一般特征值问题获得。
系统仅产生一个正特征值 λ ,被选择作为特征矢量u的解。这对于求解系数是有用的。
比例因子保证。
参数
points | 输入的2D点集,存储形式为std::vector<> 或 Mat |
(16)、void cv::fitLine (InputArray points, OutputArray line, int distType, double param, double reps, double aeps);
匹配线段到2D或3D点集。函数fitLine 匹配线段到2D 或 3D 点集,采用最小化,其中 是第i点到直线的距离,ρ(r) 是距离函数,有下面形式:
DIST_L2
(最简单和快捷的最小二乘法)
- DIST_L1
- DIST_L12
- DIST_FAIR
此处C=1.3998
- DIST_WELSCH
此处C=2.9846
- DIST_HUBER
此处C=1.345
这个算法基于M-estimator技术,使用权重最小二乘迭代适配线段。在每一次迭代之后权重 相对于成反比调节。
参数
points | 输入的2D或3D点矢量,存储形式为std::vector<> 或 Mat |
line | 输出的直线参数。在2D匹配时,是4元素矢量(如Vec4f) - (vx, vy, x0, y0),其中(vx, vy) 是与直线共线的正则矢量,(x0, y0) 是直线上的点。在3D匹配时,是6元素矢量(如Vec6f) - (vx, vy, vz, x0, y0, z0),其中(vx, vy, vz)是与直线共线的正则矢量,(x0, y0, z0)是直线上的点。 |
distType | 距离类型,用于M-estimator,参见DistanceTypes |
param | 数值参数(C),用于某些距离类型。如果为0,则选择一个最优值。 |
reps | 半径(坐标原点与直线之间的距离)的最高精度。 |
aeps | 角度的最高精度。对于reps和aeps,默认值0.01是合适的。 |
(17)、float cv::intersectConvexConvex (InputArray _p1, InputArray _p2,
OutputArray _p12, bool handleNested=true);
查找两个凸多边形的相交区域。
Parameters
_p1 | 第一个多边形 |
_p2 | 第二个多边形 |
_p12 | 输出的相交区域的多边形 |
handleNested | 若为真,则查找一个多边形完全封装在另一个多边形中的情况。若为假,则这种情况不算相交。如果多边形有共享边或,一个多边形的顶点落在另一个多边形的边上,此时多边形不是嵌套,而作为相交处理,不管handleNested值如何。 |
返回:相交多边形的面积绝对值
注意:函数intersectConvexConvex 并不确认多边形的凸特征,因此,如果有非凸多边形,则返回值不确定。
(18)、bool cv::isContourConvex (InputArray contour);
检测轮廓的凸特征。这个函数检测输入的轮廓是否具有凸特征。轮廓必须是简单的,没有自交叉。否则函数的输出无定义。
参数
Contour | 输入2D点矢量,存储形式为std::vector<> 或 Mat |
(19)、double cv::matchShapes (InputArray contour1, InputArray contour2,
int method, double parameter);
比较两个形状。这个函数比较两个形状。所有三个实现方法都是用Hu的不变性(见HuMoments)。
Parameters
contour1 | 第一个轮廓或灰度图 |
contour2 | 第二个轮廓或灰度图 |
method | 比较方法,见ShapeMatchModes |
parameter | 方法指定的参数(当前不支持) |
(20)、RotatedRect cv::minAreaRect (InputArray points);
查找封装输入的2D点集的最小面积转角矩形。这个函数计算并返回指定点集的最小面积封装的矩形(可能是转角的)。开发者应该记住返回的RotatedRect可能包含负索引值,因为点集数据靠近包含Mat元素的边界时,矩形会超出图像边界。
参数
points | 输入的2D点矢量,存储形式为std::vector<> 或 Mat |
(21)、void cv::minEnclosingCircle (InputArray points, Point2f ¢er, float &radius);
查找封装2D点集的最小面积的圆。这个函数使用迭代算法查找2D点集的最小封装圆。
参数
points | 输入的2D点矢量,存储形式为std::vector<> 或 Mat |
center | 输出圆心 |
Radius | 输出半径 |
(22)、double cv::minEnclosingTriangle (InputArray points, OutputArray triangle);
查找封装2D点集的最小面积的三角形,并返回面积值。这个函数查找封装给定2D点集的最小面积三角形并返回面积。下面图像显示了给定2D点集的输出。2D点描绘成红色的 * 而封装三角形则是黄色的。
最小封装三角形函数的实例输出
实现的算法来自于O'Rourke 、Klee和Laskowski的论文。O'Rourke 提供了θ(n) 算法用于在具有n个2D顶点的多边形中查找最小封装三角形。由于在 minEnclosingTriangle 函数中采用2D点集作为输入,因此,要求附加一个预处理步骤来计算2D点集的凸包。函数convexHull的复杂性为O(nlog(n)) ,这高于θ(n)。因此总体计算复杂性为O(nlog(n))。
参数
points | 输入的2D点矢量,具有深度CV_32S 或CV_32F,存储形式为std::vector<> 或Mat |
triangle | 输出的三个2D点,定义三角形的顶点。OutputArray深度必须是CV_32F。 |
(23)、double cv::pointPolygonTest (InputArray contour, Point2f pt, bool measureDist);
执行点在轮廓中的检测。这个函数确定是否指定点落在轮廓中,轮廓外,或落在边上(或共享顶点)。对应返回为正(在内),负(在外),或0(在边上)值。当measureDist=false,则分别返回+1,-1,和0。 否则,返回值为点与最近轮廓边的带符号距离值。
见下面的函数输出示例,每一个像素点相对于轮廓进行检测:
示例输出
参数
contour | 输入轮廓 |
pt | 相对于轮廓检测的点 |
measureDist | 如果真,这个函数估算点到最近轮廓边带符号的距离值,否则函数仅检查点是否在轮廓内。 |
(24)、int cv::rotatedRectangleIntersection(const RotatedRect &rect1,
const RotatedRect &rect2, OutputArray intersectingRegion);
查找两个转角矩形是否有交叉。如果有,交叉区域的顶点被返回。下面显示了交叉结果式样。阴影模式表示交叉区域,红色的点为函数返回的顶点。
交叉示例
参数
rect1 | 第一个矩形 |
rect2 | 第二个矩形 |
intersectingRegion | 输出的交叉区域顶点数组。最多返回8个顶点。存储形式为std::vector<cv::Point2f> 或cv::Mat 作为Mx1 类型的CV_32FC2的矩阵 |
返回:RectanglesIntersectTypes之一
(25)、Ptr< GeneralizedHoughBallard > cv::createGeneralizedHoughBallard ();
建立cv::GeneralizedHoughBallard 类的智能指针并初始化这个类。
(26)、Ptr<GeneralizedHoughGuil> cv::createGeneralizedHoughGuil ();
建立cv::GeneralizedHoughGuil 类的智能指针并初始化这个类。
结构分析函数的示例
1、图像轮廓函数
void cv::findContours(InputArray image,OutputArrayOfArrays contours,
OutputArray hierarchy,int mode, int method, Point offset = Point());
图像轮廓函数查找输入图像的边缘轮廓,并根据查找模式(mode)给出轮廓之间的关系,最外层轮廓为图像的边框。
参数mode为:
RETR_EXTERNAL Python: cv.RETR_EXTERNAL | 仅仅遍历外层轮廓。设置所有轮廓的 hierarchy[i][2]=hierarchy[i][3]=-1(即没有父和子) |
RETR_LIST Python: cv.RETR_LIST | 遍历所有轮廓,但不给出层次关系(平铺所有轮廓) |
RETR_CCOMP Python: cv.RETR_CCOMP | 遍历所有轮廓,并建立两层层次关系。顶层为区域的外边界。第二层为区域的孔边界。如果在孔中还存在另一个边界,则这个边界仍作为顶层轮廓。 |
RETR_TREE Python: cv.RETR_TREE | 遍历所有轮廓并重构嵌套轮廓的完全层次关系(不是边和孔的关系) |
参数method为:
CHAIN_APPROX_NONE Python: cv.CHAIN_APPROX_NONE | 完全存储所有轮廓点。轮廓中任何两个连续的点(x1,y1)和(x2,y2)都是水平,垂直或对角邻域点,即,max(abs(x1-x2),abs(y2-y1))==1. |
CHAIN_APPROX_SIMPLE Python: cv.CHAIN_APPROX_SIMPLE | 压缩水平,垂直和对角线段,仅仅保留其端点。例如,直立矩形轮廓用四个点编码表示。 |
CHAIN_APPROX_TC89_L1 Python: cv.CHAIN_APPROX_TC89_L1 | 使用一种称之为Teh-Chin链逼近算法查找轮廓 |
CHAIN_APPROX_TC89_KCOS Python: cv.CHAIN_APPROX_TC89_KCOS | 使用一种称之为Teh-Chin链逼近算法查找轮廓 |
左图为canny图,右图为在canny图上检测到的contours,并绘制到灰度图上。
左上图为canny图,右上图为全部contours图,左下图为最外层contours图,右下图为第二层contours图,在树状结构下,只有两层contours结构。从这个例子中可以看出,findcontours函数给出的contours之间的层次关系。然而contours并没有在视觉上分割图像中的目标,比如棋子目标,一般不能依据contour来直接提取图像目标属性,只能作为参考,然后进一步分析。
Moments cv::moments(InputArray array, bool binaryImage = false );
求图像的矩,矩是图像中非零像素区域的特征和属性表示。其中包括质量,质心等属性,对于二值图像质量也是面积。一般使用contours顶点序列作为输入,如果输入是灰度图,则计算整图的矩。可以利用矩的不变性,进行目标检测。
void cv::HuMoments (const Moments &moments, double hu[7]);
void cv::HuMoments (const Moments &m, OutputArray hu);
计算矩moments的7个不变矩。这些不变矩具有仿射不变,旋转不变,位移不变等特性,在目标检测中经常用到。
这是对图像的一个contour计算的humoment值,即moment质心位置。可以对一幅图像、一个contour计算对应的moment和humoment,并以此检测其contour或图像在目标中出现的位置,一是有没有,二是在哪里出现(这种计算是对图像逐点扫描进行的)。
对所有contours计算其moment,并标记质心位置。其中白点为对应contour的质心,红点为contours图像总体质心。可以通过contour的moment计算humoment,对另一图像的contours计算,然后比较所有contours的humoment找出相似的contour。注意,humoment具有不变性。
void cv::approxPolyDP (InputArray curve, OutputArray approxCurve, double epsilon,
bool closed);
contour中的每一个点作为多边形的顶点,形成多边形曲线curve。在findcontours函数中如果method= CHAIN_APPROX_NONE,返回的contour则全部由像素点组成,而methode= CHAIN_APPROX_SIMPLE,则返回的contour对相邻点进行优化,使用线段端点表示,简化多边形曲线的顶点。这个函数是对contour的顶点进行多边形逼近,用更少的顶点替代contour顶点。由epsilon的作用看contour到poly的图像效果:
左上图为epsilon=5,contour转换成poly的顶点数为13,此时的效果非常差,右上图为epsilon=1,poly顶点数为25,效果基本可以,左下图为epsilon=0.5,poly顶点数为37,效果基本拟合源contour,再缩小epsilon=0.1,此时的poly顶点数为86,效果并没有进一步增强,因此,一般认为epsilon应该根据目标情况环境适当设置,以1—5为好,更好的精度要求可以使用0.5—1.0。
double cv::arcLength (InputArray curve, bool closed);
计算给定contour的长度,对于封闭曲线则是周长。对于使用approxPolyDP函数获得的多边形曲线计算的值与原始的contour有一些误差。
对contour曲线计算长度,为246.6934,并且有86个顶点。
对同一条contour经过approxPolyDP逼近获得折线计算弧长为238.6901,该折线仅有25个顶点。使用折线替代contour可以及大地缩小图像处理成本。
Rect cv::boundingRect (InputArray array);
这个函数计算输入点集(contour)或灰度图像区域像素点集的最小外接矩形(正矩形,由Rect结构表示)。注意这里的输入可以是任何点集,不一定是contour或折线顶点集。返回的是囊括指定点集的矩形区域。
这是对一条contour曲线计算其外切矩形的例子。
RotatedRect cv::fitEllipse (InputArray points);
RotatedRect cv::fitEllipseAMS (InputArray points);
RotatedRect cv::fitEllipseDirect (InputArray points);
查找适配给定点集的椭圆,并返回该椭圆的外接转角矩形。注意,对于非contour点集,该椭圆囊括点集的所有点。
三个适配椭圆算法的区别,请看下图:
对同一条contour进行适配椭圆计算,其中黄色为contour,红色为算法给出的椭圆,绿色为函数返回的转角矩形。从图中可以看出,contour的顶点(在方格点)并未被包含到椭圆中,也就是说并不是contour(点集)的所有点都囊括在椭圆中,而是根据算法侧重进行取舍,获得一个算法上的最佳椭圆形状。上面左图为fitEllipse函数返回,中图为fitEllipseAMS函数返回,右图为fitEllipseDirect函数返回。
void cv::fitLine (InputArray points, OutputArray line, int distType, double param, double reps, double aeps);
查找一条拟合给定点集的直线。其中distType为选择的拟合方法类型,有:DIST_L2,DIST_L1,DIST_L12,DIST_FAIR,DIST_WELSCH,DIST_HUBER。
对两条近似直线的contour线进行fitLine操作可以得到两条拟合的射线(基本上是共线的)。
对同一条contour使用原始contour(右图)和经过approxPolyDP处理的折线,使用fitLine函数拟合,可以看出差异,fitLine函数仅对顶点进行拟合操作,不对线段进行处理,因此在做fit系列操作时,对contour点集应该使用近似方法CHAIN_APPROX_NONE,而不是其他,只有这样,在使用fit系列函数时才能真正做到点集的拟合。
这是findcontours函数使用CHAIN_APPROX_NONE方法时,同contour的fitEllipse的三个操作。
这是findcontours函数在CHAIN_APPROX_SIMPLE方法下的fitellipse操作。从图中可以看出明显的差异。这对图像的目标识别很重要。
void cv::boxPoints (RotatedRect box, OutputArray points);
这个函数计算给定转角矩形的四个定点的坐标,可以直接使用转角矩形的points方法。
int cv::connectedComponents (InputArray image, OutputArray labels, int connectivity,
int ltype, int ccltype);
int cv::connectedComponents (InputArray image, OutputArray labels, int connectivity=8,
int ltype=CV_32S);
该函数计算图像中的连通区域。输出的标签图像labels为该标签序号在连通域的灰度值。0位背景区域。函数返回连通域的个数。
上面三图是connectedComponents函数对灰度图作用的结果,中间图没有使用开闭运算处理,右图使用了开闭运算处理。下面三图是源图,canny图和contours图对比后可以看出各自的用途,可以将各种图进行运算处理,比如叠加,相减等操作,然后再进行处理,增强响应目标的效果。
int cv::connectedComponentsWithStats (InputArray image,OutputArray labels,
OutputArray stats,OutputArray centroids,int connectivity,int ltype,int ccltype);
int cv::connectedComponentsWithStats (InputArray image, OutputArray labels,
OutputArray stats, OutputArray centroids, int connectivity=8, int ltype=CV_32S);
该函数计算图像中的连通区域,并返回连通区域的统计数据。
右图是具有统计数据的联通域图。下面是对应的canny图和contours图
对于一般的图像在取二值图的过程中如果能够区分开目标区域,则这个函数可能给出正确结果,否则需要分离目标及其周围的关联区域。如下图:
要分辨出目标的联通域,还需要进一步处理。
double cv::contourArea (InputArray contour, bool oriented=false);
计算给定contour的面积,返回面积值,可能与非零像素数有不同的值。算法基本原理是对一个坐标点系列(xi,yi),进行如下计算:
area = area + (xi * yi+1)- (xi+1 * yi);
area = area / 2;
遍历所有contour顶点后,上式可获得数学意义上的面积值,并且根据contour顶点的顺时针或逆时针产生area的正负号,顺时针是负值,逆时针为正值。使用approxPolyDP函数可以获得contour对应的多边形顶点。
从图中可以看出实际contour(红色),没有闭合,通过approxPolyDP逼近的折线(黄色),计算折线的面积area = 14.5是折线围起的面积不是contour实际表示的圆面积。Contour使用的Suzuki 算法对于非封闭contour有一个回溯过程,实际是走了两遍,伪闭合,因此在计算面积时给出的是contour或对应折线的内包含面积。就如黄色折线包含的角点面积。
void cv::convexHull (InputArray points, OutputArray hull, bool clockwise=false,
bool returnPoints=true);
这个函数计算包容给定点集凸包多边形顶点。对于contour点集,给出包容contour点集的封闭多边形,如果contour本身就是凸的,凸包多边形最大程度拟合contour。
这是contour的面积,实际表示contour弧线上的顶点围起来的区域面积(如前面对contour面积的解释图)。
这是计算的contour凸包多边形,并计算了多边形的面积,比较上图,可知contour不是封闭多边形时,其面积不一定是看起来的样子,此时可以使用凸包算法,将contour转换为一个实际的凸多边形,此时可以使用凸多边形做各种操作。
void cv::convexityDefects (InputArray contour, InputArray convexhull,
OutputArray convexityDefects);
查找contour的凹点(凸缺陷点)。对于一个由contour围起来的区域,检查该区域是否为凸区域或有凹点的区域。一般来讲,contour不一定是封闭的,在图像处理中由于阈值的设定等,同一副图像(同等环境下的多幅图像),可能会出现同区域的contour表示不同,此时对contour操作就有可能得出不同的结果,因此就需要对contour进行进一步的处理。convexHull和convexityDefects函数就是这样的函数,它们给出contour的进一步特征。
左侧为源图,右侧为一条contour图。
左侧为contour的凸包图,右侧为contour的凸缺陷图,绿点为凸缺陷最深点。可以据此检验contour的凸特性。
float cv::intersectConvexConvex (InputArray _p1, InputArray _p2,
OutputArray _p12, bool handleNested=true);
检测两个凸多边形的交叉,对于非凸多边形,函数返回不确定性。同一图像的两个contour实际上是不可能相交的,要么属于同一个contour,要么属于嵌套contour(从属关系)。这是suzuki算法过程决定的。
这是contours图和对应的contours凸包图。从图中可以看出,contours是不相交的,但是对应的凸包多边形有相交。使用intersectConvexConvex()函数可以计算出相交部分的多边形顶点。
红色标注的区域就是相交区域,由函数给出的顶点矢量描绘。
bool cv::isContourConvex (InputArray contour);
检测输入的contour是否具有凸属性。使用函数对contour进行检测,返回的结果总是false,这是因为contour的顶点是按照Suzuki算法顺序给出的,相邻顶点不是真正的环绕,而是间断跳跃的,因此判断凸特性时总返回false。而使用相关的凸包多边形或DP拟合折线则可正确返回。
绿色是使用DP拟合折线给出的凸特性检测,为true,红色是使用contour原始数据给出的凸特性检测,结果为false,最下图时使用凸缺陷检测对contour原始数据给出的结果,绿色点为凸缺陷深点,图中有许多缺陷点。
double cv::matchShapes (InputArray contour1, InputArray contour2,
int method, double parameter);
比较两条contour的相似度,一个是模板contour,一个是图像contour,返回两者的相似度,越小表示越相似。使用一个模板比较整个图像的contours,可以根据相似度找出匹配目标。一般图像在经过二值化过程中有可能会丢失一些信息,在findcontours()函数操作时,得到的contour可能是多段不封闭的线条,此时要对其进行合并或过滤操作,convexhull操作,然后检查相关性,之后才能进行形状匹配,并得出正确结果。
这是使用模板在图像中查找匹配的形状的例子,实际使用中还需要对图像的处理方式(滤波,canny,findcontourd等)进行适当操作。注意,使用canny图做findcontours操作和使用二值图做findcontours操作会有不同效果,因此对于一般环境可根据实际情况进行操作选择。
RotatedRect cv::minAreaRect (InputArray points);
返回包容点集的最小面积转角矩形。输入的点集可以是连通域点集,contour点集和DP折线顶点,凸包点集等,本例只对contour相关点集进行操作。
左侧为contours图,右侧为convexhull图。
这是minAreaRect()函数返回的转角矩形图。注意,有些图形有两个contours,一个是内圈,一个是外圈,有分层。注意:以上的contours图时使用canny图生成的,因此每一个形状都有内外两圈contour。下面是使用二值图计算的contours,每一个形状仅有一个contour。
而且contour是完整的。这里说明contours计算应该使用二值图,而不是canny图。获得正确的contours,对以后的图像处理有好处。
void cv::minEnclosingCircle (InputArray points, Point2f ¢er, float &radius);
计算包容指定点集的最小封闭圆。输入的点集可以是连通域点集,contour点集和DP折线顶点,凸包点集等,本例只对contour相关点集进行操作。
Canny图和contours图。
minAreaRect图和minEnclosingCircle图,都是来源于同一个contours数据。
double cv::minEnclosingTriangle (InputArray points, OutputArray triangle);
计算包容指定点集的最小封闭三角形,输入的点集可以是连通域点集,contour点集和DP折线顶点,凸包点集等,本例只对contour相关点集进行操作。
注意,在图像处理过程中,contours给出的点集矢量是具有可识别属性的点集,而二值图、灰度图、canny图等给出的都是图像属性的点集。因此,只有在获取到图像的contours后才能做进一步的处理和识别操作。当然也可以使用连通域属性替代contours。
double cv::pointPolygonTest (InputArray contour, Point2f pt, bool measureDist);
检测点是否在多边形区域中。返回点到多边形最近边的距离,正数为在多边形内,负数为在多边形外,0为在多边形边上。
对图中所有contours进行检测。图所示,距离越远颜色越浅。不包含的点为黑色。
int cv::rotatedRectangleIntersection (const RotatedRect &rect1, const RotatedRect &rect2, OutputArray intersectingRegion);
计算两个转角矩形的交叉点集。返回最多八个顶点的相交区域多边形。函数返回INTERSECT_NONE不相交,INTERSECT_PARTIAL部分相交,INTERSECT_FULL完全嵌套相交。
左图为最小面积转角矩形,右图为相交的区域。