精华内容
下载资源
问答
  • OpenCV22轮廓发现

    2021-04-19 21:22:55
    /*OpenCV22轮廓发现 by txwtech 简介: 轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法,所以边缘提取的阈值选定会影响最终轮廓发现结果。 1.Opencv发现轮廓的函数原型为:findContours(image, mode, ...

    /*OpenCV22轮廓发现
    by txwtech


    简介:


    轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法,所以边缘提取的阈值选定会影响最终轮廓发现结果。

    1.Opencv发现轮廓的函数原型为:findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> image, contours, hierarchy

    image参数表示8位单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像。

     

    mode参数表示轮廓检索模式:

     

    ①CV_RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略。

    ②CV_RETR_LIST:检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓。

    ③CV_RETR_CCOMP:检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层。

    ④CV_RETR_TREE:检测所有轮廓,所有轮廓建立一个等级树结构,外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。

     

    method参数表示轮廓的近似方法:

     

    ①CV_CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max (abs (x1 - x2), abs(y2 - y1) == 1。

    ②CV_CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。

    ③CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法。

    contours参数是一个list,表示存储的每个轮廓的点集合。

    hierarchy参数是一个list,list中元素个数和轮廓个数相同,每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数。

    offset参数表示每个轮廓点移动的可选偏移量。

    2.Opencv绘制轮廓的函数原型为:drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]) -> image

    imgae参数表示目标图像。

    contours参数表示所有输入轮廓。

    contourIdx参数表示绘制轮廓list中的哪条轮廓, 如果是负数,则绘制所有轮廓。

    color参数表示轮廓的颜色。

    thickness参数表示绘制的轮廓线条粗细,如果是负数,则绘制轮廓内部。

    lineType参数表示线型。

    hierarchy参数表示有关层次结构的可选信息。

    maxLevel参数表示绘制轮廓的最大级别。 如果为0,则仅绘制指定的轮廓。 如果为1,则该函数绘制轮廓和所有嵌套轮廓。 如果为2,则该函数绘制轮廓,所有嵌套轮廓,所有嵌套到嵌套的轮廓,等等。 仅当有可用的层次结构时才考虑此参数。

    offset参数表示可选的轮廓偏移参数,该参数可按指定的方式移动所有绘制的轮廓。

     

    处理过程:


    输入图像转为灰度图像cvtColor
    使用Canny进行边缘提取,得到二值图像
    使用findContours寻找轮廓
    使用drawContours绘制轮廓

     

    API介绍:


    在二值图像上发现轮廓使用API cv::findContours之后对发现的轮廓数据进行绘制显示
    drawContours(
    InputOutputArray  binImg, // 输出图像
    OutputArrayOfArrays  contours,//  全部发现的轮廓对象
    Int contourIdx// 轮廓索引号
    const Scalar & color,// 绘制时候颜色
    int  thickness,// 绘制线宽
    int  lineType ,// 线的类型LINE_8
    InputArray hierarchy,// 拓扑结构图
    int maxlevel,// 最大层数, 0只绘制当前的,1表示绘制绘制当前及其内嵌的轮廓
    Point offset=Point()// 轮廓位移,可选

    白色:rgb(255,255,255) 
    黑色:rgb(0,0,0) 
    红色:rgb(255,0,0) 
    绿色:rgb(0,255,0) 
    蓝色:rgb(0,0,255) 
    青色:rgb(0,255,255) 
    紫色:rgb(255,0,255
    */

    #include <iostream>
    #include <opencv2\opencv.hpp>
    
    using namespace std;
    using namespace cv;
    
    void Contours_Func(int,void*);
    char *out_win = "轮廓发现结果";
    int threshold_min = 50;
    int threshold_max = 255;
    Mat src_gray;
    Mat dst;
    Mat src;
    int main(int argc, char* argv[])
    {
    	
    	src = imread("e:\\pictures\\米老鼠1.jpg",CV_LOAD_IMAGE_COLOR);
    	if (!src.data)
    	{
    		printf("failed to load image");
    		return -1;
    	}
    	namedWindow("原图",CV_WINDOW_AUTOSIZE);
    	imshow("原图",src);
    	cvtColor(src,src_gray,CV_BGR2GRAY);
    
    	namedWindow(out_win,CV_WINDOW_AUTOSIZE);
    
    	const char* trackbar_name = "treshold:";
    	createTrackbar(trackbar_name,out_win,&threshold_min,threshold_max,Contours_Func);
    	Contours_Func(0,0);
    	waitKey(0);
    
    	return 0;
    }
    void Contours_Func(int, void*)
    {
    	Mat canny_output;
    	vector<vector<Point>> contours;
    	vector<Vec4i> hierachy;
    	//边缘检测
    	//src_gray,是输入图像
    	Canny(src_gray,canny_output,threshold_min,threshold_min*2,3,false);
    	//发现轮廓
    	//findContours(canny_output,contours,hierachy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
    	//findContours(canny_output, contours, hierachy, CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
    	findContours(canny_output, contours, hierachy, CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE, Point(0, 0));
    	
    	dst = Mat::zeros(src.size(),CV_8UC3);
    	RNG rng(12345);
    
    	for (size_t i = 0; i < contours.size(); i++)
    	
    	{
    		//Scalar color = Scalar(rng.uniform(0,255),rng.uniform(0,255), rng.uniform(0, 255));
    		//Scalar color = Scalar(255, 0, 0);//蓝色
    		Scalar color = Scalar(255, 255, 255);//白色
    		//画出轮廓
    		drawContours(dst,contours,i,color,2,8,hierachy,0,Point(0,0));
    	}
    	imshow(out_win,dst);
    
    }

     

     

    展开全文
  • 学习OpenCV 13 轮廓

    2019-04-18 12:55:12
    学习OpenCV 13 轮廓 Canny之类的边缘检测算法可以根据像素间差异检测出轮廓边界的像素,但它并没有将轮廓作为一个整体进行处理。而我们的任务就是要将这些边缘像素合成轮廓轮廓查找Contour Finding 一个轮廓...

    学习OpenCV 13 轮廓

    Canny之类的边缘检测算法可以根据像素间差异检测出轮廓边界的像素,但它并没有将轮廓作为一个整体进行处理。而我们的任务就是要将这些边缘像素合成轮廓。

    轮廓查找Contour Finding

    一个轮廓对应一系列点,这些点以一定方式表示图像中的一条曲线。有多种方式可以表示一条曲线,在OpenCV中,轮廓用标准模板库(STL)向量vector<>表示,向量中每个值都包含轮廓上下一个点的位置信息,用一系列二维顶点(vector<cv::Point>或vector<cv::Point2f>)表示轮廓是最常见的方式,但不限于此。

    cv::findContours()从二值图像(binary image)中计算轮廓,它处理的图像可以是从cv::Canny()函数得到的有边缘像素的图像,或是从cv::threshold()及cv::adaptiveThreshold()函数得到的图像。

    轮廓层次Contour Hierarchies

    下图分别是一张输入cv::findContours()函数的测试图像(左图)。图中有五块颜色区域(A,B,C,D,E),共有9条轮廓。每条轮廓都有一组输出列表表示(右上角图—轮廓参数)。也可以选择生成一组层次表达(右下角图—层次参数)。在右下角图中(轮廓树),节点表示一条轮廓,根据每个点在层次队列中的四元数组索引,连接都做了相应标记。

    轮廓层次列表中四元数组

    OpenCV用数组(通常vector)表示轮廓树,其中每个值都代表一条特定轮廓。在该数组中,每个值都包含一个四整数组,即层次列表中的每个节点都由四个整数组成,每个数都代表在层次列表中与当前节点有特定关系的另一个节点。

    轮廓层次列表中四元素数组中每个元素含义
    索引Index含义Meaning
    0同级下一条轮廓Next Contour
    1同级前一条轮廓Previous Contour
    2下级的第一个子节点First Child
    3上级的父节点Parent

     

     

     

     

     

     

    而当节点间不存在这种关系时,数组中相应元素设为-1,如根节点的3号元素的值将设为-1,因为根节点没有父节点。

    使用cv::findContours()查找轮廓

    void cv::findContours(
        cv::InputArray             image,              //输入8位单通道二值图像
        cv::OutputArrayOfArrays    contours,           //输出轮廓数组
        cv::OutputArray            hierarchy,          //(可选项)输出轮廓树结构
        int                        mode,               //轮廓提取方式,有4种
        int                        method,             //轮廓如何被表达
        cv::point                  offset=cv::Point()  //(可选项)偏移
    )
    
    void cv::findContours(
        cv::InputArray             image,              //输入8位单通道二值图像
        cv::OutputArrayOfArrays    contours,           //输出轮廓数组
        int                        mode,               //轮廓提取方式,有4种
        int                        method,             //轮廓如何被表达
        cv::point                  offset=cv::Point()  //(可选项)偏移
    )
    
    

    第一个参数输入图像必须是8位单通道二值图像。

    第二个参数是输出轮廓数组,在一个轮廓vector中,contours[i]是一条轮廓,contours[i][j]是contours[i]中的一个点。

    参数hierarchy是可选项,输出轮廓的树结构。这一输出为一个数组,每个轮廓对应数组中一个值,每个值都是上面的四元数组。

    参数mode告诉OpenCV轮廓提取方式,mode有4种:

    1. cv::RETR_EXTERNAL 只检索最外层轮廓。
    2. cv::RETR_LIST 检索所有轮廓并保存到表中。
    3. cv::RETR_CCOMP 检索所有轮廓并组织成双层结构。
    4. cv::RETR_TREE 检索所有轮廓并重新建立网状轮廓结构。

    绘制轮廓Drawing Contours

    void cv::drawContours(
        cv::InputOutputArray    image,                //待绘制轮廓的图像
        cv::InputArrayOfArrays  contours,             //要绘制的轮廓
        int                     contourIdx,           //正数则对应轮廓被绘制,负则所有轮廓被绘制
        const cv::Scalar&       color,                //轮廓颜色
        int                     thickness=1,          //轮廓粗细
        int                     lineType=8,           //轮廓的线种类
        cv::InputArray          hierarchy=noArray(),  //层次
        int                     maxLevel=INT_MAX,     //层次深度
        cv::Point               offset=cv::Point()    //偏移
    )
    

    绘制轮廓实例

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    Mat g_gray, g_binary;//灰度图像,二值图像
    int g_thresh = 100;//阈值
    
    void on_trackbar(int, void*) {
    	threshold(g_gray, g_binary, g_thresh, 255, THRESH_BINARY);
    	vector<vector<Point>>contours;
    	findContours(g_binary, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
    	g_binary = Scalar::all(0);
    
    	drawContours(g_binary, contours, -1, Scalar::all(255));
    	imshow("Coutours", g_binary);
    }
    
    int main(int argc, char**argv) {
    	Mat src = imread("D://somephotos//test.jpg");
    	namedWindow("Resource", 1);
    	imshow("Resource", src);
    	cvtColor(src, g_gray, COLOR_BGR2GRAY);
    	namedWindow("Contours", 1);
    
    	createTrackbar("Threshold", "Contours", &g_thresh, 255, on_trackbar);
    	on_trackbar(0, 0);
    
    	waitKey();
    
    	return 0;
    }
    

     

     

     

     

    展开全文
  • OpenCV实现轮廓的发现

    2021-01-01 12:43:39
    前言:  当我们通过阈值分割提取到图像中的目标物体后,我们就需要通过边缘检测来提取目标物体的轮廓,使用这两种方法基本能够确定物体的边缘或者前景。... 在OpenCV中,提供了一个函数返回或者输出一个有序的点集或
  • opencv图像轮廓

    2015-03-24 16:15:33
    openCV中一般用序列来存储轮廓信息.序列中的每一个元素是曲线中一个点的位置.关于序列表示的轮廓细节将在后面讨论,现在只要简单把轮廓想象为使用CvSeq表示的一系列的点就可以了. 函数cvFindContours()从二值...

    查找轮廓

    轮廓到底是什么?一个轮廓一般对应一系列的点,也就是图像中的一条曲线.表示的方法可能根据不同情况而有所不同.有多重方法可以表示曲线.在openCV中一般用序列来存储轮廓信息.序列中的每一个元素是曲线中一个点的位置.关于序列表示的轮廓细节将在后面讨论,现在只要简单把轮廓想象为使用CvSeq表示的一系列的点就可以了.

    函数cvFindContours()从二值图像中寻找轮廓.cvFindContours()处理的图像可以是从cvCanny()函数得到的有边缘像素的图像,或者是从cvThreshold()及cvAdaptiveThreshold()得到的图像,这时的边缘是正和负区域之间的边界.


    图8-2描述了cvFindContours的函数功能,图像的上半部分是神色背景和白色区域(被从A到E标记)的测试图像.下半部分是使用cvFindCountours()函数后会得到的轮廓的说明.这些轮廓被标记为cX或hx,"c"表示"轮廓(contour)","h"表示"孔(hole)","X表述数字".其中一些轮廓用虚划线表示;表明他们是白色区域的外部边界(例如,非0区域).孔(hole)的外部边界(例如,非0区域)即白色区域的内部边界.在图中是用电线表示外部边界的.OpenCV的cvFindContours()函数可区分内部和外部边界.

    包含的概念在很多应用中都非常重要.因此.OpenCV允许得到的轮廓被聚合成一个轮廓树,从而把包含关系编码到树结构中.这个测试图的轮廓树在根节点的轮廓叫c0,孔h00和h01是它的字子节点.这些轮廓中直接包含轮廓称为他们的子节点,以此类推.

    现在来看cvFindContours()函数

    1. int  cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,  
                                  int header_size CV_DEFAULT(sizeof(CvContour)),  
                                  int mode CV_DEFAULT(CV_RETR_LIST),  
                                  int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),  
                                  CvPoint offset CV_DEFAULT(cvPoint(0,0)));  
      第一个参数 image是输入图像,图像必须是8位单通道图像,并且应该被转化成二值的(例如,所有非0像素的值都是一个定值).cvFindContours()运行的时候,这个图像会被直接涂改,因此如果是将来还有用的图像,应该复制之后再传给cvFindContours().

    storage 是内存存储器,cvFindContours()找到的轮廓记录在此内存里.正如之前所说,这个存储器的空间应该由cvCreateMemStorage()分配.

    first_contour 是指向CvSeq*的一个指针firstContour.无需动手,cvFindContours()会自动分配该指针.实际上,只要在这里传一个指针就可以了函数会自动设置.不需要分配和释放(new/delete或者malloc/free).就是这个指针(例如,*firstContour)指向轮廓树的首地址(head).cvFindContours()返回值是,找到的所有轮廓的个数

    cvSeq* firstContout = NULL;

    cvFindContours(..., &firstContour, ...);

    headerSize告诉cvFindContours()更多有关对象分配的信息,它可以被设定为sizeof(CvContour)或者sizeof(CvChain)(当近似方法参数method被设定为CV_ChAIN_CODE时使用后者).最后是mode和method参数,他们分别指定计算方法和如何计算.

    mode变量可以被设置为以下四个选项之一: CV_RETR_ExTERNAL, CV_RETR_LIST, CV_RETR_CCOMP或CV_RETR_TREE.mode的值向cvFindeContours()说明需要的轮廓类型,和希望的放回值形式.具体说来,mode的值决定把找到的轮廓如何挂到轮廓树节点变量(h_prev,h_next,v_prev和v_next)上,图8-3展示了四种可能的mode值所得到的结果的拓扑结构.


    每中情况下,结构都可以看成是被"横向"连接(h_next和h_prev)联系和被"纵向"连接(v_next和v_prev)不同的"层次".

    CV_RETR_EXTERNAL 只检测出最外的轮廓.图8-2中,只有一个最外轮廓,因此图8-3中第一个轮廓指向最外的序列,除此之外没有别的连接

    CV_RETR_LIST 检测所有的轮廓并将他们保存到表(list)中.图8-3描绘了从图8-2样图中得到的表.在这个例子中,有8条轮廓被找到,他们相互之间有h_prev和h_next连接(这里并没有使用v_prev和v_next)

    CV_RETR_CCOMP 检出所有的轮廓并将他们组织成双层结构(two-level hierarchy),顶层边界是所有成份的外界边界,第二层边界是空的边界.图8-3中,我们能看到5个外部边界,其中3个包含孔.孔被v_next和v_prev可以只包括一个值,此节点可以只有一个子节点.c0中有两个孔,因为v_next可以值包括一个值,次节点可以只有一个子节点.c0之内的所有孔相互间有h_prev和h_next指针连接.

    CV_RETR_TREE 检出所有轮廓并且重新建立网状的轮廓结构.在我们给出的例子中(图8-2和8-3中),这意味着根节点是最外的轮廓c0.c0之下是空h00,在同一层次中与另一个孔h01相连接.同理,每个孔都有子节点(相对应的是c000和c010),这些子节点与父节点被垂直连接起来.这个步骤一直持续到图像最内层的轮廓,这些轮廓会成为树叶节点.

    以下的五个值与方法相关(例如轮廓会如何被近似).

     CV_CHAIN_CODE 用freeman链码输出轮廓,其他方法输出多边形(顶点的序列)

    CV_CHAIN_APPROX_NONE 将链码编码中的所有点转换为点

    CV_CHAIN_APPROX_SIMPLE 压缩水平,垂直或斜的部分,只保存最后一个点

    CV_CHAIN_APPROX_TC89_L1或CV_CHAIN_APPROX_TC89_KCOS使用Ten-Chin链逼近算法中的一个

    CV_LINK_RUNS 与上述算法完全不同的算法,连接所有水平层次的轮廓.此方法只可与Cv_RETR_LIST搭配使用.

    使用序列表示轮廓

    当调用cvFindContours函数的时候,返回多个序列.序列的类型依赖与调用cvFindContours时 所传递的参数.默认情况下使用CV_RETR_LIST和CV_CHAIN_APPROX_SIMPLE参数.

    序列中保存一系列的点,这些点构成轮廓,轮廓是本章的重点.轮廓只是序列所能表示物体的一种.轮廓的点的序列,可以用来表示图像空间中的曲线.这种点的序列很常用,所有需要有专门的函数来帮助我们对他进行处理.下面是一组这样的处理函数.

    1. int  cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,    
    2.                             int header_size CV_DEFAULT(sizeof(CvContour)),    
    3.                             int mode CV_DEFAULT(CV_RETR_LIST),    
    4.                             int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),    
    5.                             CvPoint offset CV_DEFAULT(cvPoint(0,0)));    
    int  cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,  
                                int header_size CV_DEFAULT(sizeof(CvContour)),  
                                int mode CV_DEFAULT(CV_RETR_LIST),  
                                int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),  
                                CvPoint offset CV_DEFAULT(cvPoint(0,0)));  
    1. CvContourScanner  cvStartFindContours( CvArr* image, CvMemStorage* storage,    
    2.                             int header_size CV_DEFAULT(sizeof(CvContour)),    
    3.                             int mode CV_DEFAULT(CV_RETR_LIST),    
    4.                             int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),    
    5.                             CvPoint offset CV_DEFAULT(cvPoint(0,0)));    
    CvContourScanner  cvStartFindContours( CvArr* image, CvMemStorage* storage,  
                                int header_size CV_DEFAULT(sizeof(CvContour)),  
                                int mode CV_DEFAULT(CV_RETR_LIST),  
                                int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),  
                                CvPoint offset CV_DEFAULT(cvPoint(0,0)));  
    1. CvSeq*  cvFindNextContour( CvContourScanner scanner );    
    CvSeq*  cvFindNextContour( CvContourScanner scanner );  
    1. void  cvSubstituteContour( CvContourScanner scanner, CvSeq* new_contour );    
    void  cvSubstituteContour( CvContourScanner scanner, CvSeq* new_contour );  
    1. /* Releases contour scanner and returns pointer to the first outer contour */    
    2. CvSeq*  cvEndFindContours( CvContourScanner* scanner );    
    /* Releases contour scanner and returns pointer to the first outer contour */  
    CvSeq*  cvEndFindContours( CvContourScanner* scanner );  
    1. /* Approximates a single Freeman chain or a tree of chains to polygonal curves */    
    2. CvSeq* cvApproxChains( CvSeq* src_seq, CvMemStorage* storage,    
    3.                             int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),    
    4.                             double parameter CV_DEFAULT(0),    
    5.                             int  minimal_perimeter CV_DEFAULT(0),    
    6.                             int  recursive CV_DEFAULT(0));    
    /* Approximates a single Freeman chain or a tree of chains to polygonal curves */  
    CvSeq* cvApproxChains( CvSeq* src_seq, CvMemStorage* storage,  
                                int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),  
                                double parameter CV_DEFAULT(0),  
                                int  minimal_perimeter CV_DEFAULT(0),  
                                int  recursive CV_DEFAULT(0));  
    第一函数是cvFindContours(),在前面已经提到.接着是cvStartFindContiors()函数,它和cvFin的Contours()功能类似.但是cvStartFindContours()每次放回一个轮廓,而不像cvFinContours()那样一次查找所有轮廓然后统一返回.调用cvStartFindContours()函数后,返回一个CvSequenceScanner结构.CvSequenceScanner结构中包含一些状态信息,这些信息不可读.你可以通过在cvSequenceScabber结构上依次调用cvFinNextContour()来查找剩余的轮廓.当全部轮廓都被找完之后,cvFindNextContour()将放回NULL

    cvSubstituteContour()函数用于替换scanner指向的轮廓.该函数的一个特性是,如果参数 new_contour为NULL,那么当前的轮廓将被从Scanner指定的树或链表中删除(受影响的序列会作适当更新,来保证不会有指针指向不存在的物体).

    函数cvEndFindContour()结束轮廓查找,并且将scanner设置为结束状态.注意,scanner并没有被删除,实际上该函数返回的是指针所指序列的第一个元素.

    最后一个函数cvApproxChains()函数.该函数将Freeman链转换为多边形表示(精确转换或者近似拟合).

    Freeman链码

    一般情况下,通过cvFindCountours获取的轮廓是一系列顶点的序列.另一种不同的表达是设置method参数为CV_CHAIN_CODE,然后生成轮廓.当选者CV_CHAIN_CODE标志的时候,检测的轮廓通过Freemain链码[Freeman67](图8-4)的方式返回.在Freeman链码中,多边形被表示为一系列的位移,每一个位移有8个方向,这8个方向使用整数0到7表示.Freeman链码对于识别一些形状的物体很有帮助.如果得到的是Freeman链码,可以通过以下两个函数读出每个点

    1. void cvStartReadChainPoints( CvChain* chain, CvChainPtReader* reader );    
    void cvStartReadChainPoints( CvChain* chain, CvChainPtReader* reader );  
    1. CvPoint cvReadChainPoint( CvChainPtReader* reader );    
    CvPoint cvReadChainPoint( CvChainPtReader* reader );  

    第一个函数用来初始化Freeman链CvChainPtReader结构,第二个函数通过CvChainptReader来读每个点,CvChainPtReader对应当前状态.结构CvChain从CvSeq扩展得来.和CvContourScanner从多个轮廓间迭代一样,CvChainPtReader用于迭代一个使用Freemain链码表示轮廓中的每个点.CvChainPtReader和CvSeqReader的用法类似.如您所期望,当所有点都读完后,返回CvChainPtReader值为NULL.

    绘制轮廓

    一个经常使用的功能是在屏幕上绘制检测到的轮廓.绘制可以用cvDrawContours函数完成

    1. /* Draws contour outlines or filled interiors on the image */    
    2. void  cvDrawContours( CvArr *img, CvSeq* contour,    
    3.                              CvScalar external_color, CvScalar hole_color,    
    4.                              int max_level, int thickness CV_DEFAULT(1),    
    5.                              int line_type CV_DEFAULT(8),    
    6.                              CvPoint offset CV_DEFAULT(cvPoint(0,0)));    
    /* Draws contour outlines or filled interiors on the image */  
    void  cvDrawContours( CvArr *img, CvSeq* contour,  
                                 CvScalar external_color, CvScalar hole_color,  
                                 int max_level, int thickness CV_DEFAULT(1),  
                                 int line_type CV_DEFAULT(8),  
                                 CvPoint offset CV_DEFAULT(cvPoint(0,0)));  
    第一个参数为要绘制轮廓的图像.第二个参数是要绘制的轮廓,他不像乍看上去那么简单,他是轮廓树的根节点.其他的参数(主要是max_level)将会控制如何绘制轮廓树.下一个参数很容易理解,是绘制轮廓所用的颜色.但是hole_color那?请回忆轮廓的分类,有外轮廓,也有"洞"(图8-2中的虚划线和点线).无论绘制单个轮廓还是轮廓树中的所有轮廓,标记为"洞"的轮廓都会使用hole_color指定的颜色绘制.

    通过max_level变量可以告诉cvDrawConturs() 如何处理通过节点树变量连结到一个轮廓上的其他任何轮廓.此变量可以被设置为遍历轮廓的最大深度.因此max_level = 0表示与输入轮廓属于同意等级的所有轮廓(更具体的说,输入轮廓和与其相邻的轮廓被画出),max_level = 1表示与输入轮廓属于同一登记的所有轮廓与其子节点被画出,以此类推.如果项要画的轮廓是由cvFindContous()的CV_RETR_CCOMP或CV_RETR_TREE模式得到的话,max_level的负值也是被支持的.在这种情况下,max_level=-1表示只有输入轮廓被画出,以此类推,max_level = -2 表示输入轮廓与其直系(仅直接相连的)子节点会被画出,以此类推.

    参数thickness和line_type就如其字面含义所示.最后,我们可以给绘图程序一个偏移量,这样轮廓可以被画在指定的精确坐标上.当轮廓坐标被转换成质心坐标或其他局部坐标系的时候,这个特性非常有用.
    如果在图像上的不同感兴趣的区域多次执行cvFindContour(),然后又想将所有结果在原来大图像上显示出来,便宜量offset也很有用.相反,可以先从大图提取出一个轮廓,然后在用offset和填充,在小图像上形成和轮廓对应的蒙板(mask);

    轮廓例子

    首先创建一个窗口用于显示图像,滑动条(trackbar)用于设置阈值,然后对采二值化后的图像提取轮廓并绘制轮廓.当控制参数的滑动条变换时,图像被更新.

    1. #include "stdafx.h"     
    2. #include <cv.h>     
    3. #include <highgui.h>     
    4.     
    5. IplImage* g_image = NULL;    
    6. IplImage* g_gray = NULL;    
    7. int g_thresh = 100;    
    8. CvMemStorage* g_storage = nullptr;    
    9. void on_trackbar(int)    
    10. {    
    11.     if (g_storage == nullptr)    
    12.     {    
    13.         g_gray = cvCreateImage(cvGetSize(g_image),8,1);    
    14.         g_storage = cvCreateMemStorage(0);    
    15.     }    
    16.     else    
    17.     {    
    18.         cvClearMemStorage(g_storage);    
    19.     }    
    20.     CvSeq* contours = NULL;    
    21.     cvCvtColor(g_image,g_gray,CV_BGR2GRAY);    
    22.     cvThreshold(g_gray,g_gray,g_thresh,255,CV_THRESH_BINARY);    
    23.     cvFindContours(g_gray,g_storage,&contours);    
    24.     cvZero(g_gray);    
    25.     if (contours)    
    26.     {    
    27.         cvDrawContours(g_gray,contours,cvScalarAll(255),cvScalarAll(255),100);    
    28.     }    
    29.     cvShowImage("Contours",g_gray);    
    30. }    
    31.     
    32. int _tmain(int argc, _TCHAR* argv[])    
    33. {    
    34.     g_image = cvLoadImage("C:\\Users\\chenchao\\Desktop\\细胞图象\\正常的红细胞\\5.bmp");    
    35.     cvNamedWindow("Contours",1);    
    36.     cvCreateTrackbar("Threshold","Contours",&g_thresh,300,on_trackbar);    
    37.     on_trackbar(0);    
    38.     cvWaitKey(0);    
    39.     printf("HELLO");    
    40.     return 0;    
    41. }    
    #include "stdafx.h"  
    #include <cv.h>  
    #include <highgui.h>  
      
    IplImage* g_image = NULL;  
    IplImage* g_gray = NULL;  
    int g_thresh = 100;  
    CvMemStorage* g_storage = nullptr;  
    void on_trackbar(int)  
    {  
        if (g_storage == nullptr)  
        {  
            g_gray = cvCreateImage(cvGetSize(g_image),8,1);  
            g_storage = cvCreateMemStorage(0);  
        }  
        else  
        {  
            cvClearMemStorage(g_storage);  
        }  
        CvSeq* contours = NULL;  
        cvCvtColor(g_image,g_gray,CV_BGR2GRAY);  
        cvThreshold(g_gray,g_gray,g_thresh,255,CV_THRESH_BINARY);  
        cvFindContours(g_gray,g_storage,&contours);  
        cvZero(g_gray);  
        if (contours)  
        {  
            cvDrawContours(g_gray,contours,cvScalarAll(255),cvScalarAll(255),100);  
        }  
        cvShowImage("Contours",g_gray);  
    }  
      
    int _tmain(int argc, _TCHAR* argv[])  
    {  
        g_image = cvLoadImage("C:\\Users\\chenchao\\Desktop\\细胞图象\\正常的红细胞\\5.bmp");  
        cvNamedWindow("Contours",1);  
        cvCreateTrackbar("Threshold","Contours",&g_thresh,300,on_trackbar);  
        on_trackbar(0);  
        cvWaitKey(0);  
        printf("HELLO");  
        return 0;  
    }  
    如果全局参数g_storage为NULL的话,则用cvCreateMemSotrage(0)创建一个内存存储器.g_gray被初始化为和g_image同样大小的黑色图像,但是为单通道图像.如果g_storage非空的话,则先用cvClearMemStorage清空内存存储器的中间,这样以便重复利用内存存储器中的资源.然后创建一个CvSeq*指针,该指针用来保存cvFindCountours()检测到的轮廓.

    然后g_image被转换为灰度图像,接着用g_thresh为参数进行二值化处理,得到的二值图像保存在g_gray中.cvFindContours从二值图像g_gray查找轮廓,然后将得到的轮廓用cvDrawContours()函数绘制为白色到灰度图像.最终图像在窗口中显示出来,并将在回调函数开始处申请的结构释放.

    另一个轮廓的例子

    在上例中,我们检测出输入图像的轮廓,然后逐个绘制没格轮廓.从这个例子中,我们可以了解到轮廓检测方法(如代码中是CV_RETR_LIST)以及max_depth(代码中是0)等参数的细节.如果设置的max_depth是一个比较大的值,你可以发现cvFindCountours()返回的轮廓是通过h_next连接被遍历.对于其他一些拓扑结构(CV_RETR_TREE,CV_REER_CCOMP等),你会发现有些轮廓被画过不只一次

    例8-3 在输入图像上寻找并绘制轮廓

    1. int _tmain(int argc, _TCHAR* argv[])    
    2. {    
    3.     cvNamedWindow("src");    
    4.     IplImage* img_8uc1 = cvLoadImage("C:\\Users\\chenchao\\Desktop\\细胞图象\\正常的红细胞\\5.bmp",0);    
    5.     IplImage* img_edge = cvCreateImage(cvGetSize(img_8uc1),8,1);    
    6.     IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);    
    7.     cvThreshold(img_8uc1,img_edge,128,255,CV_THRESH_BINARY);    
    8.     CvMemStorage* storage = cvCreateMemStorage(0);    
    9.     CvSeq* first_contour = nullptr;    
    10.     int Nc = cvFindContours(img_edge,storage,&first_contour,sizeof(CvContour),CV_RETR_LIST);    
    11.     int n = 0;    
    12.     printf("Total Contours Detected : %d \n",Nc);    
    13.     for (CvSeq* c = first_contour; c!= NULL; c= c->h_next)    
    14.     {    
    15.         cvCvtColor(img_edge,img_8uc3,CV_GRAY2BGR);    
    16.         cvDrawContours(img_8uc3,c,cvScalar(0,255,0),cvScalar(0,0,255),0,2,8);    
    17.         printf("contours #%d\n",n);    
    18.         cvShowImage("src",img_8uc3);    
    19.         printf("   %d elements: \n",c->total);    
    20.         for (int i=0 ; i<c->total; ++i)    
    21.         {    
    22.             CvPoint* p  =  CV_GET_SEQ_ELEM(CvPoint,c,i);    
    23.             printf(" (%d,%d) \n",p->x,p->y);    
    24.         }    
    25.         cvWaitKey(0);    
    26.         n++;    
    27.     }    
    28.     
    29.     printf("Finished all contours.\n");    
    30.     cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);    
    31.     cvShowImage("src",img_8uc3);    
    32.     cvWaitKey(0);    
    33.     cvDestroyWindow("src");    
    34.     cvReleaseImage(&img_8uc1);    
    35.     cvReleaseImage(&img_8uc3);    
    36.     cvReleaseImage(&img_edge);    
    37.     return 0;    
    38. }    
    int _tmain(int argc, _TCHAR* argv[])  
    {  
        cvNamedWindow("src");  
        IplImage* img_8uc1 = cvLoadImage("C:\\Users\\chenchao\\Desktop\\细胞图象\\正常的红细胞\\5.bmp",0);  
        IplImage* img_edge = cvCreateImage(cvGetSize(img_8uc1),8,1);  
        IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);  
        cvThreshold(img_8uc1,img_edge,128,255,CV_THRESH_BINARY);  
        CvMemStorage* storage = cvCreateMemStorage(0);  
        CvSeq* first_contour = nullptr;  
        int Nc = cvFindContours(img_edge,storage,&first_contour,sizeof(CvContour),CV_RETR_LIST);  
        int n = 0;  
        printf("Total Contours Detected : %d \n",Nc);  
        for (CvSeq* c = first_contour; c!= NULL; c= c->h_next)  
        {  
            cvCvtColor(img_edge,img_8uc3,CV_GRAY2BGR);  
            cvDrawContours(img_8uc3,c,cvScalar(0,255,0),cvScalar(0,0,255),0,2,8);  
            printf("contours #%d\n",n);  
            cvShowImage("src",img_8uc3);  
            printf("   %d elements: \n",c->total);  
            for (int i=0 ; i<c->total; ++i)  
            {  
                CvPoint* p  =  CV_GET_SEQ_ELEM(CvPoint,c,i);  
                printf(" (%d,%d) \n",p->x,p->y);  
            }  
            cvWaitKey(0);  
            n++;  
        }  
      
        printf("Finished all contours.\n");  
        cvCvtColor(img_8uc1,img_8uc3,CV_GRAY2BGR);  
        cvShowImage("src",img_8uc3);  
        cvWaitKey(0);  
        cvDestroyWindow("src");  
        cvReleaseImage(&img_8uc1);  
        cvReleaseImage(&img_8uc3);  
        cvReleaseImage(&img_edge);  
        return 0;  
    }  

    深入分析轮廓

    多边形逼近

    当我们绘制一个多边形或者进行形状分析的时候,通常需要使用多边形毕竟一个轮廓,使顶点数目变少.有多种方法可以实现这个功能,OpenCV实现了其中的一种逼近算法.函数cvApproxPoly是该算法的一种实现,可以处理轮廓的序列.

    1. (CvSeq*)  cvApproxPoly( const void* src_seq,    
    2.                              int header_size, CvMemStorage* storage,    
    3.                              int method, double eps,    
    4.                              int recursive CV_DEFAULT(0));    
    (CvSeq*)  cvApproxPoly( const void* src_seq,  
                                 int header_size, CvMemStorage* storage,  
                                 int method, double eps,  
                                 int recursive CV_DEFAULT(0));  
    我们可以传递一个列表或者数状序列给cvApproxPoly,然后对其表示的轮廓进行处理.函数返回值对应第一个轮廓,同样我们可用通过h_next(以及v_next)来访问返回其他的轮廓.

    因为cvApproxPoly在返回结果的时候需要创建新的对象,因此 需要指定一个内存存储器以及头结构大小.(一般为sizeof(CvContour)).

    逼急算法目前只可使用CV_POLY_APPROx_DP.另外两个参数为逼近算法参数(目前只用到第一个).eps参数指定逼近的精度.如果想了解这个参数如何起作用的的必须仔细了解具体的算法.最后一个参数指定是否针对全部的轮廓(通过h_next和v_next可达的)进行逼近

    如果为0,则表示只处理src_seq指向轮廓.

    下面简要介绍一下算法的工作原理.参考图8-5,算法先从轮廓(图b)选择2个最远的点,然后将2个连成一个线段(图c),然后再查找轮廓上到线段距离最远的点,添加到逼近后的心轮廓(图d).算法反复迭代,不断将最远点的添加到结果中.直到所有点的点到多边形的最短距离小于eps参数指定的精度(图f).从这里可以看出,精度和轮廓的周长,或者外包矩形周长的几分之一比较合适.

    曲线逼近的过程和寻找关掉点的过程密切相关。跟曲线上的其他点相比,关键点是那些包含曲线信息比较多的点。关键点在逼近算法以及其他应用中都会涉及。函数cvFindDominantPoints()实现了被称为IPAN*[Chetvreikov99]的算法.

    1. CvSeq  cvFindDominantPoints(CvSeq* contour,CvMemStorage* storage,int metod = CV_DOMINANT_IPAN,double parameter1 = 0,double parameter2 = 0,double parameter3 = 0,double parameter4 = 0);  
    CvSeq  cvFindDominantPoints(CvSeq* contour,CvMemStorage* storage,int metod = CV_DOMINANT_IPAN,double parameter1 = 0,double parameter2 = 0,double parameter3 = 0,double parameter4 = 0);
           本质上,IPAN算法通过扫描轮廓上并在曲线内部使用可能顶点构造三角形来实现.对于三角形的大小和张角有特殊要求.在此某一特定的全局阈值和它的相邻的张角小的情况下,具有大张角的点被保留.

    函数cvFindDominantPoints()按照惯例使用参数CvSeq* 和CvMemStorage* .并且要求指定一个方法,和cvApproxPoly()相同,目前可供选择的方法只有一个,就是CV_DOMINANT_IPAN.


    接下来四个参数是:最短距离dmin,最长距离dmax,相邻距离dn和最大角度θmax.如图8-6所示,算法首先把所有两边距离rpa和rpb在dmin和dmax之间,θab < θmax的三角形找出来.然后保留对于距离dn(dn的大小不得超过dmax)有最小夹角θab的所有点p.dmin,dmax,dn和θmax典型值可以是7,9,9,150(最后一个参数是以度数为单位的角大小).

    特性概括

    轮廓处理中经常遇到的另一个任务是计算一些轮廓变化的概括特性.这可能包括长度或者其他一些反映轮廓整体大小的度量.另一个有用的特性是轮廓的轮廓矩(contourmoment),可以用来概括轮廓的总形状特性

    长度

    函数cvContourPerimeter()作用于一个轮廓并返回其长度.事实上,此函数是一个调用函数cvArcLength()的宏.

    1. CVAPI(double)  cvArcLength( const void* curve,    
    2.                             CvSlice slice CV_DEFAULT(CV_WHOLE_SEQ),    
    3.                             int is_closed CV_DEFAULT(-1));    
    4.     
    5. CV_INLINE double cvContourPerimeter( const void* contour )    
    6. {    
    7.     return cvArcLength( contour, CV_WHOLE_SEQ, 1 );    
    8. }    
    CVAPI(double)  cvArcLength( const void* curve,  
                                CvSlice slice CV_DEFAULT(CV_WHOLE_SEQ),  
                                int is_closed CV_DEFAULT(-1));  
      
    CV_INLINE double cvContourPerimeter( const void* contour )  
    {  
        return cvArcLength( contour, CV_WHOLE_SEQ, 1 );  
    }  
    cvArcLength()的第一参数是轮廓,其形式可以是点的序列(CvContour*或CvSeq*)或任一n×2的点的数组.后边的参数是slice,以及表明是否将轮廓视为闭合的一个布尔类型(例如,是否将轮廓的最后一个点视为和第一个点有连接).slice可以让我们只选择曲线上的点的部分集合.

    一个和cvArcLength()有紧密关系的函数是cvContourArea(),如其名称所示,这个函数同于计算轮廓的面积.函数的参数contour和slice和cvArcLength()一样.

    1. CVAPI(double)  cvContourArea( const CvArr* contour,    
    2.                               CvSlice slice CV_DEFAULT(CV_WHOLE_SEQ),    
    3.                               int oriented CV_DEFAULT(0));    
    CVAPI(double)  cvContourArea( const CvArr* contour,  
                                  CvSlice slice CV_DEFAULT(CV_WHOLE_SEQ),  
                                  int oriented CV_DEFAULT(0));  

    边界框

    当然长度和面积只是轮廓的简单特性,更复杂一些的特性描述应该是矩形边界框,圆形边界框或椭圆形边界框.有两种方法可以得到矩形边界框,圆形与椭圆形编辑框各只有一种方法.

    1. CVAPI(CvRect)  cvBoundingRect( CvArr* points, int update CV_DEFAULT(0) );    
    CVAPI(CvRect)  cvBoundingRect( CvArr* points, int update CV_DEFAULT(0) );  
    1. CVAPI(CvBox2D)  cvMinAreaRect2(const CvArr* points,CvMemStorage* storage CV_DEFAULT(NULL));    
    CVAPI(CvBox2D)  cvMinAreaRect2(const CvArr* points,CvMemStorage* storage CV_DEFAULT(NULL));  

    最简单的方法是调用函数cvBoundingRect();它将放回一个包围轮廓的CvRect.第一个参数points可以是由点组成的序列,一个轮廓(CvContour*)或者一个n×1双通道的矩阵(CvMat*).为了理解第二个参数update,我们需要想想前面的描述,当时说CvContour并不完全等于CvSeq;CvSeq能实现的CvContour都可以实现,CvContour甚至能做的更多一点.其中一个附加功能就是CvRect成员可以记载轮廓自己的边界框.如果调用函数cvBoundingRect()时参数update设置为0,便可以直接从CvCoutour的成员中获取边界框;如果将uodate设置为1,边界框便会被计算出(CvContour成员的内容也会被更新).

    cvBoundingRect()得到的长方形的一个问题是,cvRect只能表现一个四边水平和竖直的长方形.然而函数cvMinAreaRect2()可以返回一个包围轮廓最小的长方形,这个长方形可能是倾斜的;请看图8-7,该函数的参数和cvBoundingRect()的相似.opencv的数据类型CvBox2D就是用来表述这样的长方形状的.

    1. typedef struct CvBox2D    
    2. {    
    3.     CvPoint2D32f center;  /* Center of the box.                          */    
    4.     CvSize2D32f  size;    /* Box width and length.                       */    
    5.     float angle;          /* Angle between the horizontal axis           */    
    6.                           /* and the first side (i.e. length) in degrees */    
    7. }    
    8. CvBox2D;    
    typedef struct CvBox2D  
    {  
        CvPoint2D32f center;  /* Center of the box.                          */  
        CvSize2D32f  size;    /* Box width and length.                       */  
        float angle;          /* Angle between the horizontal axis           */  
                              /* and the first side (i.e. length) in degrees */  
    }  
    CvBox2D;  

    圆形和椭圆形边界

    接着我们来看函数cvMinEnclosingCircle().该函数和矩形边界框的作用基本相同,输入同样很灵活,可以是点的序列,也可以是二维点的数组.

    1. CVAPI(int)  cvMinEnclosingCircle(const CvArr* points,CvPoint2D32f* center, float* radius);    
    CVAPI(int)  cvMinEnclosingCircle(const CvArr* points,CvPoint2D32f* center, float* radius);  

    OpenCV里没有专门用来表示圆的结构,因此需要给函数cvMinEnclosingCircle()传递中心和浮点型半径的两个指针来获取计算结果.

    与最小包围圆一样,OpenCV提供一函数来拟合一组点,以获取最佳拟合椭圆

    1. CVAPI(CvBox2D) cvFitEllipse2( const CvArr* points );    
    CVAPI(CvBox2D) cvFitEllipse2( const CvArr* points );  
    cvMinEnclosingCircle()和cvFitEllipse2()的细微差别在于,前者只简单计算完全包围已有轮廓的最小圆,而后者使用拟合函数返回一个与轮廓最相近似的椭圆.这意味着并不是轮廓中所有的点都会被包在cvFitEllipse2()返回的椭圆中.该拟合由最小二乘拟合方法算出.

    椭圆的拟合结果由CvBox2D结构体返回,给出的矩形正好完全包围椭圆,如图8-8所示.



    几何

    在处理CvBox2D或多边形边界的时候,经常需要进行多边形以及边界框的重叠判断.OpenCV提供了一组方便的小函数用于此类测试.

    1. CVAPI(CvRect)  cvMaxRect( const CvRect* rect1, const CvRect* rect2 );    
    2. CVAPI(void) cvBoxPoints( CvBox2D box, CvPoint2D32f pt[4] );    
    3. /* Initializes sequence header for a matrix (column or row vector) of points -  
    4.    a wrapper for cvMakeSeqHeaderForArray (it does not initialize bounding rectangle!!!) */    
    5. CVAPI(CvSeq*) cvPointSeqFromMat( int seq_kind, const CvArr* mat,    
    6.                                  CvContour* contour_header,    
    7.                                  CvSeqBlock* block );    
    8. /* Checks whether the point is inside polygon, outside, on an edge (at a vertex).  
    9.    Returns positive, negative or zero value, correspondingly.  
    10.    Optionally, measures a signed distance between  
    11.    the point and the nearest polygon edge (measure_dist=1) */    
    12. CVAPI(double) cvPointPolygonTest( const CvArr* contour,    
    13.                                   CvPoint2D32f pt, int measure_dist );    
    CVAPI(CvRect)  cvMaxRect( const CvRect* rect1, const CvRect* rect2 );  
    CVAPI(void) cvBoxPoints( CvBox2D box, CvPoint2D32f pt[4] );  
    /* Initializes sequence header for a matrix (column or row vector) of points - 
       a wrapper for cvMakeSeqHeaderForArray (it does not initialize bounding rectangle!!!) */  
    CVAPI(CvSeq*) cvPointSeqFromMat( int seq_kind, const CvArr* mat,  
                                     CvContour* contour_header,  
                                     CvSeqBlock* block );  
    /* Checks whether the point is inside polygon, outside, on an edge (at a vertex). 
       Returns positive, negative or zero value, correspondingly. 
       Optionally, measures a signed distance between 
       the point and the nearest polygon edge (measure_dist=1) */  
    CVAPI(double) cvPointPolygonTest( const CvArr* contour,  
                                      CvPoint2D32f pt, int measure_dist );  

    第一个函数cvMaxRect()根据输入的2个矩形计算,他们的最小外包矩形.

    下一个使用函数cvBoxPoints()用于计算CvBox2D结构表示矩形的4个顶点.当然你也可以自己通过三角函数计算,不过这令人头大,而简单调用一下这个函数则可求出.

    第三实用函数cvPointSeqFromMat()从mat中初始化序列.这在你需要使用轮廓相关的函数,但是函数又不支持矩阵参数的时候使用.第一个参数用于指定点序列类型,seq_kind可以为以下类型:点集为0;曲线为CV_SEQ_KIND_CURVE;封闭曲线为CV_SEQ_KIND_CURVE|Cv_SEQ_FLAG_CLOSED.第二个参数是输入的矩阵,该参数是连续的1维向量.矩阵类型必须为cv_32C2或CV_32FC2.

    下面的两个参数是指针,指针指向的内容通过该函数填充.contour_header参数对应轮廓结构,一般要事先创建,不过由该函数负责初始化.block参数同样如此,也是由该函数复杂初始化.最后,该函数放回一个类型为CvSeq*的序列指针,指向你输入的序列头*contour_header.返回值跟输入参数相同只是为了使用该函数时更方便,因为这样你就可以将该函数当作某个轮廓函数的参数使用,代码写入同一行.

    最后一个平面几个相关的函数是cvPointPolygonTest(),用于测试一个点是否在多边形的内部.如果参数measure_dist非零,函数返回值是点到多边形最近距离.如果measure_dist为0,函数返回+1,-1,0,分别表示在内部,外部,在多边形边上.参数contour可以是序列,也可以是2通道矩阵向量.

    展开全文
  • opencv寻找轮廓

    2016-06-16 13:20:14
    opencv寻找图像的轮廓

    1. 一个轮廓一般对应一系列的点,也就是图像中的一条曲线,有多种方法来表示曲线,opencv中一般用序列来存储轮廓信息。函数cvFindContours()用来从二值图像中寻找轮廓,可以来自于边缘检测后的图像,也可以是二值化后的图像,cvFindContours()可以找到图像中的外围轮廓和内部的孔洞,函数原型如下:



    第一个参数是输入图像,必须是8位的单通道的图像,所以输入的应该是二值图,下一个参数是内部存储器,找到的轮廓存储在里面,再下一个参数是一个CvSeq指针,headerSize是关于对象的信息,一般设置成sizeof(CvContour),最后两个参数是指定计算方法和如何计算。

    mode表示检索的模式有四个选项:

    CV_RETR_EXTERNAL:只检索最外面的轮廓; 

    CV_RETR_LIST:检索所有的轮廓,并将其放入list中; 

    CV_RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界; 

    CV_RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。

    method表示边缘近似方法,有五个选项:

    CV_CHAIN_CODE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。

    CV_CHAIN_APPROX_NONE:将所有的连码点,转换成点。 

    CV_CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。 

    CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用the flavors of Teh-Chin chain近似算法的一种。 

    CV_LINK_RUNS:通过连接水平段的1,使用完全不同的边缘提取算法。使用CV_RETR_LIST检索模式能使用此方法。 


    找到轮廓之后可以绘制出来,可以用函数cvDrawContours(),函数原型如下:



    第一个参数表示要绘制的图像,第二个参数表示要绘制的轮廓,第三个参数外围轮廓的颜色,第四个参数内部轮廓的颜色。 max_level 画轮廓的最大层数。如果是0,只绘制contour;如果是1,将绘制contour后和contour同层的所有轮廓;如果是2,绘制contour后所有同层和低一层的轮廓,以此类推;如果值是负值,则函数并不绘制contour后的轮廓,但是将画出其子轮廓,一直到abs(max_level) - 1层。 thickness 绘制轮廓线的宽度。如果为负值(例如,等于CV_FILLED),则contour内部将被绘制。 line_type 轮廓线段的类型。

    下面是在在一幅图像中找到轮廓并显示:

    源码:

    #include <cv.h>
    #include <highgui.h>
    
    IplImage*    g_image = NULL;
    IplImage*    g_gray = NULL;
    int        g_thresh = 100;
    CvMemStorage*  g_storage = NULL;
    //回调函数
    void on_trackbar(int) {
    	if (g_storage == NULL) {
    		g_gray = cvCreateImage(cvGetSize(g_image), 8, 1);
    		g_storage = cvCreateMemStorage(0);
    	}
    	else {
    		cvClearMemStorage(g_storage);
    	}
    	CvSeq* contours = 0;
    	cvCvtColor(g_image, g_gray, CV_BGR2GRAY);
    	cvThreshold(g_gray, g_gray, g_thresh, 255, CV_THRESH_BINARY);
    	cvFindContours(g_gray, g_storage, &contours);
    	cvZero(g_gray);
    	if (contours)
    		cvDrawContours(
    		g_gray,
    		contours,
    		cvScalarAll(255),//绘制白线
    		cvScalarAll(255),
    		100
    		);
    	cvShowImage("Contours", g_gray);
    }
    
    int main(int argc, char** argv)
    {
    	
    	g_image = cvLoadImage("99.jpg");
    	cvNamedWindow("Contours", 1);
    	cvCreateTrackbar(
    		"Threshold",
    		"Contours",
    		&g_thresh,
    		255,
    		on_trackbar
    		);
    	on_trackbar(0);
    	cvWaitKey(0);
    	return 0;
    }



    结果:



    展开全文
  • OpenCV-轮廓查找与对象测量(Python)
  • opencv查找轮廓

    2019-06-14 21:22:14
    这是一个查找轮廓算法,他的原理是从图像的0行,0列开始扫描,找到当前像素,与之前一个像素不同的像素并改变他的颜色,以作为...opencv2/opencv.hpp> using namespace std; using namespace cv; void find...
  • opencv轮廓发现

    2019-11-13 15:25:08
    opencv版本:3.4.7 编译器版本:VS2019 轮廓发现概述(find contour in your image) 轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法。所以边缘提取的阈值选定会影响最终轮廓发现的结果 相关函数API ...
  • 使用Opencv进行轮廓检测! 二. 开始编写代码 三. 字符提取 四. 文字识别 一.使用Opencv进行轮廓检测! 所需函数: 1.cvFindContours 函数功能:从二值图像中检索轮廓,并返回检测到的轮廓的个数 函数原型: ...
  • OpenCV图像轮廓(2)轮廓面积

    千次阅读 2020-09-29 09:26:19
    OpenCV的图像轮廓有一些特征,像面积、弧长(其实就是轮廓的长度)、图像矩等。 先写一下相关术语中英文参照。 轮廓contour 特征feature 面积area 弧长perimeter 图像矩image moment 通过cv2.contourArea可以求得...
  • OpenCV轮廓查找和填充

    千次阅读 2016-03-12 16:20:55
    OpenCV轮廓查找有C版本和C++版本,当轮廓比较复杂的时候,例如嵌入多层轮廓,如果方法不当那么很容易会漏处理一些轮廓。本文介绍了复杂轮廓场景下的几种主要的查找轮廓和颜色填充方法。 1:cvFindContours函数...
  • OpenCV3轮廓发现

    2020-08-05 12:55:25
    轮廓发现 轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法。所以边缘提取的阈值选定会影响最终轮廓发现结果 相关API findContours发现轮廓 drawContours绘制轮廓 函数使用以及参数详情 在二值图像上发现轮廓...
  • opencv轮廓

    2015-02-05 09:29:30
    openCV中一般用序列来存储轮廓信息.序列中的每一个元素是曲线中一个点的位置.关于序列表示的轮廓细节将在后面讨论,现在只要简单把轮廓想象为使用CvSeq表示的一系列的点就可以了. 函数cvFindContours()从二值...
  • Opencv轮廓的中心点坐标

    千次阅读 2020-12-12 16:27:13
    Opencv轮廓的中心点坐标 思路: 1.通过findContours找出图片中的轮廓; 2.minAreaRect找到最小外接矩形; 3.得到最小外接矩形的中心点坐标作为轮廓的中心坐标; cv::threshold(Imsrc, Imdest, 200, 255, cv::/*...
  • findContours函数中第三个参数hierarchy,为输出向量,包含图像的拓扑信息。 每个Contours[i]包含4个hierarchy元素,分别为:后一个轮廓,前一个轮廓,内嵌轮廓,父轮廓。 ...
  • OpenCV 矩形轮廓检测

    万次阅读 2014-12-11 11:01:03
    #include "cv.h" #include #include #include #include #include ...#pragma comment(lib,"opencv_core2410d.lib") #pragma comment(lib,"opencv_highgui2410d.lib") #pragma comment(lib,"op
  • Freeman链码表示一个轮廓,是不是也可以表示出每个轮廓点的坐标?根据这些链码怎么求轮廓的面积~~请求各位给个思路,有代码甚好~~~
  • 查找轮廓: 0.1 findContours()函数 作用: 函数FindContours()从二值图像中寻找轮廓,其处理的图像可以是从Canny()函数得到有边缘像素的图像,或者是从Threshold()得到的图像,这时的边缘是正和负区域之间的边界...
  • 如何使用opencv 绘制轮廓边框最小包裹 多边形 圆形 矩形? 函数说明: Rect boundingRect(InputArray points) points:输入信息,可以为包含点的容器(vector)或是Mat。 返回包覆输入信息的最小正矩形。 RotatedRect ...
  • Opencv查找轮廓并绘制

    千次阅读 2018-10-23 17:17:56
    将这些轮廓边缘像素组装成一个整体(轮廓),就要进行轮廓检测.opencv提供了轮廓检测的函数cvFindContours,函数参数如下:  cvFindContours(CvArr *图像,CvMemStorage *存储,CvSeq ** first_contour,  ...
  • (0)轮廓分析概述及作用通过将Canny边缘提取或者二值化结果作为输入图像来实现轮廓发现与绘制,可是这些并不是我们想要的最终结果,我们一般根据获取到的轮廓求出它们的外接矩形或者最小外接矩形,并计算外接矩形的...
  • OpenCV python 轮廓内最大最小值

    千次阅读 2020-01-14 10:42:46
    OpenCV python 轮廓内最大最小值 处理图片:[ct.jpg] import cv2 import numpy as np def get_contour(img): """获取连通域 :param img: 输入图片 :return: 最大连通域 """ # 灰度化, 二值化, 连通域分析 ...
  • opencv 查找轮廓 绘制轮廓

    千次阅读 2012-04-03 19:43:04
     *查找轮廓, 绘制轮廓.   *1)cvFindConturs 的输入必须是8bit单通道二值化图像.  *2)当block值足够大时,cvAdaptThreshold能够很好的阈值化.block_size必须为奇数!  *3)开闭运算不需要临时图像  *4)...
  • 本文参照OpenCV官网文档来写,中英文对照,略作修改。 For better accuracy, use binary images. So before finding contours, apply threshold or canny edge detection. 为了查找轮廓更加精确,请使用二值图像。...
  • opencv 获取轮廓的 质心 python实现。

    千次阅读 2020-08-28 09:11:09
    opencv 获取轮廓的 质心 python实现。 使用findContours 找到 图像轮扣后,我们需要得到轮廓的 质心,那么应该怎么做呢? 如果想根据多边形的轮廓信息得到多边形的多阶矩,可以使用类moments,这个类可以得到...
  • Opencv——轮廓相似度比较

    千次阅读 2019-07-03 18:38:08
    图片进行边缘检测、轮廓提取之后,可以进行不同轮廓之间的相似比较 主要函数: public staticdoublematchShapes(Matcontour1, Matcontour2, intmethod, ...
  • 最近用OPENCV轮廓提取函数,总结一下。不然老忘记。。。。。。。 void findContours//提取轮廓,用于提取图像的轮廓 ( InputOutputArray image,//输入图像,必须是8位单通道图像,并且应该转化成二值的 OutputArra...
  • OpenCV轮廓处理

    千次阅读 2016-07-18 13:24:06
    在Qt中使用OpenCV库,下面是轮廓处理的程序 void SecondWindow::on_pushButton_4_clicked() { Mat showMat(frame); //frame是视频取到一帧的图像 Mat imgHSV; Ipl
  • opencv中 查找轮廓的函数提供的是cv::findContours() 把查找的轮廓划到图像上cv::drawContours() 1.轮廓的查找——cv::findContours() 函数cv::findContour是从二值图像中来计算轮廓的,它可以使用cv::Canny()函数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,143
精华内容 3,257
关键字:

opencv输出轮廓