1.OpenCV轮廓提取算法详解findContours()
2.OpenCV函数:提取轮廓相关函数使用方法
3.opencv如何获得所有像素点的函数函数坐标?
4.Opencv findcontours函数原理,以及python numpy实现
5.opencv(3):findcontours讲解及实例
OpenCV轮廓提取算法详解findContours()
在OpenCV中,源码原理findContours()函数实现了一种名为"边界跟随"的函数函数轮廓提取算法,它源自Satoshi Suzuki等人年发表的源码原理论文。该论文专注于二值图像的函数函数轮廓分析。算法的源码原理源码减法计算符号怎么判断核心是分析图像中1像素(1连通域)和0像素(0连通域)的结构,以及它们之间的函数函数环绕关系。
首先,源码原理定义了基础概念,函数函数如frame(图像边缘)、源码原理0-pixel和1-pixel,函数函数以及4(8)连通性。源码原理在4(8)连通场景中,函数函数1像素的源码原理边界点是其8(4)邻域中包含0像素的点。环绕连通域和外边界/孔边界的函数函数概念用于描述不同连通域之间的关系。
算法从光栅扫描(从左到右,从上到下)开始,从框架边缘作为边界开始点,使用NBD和LNBD编号跟踪边界。在扫描过程中,遇到0像素点时,dic技术源码会根据其位置和相邻像素判断是外边界、孔边界还是其他情况,然后按照特定规则进行边界跟踪,包括顺时针和逆时针搜索非0像素点。
举例来说,如图3所示,算法会从第一个非0像素(如图中红色的1)开始,按步骤进行边界查找和更新。每条新边界都会被赋予一个唯一的编号,同时记录父边界和拓扑关系。findContours()函数的hierarchy参数存储了这些拓扑关系,而contours参数则包含了提取的轮廓,通过mode和method参数可以控制轮廓的提取细节。
总之,OpenCV的findContours()函数通过这种细致的边界分析,有效地从二值图像中提取出连通域的轮廓,对于图像处理和分析至关重要。
OpenCV函数:提取轮廓相关函数使用方法
0、绪:
步骤:
一、findContours()查找轮廓;
二、软件公园源码drawContours()画轮廓;
三、轮廓填充;
四、计算轮廓的面积和周长;
五、提取轮廓凸包,矩形,最小外接矩形,外接圆
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_TC_L1,CV_CHAIN_QPPROX_TC_KCOS:使用Teh-Chin链逼近算法中的10米源码一个。 CV_LINK_RUNS:与上述的算法完全不同,连接所有的水平层次的轮廓。 注:findContours()查找时,这个图像会被直接涂改,因此如果是以后有用的图像,应该复制之后再进行查找;
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,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点
轮廓填充: 步骤: a) 依次遍历轮廓点,将点绘制到img上; b) 使用floodFill以及一个种子点进行填充; 两种方法:自己编写程序;使用drawContours()函数; void drawMaxAreaLine(Mat dst, vectorPoint 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) = ; } } //孔洞填充算法 void fillHole(Mat src_Bw, Mat dst_Bw) { Size m_Size = src_Bw.size(); Mat Temp=Mat::zeros(m_Size.height+,m_Size.width+,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()); Mat cutImg; Temp(Range(5, m_Size.height + 5), Range(5, m_Size.width + 5)).copyTo(cutImg); dst_Bw = src_Bw | (~cutImg); } 注:这里常会碰到种子点不好选取的问题,因为有时候所选择的种子点不能保证对所有轮廓都适用。也就是查找一个在轮廓内的点是存在一定难度的。 使用drawContours()就会很方便: vectorvectorPoint 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), ); drawContours(savedGrayMat, contours, 0, Scalar(), CV_FILLED); imshow("savedGrayMat", savedGrayMat); waitKey();
计算轮廓的面积和周长: ①计算轮廓面积: 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()来得到面积的绝对值;
提取轮廓凸包,矩形,最小外接矩形,外接圆 ①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, , 0)); ③minAreaRect():提取轮廓的最小外包矩形; 主要求包含点集最小面积的矩形,这个矩形是可以有偏转角度的,可以与图像的边界不平行; 格式: RotatedRect minAreaRect(InputArray points) 输入:二维点集,点的序列或向量 (Mat) 返回:RotatedRect ④minEnclosingcircle():提取轮廓的最小外包圆; 格式: void minEnclosingcircle(InputArray points,Point2f center,float radius) 输入: 二维点集:点的序列vector point 或向量 (Mat) , 圆心坐标; 半径;
示例: #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\\.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, , THRESH_OTSU); imshow("1",threshold_img); vector vectorPoint contours; vectorVec4i 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(,,); //Scalar color( (rand()), (rand()), (rand()) );//随机生成颜色 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 = g_dConArea ) { m_count = i; //Scalar color( (rand()), (rand()), (rand()) );//随机生成颜色 //drawContours(contours_img_1, contours, m_count, color, CV_FILLED, 8, hierarchy );//对轮廓内部着色 //计算凸包 //vectorint 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, , 0), 1,8); // pt0 = pt; //} ////寻找外包矩阵 //Rect maxRect = boundingRect(contours[m_count]); ////绘制外包矩阵 //rectangle(contours_img_1, maxRect, Scalar(0, , 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,), 3); //} //line(contours_img_1, fourPoint2f[0], fourPoint2f[3], Scalar(0, 0, ), 3); //在生成的那些随机点中寻找最小包围圆形 Point2f center; //圆心 float radius; //半径 minEnclosingCircle(contours[m_count], center, radius); //根据得到的圆形和半径 绘制圆形 circle(contours_img_1, static_castPoint(center), (int)radius, Scalar( (rand()), (rand()), (rand()) ), 3); } } } imshow("src_img",src_img); imshow("contours_img-1",contours_img_1); waitKey(0); return 0; }
opencv如何获得所有像素点的坐标?
cv::findContours( ) 这个函数是用来找轮廓的,我经常用,应该能解决你这个问题。 这个函数找到的线存储的数据结构是std::vector<std::vector<cv::Point> > ,这个里面就有你想要的所有的轮廓点的坐标。
Opencv findcontours函数原理,以及python numpy实现
OpenCV的Findcontours()函数原理来源于一篇名为《Topological Structural Analysis of Digitized Binary Images by Border Following》的论文。该论文详细介绍了算法的实现,并提供了算法的源代码。
在论文中,对于一些关键定义进行了阐述:
1. 轮廓点:在一个4-或8-邻域内,如果存在一个像素为0的点,则该点为轮廓点。
2. 连通区域的环绕:对于两个相邻的连通区域S1和S2,如果S1上任意一个点的四个方向都能到达S2,那么S2环绕S1。
3. 外轮廓和孔轮廓:外轮廓是指像素为1的连通域内被像素为0的连通域环绕的轮廓点,孔轮廓是指像素为0的连通区域被像素为1的连通区域环绕的轮廓点。
4. 父轮廓:定义了层级关系,例如,对于一个像素为1的连通区域S1和一个像素为0的连通区域S2,如果S2环绕S1,则S1的父轮廓为环绕S2的值为1的像素。
轮廓扫描过程中,从左到右、从上到下的顺序扫描,根据扫描到的边界起始点判断轮廓类型。找到起始点后,根据上一个轮廓的编号判断父轮廓。最后,通过border following找到该轮廓的所有点。
在实现过程中,需要定义输入,初始化NBD为1,LNBD为1。在每一行扫描开始时,LNBD重置为1。根据当前扫描到的像素值,不断更新当前点,并绕着该点逆时针旋转寻找下一点,并不断更新像素值。
在实现过程中,遇到了一个问题,即当像素左边和右边同时为0时,需要进行特殊处理。因为轮廓是逆时针寻找,所以可以通过寻找的方位判断该赋值NBD还是-NBD。具体实现可以参考代码。
修正后,结果与论文一致。有兴趣的朋友可以查看代码。
结果图展示了轮廓编号、frame边缘、子轮廓、父轮廓、轮廓开始索引和轮廓类型等信息。
opencv(3):findcontours讲解及实例
OpenCV中的findContours函数是图像处理中的关键工具,用于查找图像中的轮廓信息。这个函数在识别目标、筛选特征和形态分析中扮演着重要角色。
findContours函数的基本用法是:
1. 它接收一个二值单通道图像(通常是Canny边缘检测后的图像),将黑色定义为背景,白色为识别对象。
2. 结果通过contours参数返回,这是一个轮廓数组,每个轮廓由一个3维点向量表示,记录了轮廓的每个点坐标。
3. hierarchy参数则存储轮廓之间的关系,包括父轮廓、内嵌轮廓等,每个轮廓对应一组4个索引值。
函数的输入参数还包括检索模式(CV_RETR_EXTERNAL、CV_RETR_LIST、CV_RETR_CCOMP、CV_RETR_TREE)和轮廓近似方法(CV_CHAIN_APPROX_NONE、CV_CHAIN_APPROX_SIMPLE等),以及可选的轮廓点偏移量。
在实际应用中,findContours常与approxPolyDP、contourArea、drawContours等函数配合使用,例如筛选出特定面积的轮廓、绘制轮廓图、提取凸包等。示例代码展示了如何使用findContours进行边缘检测、轮廓检测和筛选,以及在图像上绘制轮廓的过程。
总之,findContours是OpenCV中一个强大的轮廓处理工具,熟练掌握其用法对于图像处理任务至关重要。