OpenCV函数:提取轮廓相关函数使用方法

0、绪:
步骤:
一、findContours()查找轮廓;
二、drawContours()画轮廓;
三、轮廓填充;
四、计算轮廓的面积和周长;
五、提取轮廓凸包,矩形,最小外接矩形,外接圆

操作方法

  • 01

    findContours()查找轮廓: void findContours ( InputOutputArray image,//输入图像,必须是8位单通道二值图像 OutputArrayOfArrays contours,//检测到的轮廓,每个轮廓被表示成一个point向量 OutputArray hierarchy,//可选的输出向量,包含图像的拓扑信息。其中元素的个数和检测到的轮廓的数量相等 int mode,//说明需要的轮廓类型和希望的返回值方式 int method,//轮廓近似方法 Point offset = Point() ) 参数mode: ①mode的值决定把找到的轮廓如何挂到轮廓树节点变量(h_prev, h_next, v_prev, v_next)上,拓扑结构图如下; ②每种情况下,结构都可以看成是被横向连接(h_prev, h_next)联系和被纵向连接(v_prev, v_next)不同层次。 ③CV_RETR_EXTERNAL:只检测出最外轮廓即c0; CV_RETR_LIST:检测出所有的轮廓并将他们保存到表(list)中; CV_RETR_COMP:检测出所有的轮廓并将他们组织成双层的结构,第一层是外部轮廓边界,第二层边界是孔的边界; CV_RETR_TREE:检测出所有轮廓并且重新建立网状的轮廓结构; ④参数method: CV_CHAIN_CODE:用freeman链码输出轮廓,其他方法输出多边形(顶点的序列); CV_CHAIN_APPROX_NONE:将链码编码中的所有点转换为点; CV_CHAIN_APPROX_SIMPLE:压缩水平,垂直或斜的部分,只保存最后一个点; CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_QPPROX_TC89_KCOS:使用Teh-Chin链逼近算法中的一个。 CV_LINK_RUNS:与上述的算法完全不同,连接所有的水平层次的轮廓。 【注】:findContours()查找时,这个图像会被直接涂改,因此如果是以后有用的图像,应该复制之后再进行查找;

  • 02

    drawContours()绘制轮廓: void drawContours( InputOutputArray image,//要绘制轮廓的图像 InputArrayOfArrays contours,//所有输入的轮廓,每个轮廓被保存成一个point向量 int contourIdx,//指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓 const Scalar& color,//绘制轮廓所用的颜色 int thickness = 1, //绘制轮廓的线的粗细,如果是负数,则轮廓内部被填充 int lineType = 8, /绘制轮廓线的连通性 InputArray hierarchy = noArray(),//关于层级的可选参数,只有绘制部分轮廓时才会用到 int maxLevel = INT_MAX,//绘制轮廓的最高级别,这个参数只有hierarchy有效的时候才有效 Point offset = Point() ) 【注】: ①maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓 maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。 maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点

  • 03

    轮廓填充: 步骤: a) 依次遍历轮廓点,将点绘制到img上; b) 使用floodFill以及一个种子点进行填充; 两种方法:自己编写程序;使用drawContours()函数; void drawMaxAreaLine(Mat& dst, vector<Point> maxAreaPoints) { int step = dst.step; auto data = dst.data; for (int i = 0; i < maxAreaPoints.size(); ++i) { *(data + maxAreaPoints[i].x + maxAreaPoints[i].y * step) = 255; } } //孔洞填充算法 void fillHole(Mat src_Bw, Mat &dst_Bw) { Size m_Size = src_Bw.size(); Mat Temp=Mat::zeros(m_Size.height+10,m_Size.width+10,src_Bw.type()); src_Bw.copyTo(Temp(Range(5, m_Size.height + 5), Range(5, m_Size.width + 5))); floodFill(Temp, Point(0, 0), Scalar(255)); Mat cutImg; Temp(Range(5, m_Size.height + 5), Range(5, m_Size.width + 5)).copyTo(cutImg); dst_Bw = src_Bw | (~cutImg); } 【注】:这里常会碰到种子点不好选取的问题,因为有时候所选择的种子点不能保证对所有轮廓都适用。也就是查找一个在轮廓内的点是存在一定难度的。 使用drawContours()就会很方便: vector<vector<Point> > contours; contours.push_back(currentFrameEdge); Mat savedGrayMat = Mat::zeros(RectData[0].rows, RectData[0].cols, CV_8UC1); //drawMaxAreaLine(savedGrayMat, currentFrameEdge); //floodFill(savedGrayMat, Point(currentFrameEdge[0].x + 2, currentFrameEdge[0].y + 2), 255); drawContours(savedGrayMat, contours, 0, Scalar(255), CV_FILLED); imshow("savedGrayMat", savedGrayMat); waitKey();

  • 04

    计算轮廓的面积和周长: ①计算轮廓面积: double contourArea(InputArray contour, bool oriented=false ) InputArray contour:输入的点,一般是图像的轮廓点; bool oriented=false: 表示某一个方向上轮廓的的面积值,顺时针或者逆时针,一般选择默认false; ②计算轮廓边长: double arcLength(InputArray curve, bool closed) InputArray curve:表示图像的轮廓; bool closed:表示轮廓是否封闭的; 【注】: ①contourArea计算整个或部分轮廓的面积; 在计算部分轮廓的情况时,由轮廓弧线和连接两端点的弦围成的区域总面积被计算,如图; ②轮廓的位置将影响区域面积的符号,因此函数范围的有可能是负值。可以在运行时使用fabs()来得到面积的绝对值;

  • 05

    提取轮廓凸包,矩形,最小外接矩形,外接圆 ①convexhull():函数提取轮廓的凸包: 格式: void convexhul(InputArray points, OutputArray hull, bool clockwise=false, bool returnPoints=true) 参数: 第一个参数:要求凸包的点集 第二个参数:输出的凸包点,可以为vector,此时返回的是凸包点在原轮廓点集中的索引,也可以为vector,此时存放的是凸包点的位置 第三个参数:一个bool变量,表示求得的凸包是顺时针方向还是逆时针方向,true是顺时针方向; 第四个参数,第二个参数的返回类型是vector还是vector,可以忽略; ②boundingRect():计算轮廓外包矩形,矩形是与图像上下边界平行的; 格式: Rect boundingRect(InputArray points); 输入:二维点集,点的序列或向量 (Mat) 返回:Rect //寻找外包矩阵 Rect maxRect = boundingRect(contours[m_count]); //绘制外包矩阵 rectangle(contours_img_1, maxRect, Scalar(0, 255, 0)); ③minAreaRect():提取轮廓的最小外包矩形; 主要求包含点集最小面积的矩形,这个矩形是可以有偏转角度的,可以与图像的边界不平行; 格式: RotatedRect minAreaRect(InputArray points) 输入:二维点集,点的序列或向量 (Mat) 返回:RotatedRect ④minEnclosingcircle():提取轮廓的最小外包圆; 格式: void minEnclosingcircle(InputArray points,Point2f& center,float& radius) 输入: 二维点集:点的序列vector< point >或向量 (Mat) , 圆心坐标; 半径;

  • 06

    示例: #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include "opencv2/contrib/contrib.hpp" #include <iostream> #include <stdio.h> using namespace cv; using namespace std; void KmeansFun(); void fillHole(Mat src_Bw, Mat &dst_Bw); int main() { Mat src_img = imread("data\\00.jpg",1); Mat src_img_1(src_img.rows,src_img.cols,CV_8UC1,Scalar(0)); cvtColor(src_img, src_img_1, CV_BGR2GRAY); GaussianBlur(src_img_1, src_img_1, Size(3, 3), 3, 3); Mat threshold_img(src_img.rows,src_img.cols,CV_8UC1,Scalar(0)); threshold(src_img_1, threshold_img, 0, 255, THRESH_OTSU); imshow("1",threshold_img); vector< vector<Point> > contours; vector<Vec4i> hierarchy; findContours( threshold_img, contours, hierarchy,//轮廓的继承关系 CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE ); Mat contours_img(src_img.rows,src_img.cols,CV_8U,Scalar(0)); if(!contours.empty() && !hierarchy.empty()) { int idx = 0; for( ; idx >= 0; idx = hierarchy[idx][0] ) { Scalar color(255,255,255); //Scalar color( (rand()&255), (rand()&255), (rand()&255) );//随机生成颜色 drawContours( contours_img, contours, idx, color, 1, 8, hierarchy ); } } imshow("contours_img",contours_img); int m_count = 0; Mat contours_img_1(src_img.rows,src_img.cols,CV_8UC3,Scalar(0)); if(!contours.empty() && !hierarchy.empty())      //计算轮廓的面积 { for (int i = 0; i < (int)contours.size(); i++) { double g_dConArea = abs(contourArea(contours[i], false)); double g_dConLength = arcLength(contours[i],true); cout << "【用轮廓面积计算函数计算出来的第" << i << "个轮廓的面积为:】" << g_dConArea << endl; cout << "【用轮廓面积计算函数计算出来的第" << i << "个轮廓的边长为:】" << g_dConLength << endl; if (g_dConArea >= 10 && g_dConArea < 300) { m_count = i; //Scalar color( (rand()&255), (rand()&255), (rand()&255) );//随机生成颜色 //drawContours(contours_img_1, contours, m_count, color, CV_FILLED, 8, hierarchy );//对轮廓内部着色 //计算凸包 //vector<int> hull; //convexHull(Mat(contours[m_count]), hull, true); ////绘制凸包 //int hull_count = (int)hull.size(); //Point pt0 = contours[m_count][hull[hull_count - 1]]; //for (int i = 0;i < hull_count;i++) //{ //     Point pt = contours[m_count][hull[i]]; //     line(contours_img_1, pt0, pt, Scalar(0, 255, 0), 1,8); //     pt0 = pt; //} ////寻找外包矩阵 //Rect maxRect = boundingRect(contours[m_count]); ////绘制外包矩阵 //rectangle(contours_img_1, maxRect, Scalar(0, 255, 0)); ////寻找最小外包矩形 //RotatedRect minRect = minAreaRect(contours[m_count]); //Point2f fourPoint2f[4]; ////将minRect的四个顶点坐标值放到fourPoint的数组中 //minRect.points(fourPoint2f); ////根据得到的四个点的坐标  绘制矩形 //for (int i = 0; i < 3; i++) //{ //     line(contours_img_1, fourPoint2f[i], fourPoint2f[i + 1], Scalar(0,0,255), 3); //} //line(contours_img_1, fourPoint2f[0], fourPoint2f[3], Scalar(0, 0, 255), 3); //在生成的那些随机点中寻找最小包围圆形 Point2f center; //圆心 float radius; //半径 minEnclosingCircle(contours[m_count], center, radius); //根据得到的圆形和半径  绘制圆形 circle(contours_img_1, static_cast<Point>(center), (int)radius, Scalar( (rand()&255), (rand()&255), (rand()&255) ), 3); } } } imshow("src_img",src_img); imshow("contours_img-1",contours_img_1); waitKey(0); return 0; }

(0)

相关推荐

  • excel怎么提取汉字和英文?excel不使用函数提取的方法

    在我们分离汉字和英文名字时经常需要将数据分离.这里教大家不用函数的方式,简单易学. 1.首先我们打开一个工作样表作为例子. 2.我们需要将d列源数据,分离成A列和C列的数据.一般我们会使用函数提取,实 ...

  • excel不使用函数提取汉字和英文的方法

    在我们分离汉字和英文名字时经常需要将数据分离.这里教大家不用函数的方式,简单易学. 操作方法 01 首先我们打开一个工作样表作为例子. 02 我们需要将d列源数据,分离成A列和C列的数据.一般我们会使 ...

  • excel怎样提取相同内容对应的数据 excel按条件提取指定数据的方法

    excel是我们常用的办公软件,有时为了办公需要,我们会提取一些特殊的数据,那么excel怎样提取相同内容对应的数据?下面小编带来excel按条件提取指定数据的方法,希望对大家有所帮助. excel按 ...

  • 如何在Excel里用mid函数提取身份证号码里的生日

    在工作中,怎样快速,准确的录入员工的生日呢?下面和大家分享下用Mid函数提取身份证号码里的生日 操作方法 01 打开表格 02 点击C2单元格 03 输入函数"=mid(B2,7,8)&qu ...

  • Excel技巧16如何LEFT,RIGHT函数提取特殊字段

    我们在日常生活中经常会遇见一些如地址,电话号码等字段,但是我们又需要从中提取出区号或是生市县等一些关键字段,若用复制粘贴的办法,当然能实现,但是麻烦,切容易出错,而采用left,right函数则能轻松 ...

  • 如何利用mid函数提取身份证中的出生日期?

    在我们录入大量人员信息时,如何从大量身份证中得到此人的出生年月日呢,下面我们分享一下利用mid函数来提取身份证中的年月日. 操作方法 01 我们打开已做好的数据表格,具体实际表格结构可按需要调整. 0 ...

  • excel中如何使用MID函数提取指定字符

    excel里内置了很多的函数,合理利用这些函数,能提高我们的效率,降低出错的机率.现在介绍下MID函数的使用,使用该函数会得到字符串中指定位置的字符,下面看下具体的使用. 操作方法 01 首先打开ex ...

  • EXCEL2007 vlookup函数公式#N/A解决方法

    办公软件EXCEL2007 vlookup函数公式提示#N/A错误,说明当在函数或公式中没有可用数值,那我们很明确的是这个函数公式是错误的,我现在用=VLOOKUP(A:A,Sheet3!A:B,2, ...

  • Eclipse中设置选中变量和函数的高亮颜色的方法

    Eclipse中设置选中变量和函数的高亮颜色的方法 方法: 1.在Eclipse中依次点击[Window]--[Preferences],打开[Preferences]窗体. 2.在[Preferen ...