上一篇OpenCV contour detection(C++实现)(一)实现了image的读取、灰度处理、高斯模糊、canny边缘检测、轮廓检测、轮廓绘制。本篇在之前基础上对检测到的轮廓进行多边形逼近(使用多边形逼近轮廓,减少表示轮廓的Point)。

  1. conPoly变量保存多边形逼近后的轮廓
vector<vector<cv::Point>> conPoly(contours.size());

回顾一下,contoursconPoly变量第一维是每一个轮廓的序号,第二维是每一个轮廓包含的点集。

  1. 通过轮廓面积筛选轮廓
    contourArea()函数,定义:
double contourArea( InputArray contour, bool oriented = false );

参数:
contour:一个轮廓
oriented:为true时,返回结果带符号,取决于轮廓是顺时针还是逆时针(点集也可以看成向量集),一般取默认值false即可
可以计算每个轮廓的面积:

int area = contourArea(contours[i]);
  1. 多边形逼近
    函数approxPolyDP(),定义:
void approxPolyDP( InputArray curve,
                   OutputArray approxCurve,
                   double epsilon, bool closed );

参数:
curve:保存2D Point的集合,可以是vector<cv::Point>cv::Mat
approxCurve:保存逼近结果,需要和curve类型相同
epsilon:用来指定逼近精度,表示原始轮廓到近似轮廓之间的最大距离
closed:为true时,近似轮廓首尾顶点相连
事实上,对不同大小的contour一般会指定不同大小的epsilon,常用加权的轮廓周长,计算轮廓周长使用arcLength函数:

double arcLength( InputArray curve, bool closed );

参数同approxPolyDP,多边形逼近过程可以写做如下(示例):

double param = arcLength(contours[i], true);
approxPolyDP(contours[i], conPoly[i], 0.02*param, true);
  1. 多边形轮廓分类
    conPoly中有几个Point,逼近轮廓就是几边形,可以以此来对conPoly分类。
    用长方形边框框出每一个conPoly:
    boundingRect()函数:
Rect boundingRect( InputArray array );

输入灰度图或者2D Point集,返回点集或灰度图像的非零像素的最小边界矩形。返回类型Rect是OpenCV内的矩形类。
接下来还需要把这个矩形绘制出来,使用rectangle()函数:

void rectangle(InputOutputArray img, Point pt1, Point pt2,
               const Scalar& color, int thickness = 1,
               int lineType = LINE_8, int shift = 0);
 //overload
 void rectangle(InputOutputArray img, Rect rec,
                const Scalar& color, int thickness = 1,
                int lineType = LINE_8, int shift = 0);

参数:
img:图像
pt1、pt2:矩形的一对相对的顶点
recRect
colorScalar(B, G, R)类,矩形框颜色
thickness:矩形框粗细
lineType:线型,上一篇有介绍
shift:坐标点的小数点位数
那么分类多边形轮廓边数、绘制矩形框过程可以写做如下:

//其实需要一个循环遍历contours、conPoly和boundRect
//这里是伪代码,只写一个下标作为示例
//多边形逼近
double param = arcLength(contours[i], true);//轮廓周长
approxPolyDP(contours[i], conPoly[i], 0.02*param, true);//多边形逼近
//多边形分类
string objType;//多边形类型
int objCor = (int)conPoly[i].size();//轮廓的角点数量
if(objCor == 3) objType = "Tri";//若角点数量为3,则多边形轮廓为三角形...以此类推
//绘制边界框
vector<Rect> boundRect(contours.size());//这个变量保存每个轮廓对应的边界框
boundRect[i] = boundingRect(conPoly[i]);//获取边界矩形
//Rect类的成员函数tl()、br()是矩形的左上角(top_left)、右下角(bottom_right)点
rectangle(img, boundRect[i].tl, boundRect[i].br, Scalar(255, 0, 255), 4);//绘制边界框
//最后给边框加上文字标注
putText(img, objType, {boundRect[i].x, boundRect[i].y - 5}, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 1);

最后补充下创建文本操作,使用putText函数,定义:

void putText( InputOutputArray img, const String& text, Point org,
              int fontFace, double fontScale, Scalar color,
              int thickness = 1, int lineType = LINE_8,
              bool bottomLeftOrigin = false );

部分参数:
org:text的origin坐标
fontFace:字体
fontScale:字体大小
bottomLeftOrigin:为true时,origin坐标是text的左下角,否则是左上角