精华内容
下载资源
问答
  • OpenCV轮廓形状匹配

    2021-06-23 19:15:48
    能够编程实现形状匹配 能够掌握轮廓的几何形状拟合方法 ***— 查找轮廓 cv2.RETR_EXTERNAL 只检测外轮廓 cv2.RETR_LIST检测的轮廓不建立等级关系 cv2.RETR_CCOMP建立两个等级的轮廓 cv2.RETR_TREE建立一个等级树...

    案例来源于© Fu Xianjun. All Rights Reserved

    目标:

    1. 能够掌握轮廓查找与绘制的方法
    2. 能够掌握轮廓面积及长度的计算方法
    3. 能够编程实现形状匹配
    4. 能够掌握轮廓的几何形状拟合方法

    ***— 查找轮廓

    cv2.RETR_EXTERNAL 只检测外轮廓
    cv2.RETR_LIST检测的轮廓不建立等级关系
    cv2.RETR_CCOMP建立两个等级的轮廓
    cv2.RETR_TREE建立一个等级树结构的轮廓
    method:
    cv2.CHAIN_APPROX_NONE存储所有的轮廓点
    cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。

    文章目录

    img = cv2.imread("shape.jpg")
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #-----------#阈值处理
    ret,binary = cv2.threshold(gray,200,255,0)
    
    #-----------#查找轮廓
    contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    img = cv2.drawContours(img,contours,-1,(255,255,0),5)
    #计算图像的矩特征
    retval = cv2.moments(contours[1])
    print(retval["m00"])
    area = cv2.contourArea(contours[1])
    print(area)
    cv2.imshow("img",img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    在这里插入图片描述

    1.显示面积小于20000的轮廓

    img = cv2.imread("shape.jpg")
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #-----------#阈值处理
    ret,binary = cv2.threshold(gray,200,255,0)
    
    #-----------#查找轮廓
    contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    
    for i in range(len(contours)):
        area = cv2.contourArea(contours[i])
        if area<20000:
            img = cv2.drawContours(img,contours,i,(255,255,0),5)
            
    cv2.imshow("img",img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    2.显示长度小于600的轮廓

    img = cv2.imread("shape.jpg")
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #-----------#阈值处理
    ret,binary = cv2.threshold(gray,200,255,0)
    
    #-----------#查找轮廓
    contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    for i in range(len(contours)):
        length = cv2.arcLength(contours[i],True)
        print(length)
        if length<600:
            img = cv2.drawContours(img,contours,i,(255,255,0),5)
    cv2.imshow("img",img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    二.使用Hu特征进行形状匹配

    img1 = cv2.imread("1.png")
    gray1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
    ret,binary1 = cv2.threshold(gray1,200,255,0)
    contours1,hierarchy = cv2.findContours(binary1,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    
    img2 = cv2.imread("2.png")
    gray2 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
    ret,binary2 = cv2.threshold(gray2,200,255,0)
    contours2,hierarchy = cv2.findContours(binary2,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    
    img3 = cv2.imread("3.png")
    gray3 = cv2.cvtColor(img3,cv2.COLOR_BGR2GRAY)
    ret,binary3 = cv2.threshold(gray3,200,255,0)
    contours3,hierarchy = cv2.findContours(binary3,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    
    retval1 = cv2.matchShapes(contours1[0],contours2[0],1,0.0)
    retval2 = cv2.matchShapes(contours1[0],contours3[0],1,0.0)
    print(retval1,retval2)
    

    三.轮廓拟合

    1.矩形包围框

    img = cv2.imread("shape.jpg")
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #-----------#阈值处理
    ret,binary = cv2.threshold(gray,200,255,0)
    
    #-----------#查找轮廓
    contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    x,y,w,h = cv2.boundingRect(contours[0])
    cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),1)
    cv2.imshow("img",img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    2.最小包围矩形框

    img = cv2.imread("shape.jpg")
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #-----------#阈值处理
    ret,binary = cv2.threshold(gray,200,255,0)
    
    #-----------#查找轮廓
    contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    rect = cv2.minAreaRect(contours[1])
    print(rect)
    points = cv2.boxPoints(rect)
    print(rect)
    points = np.int64(points)
    img = cv2.drawContours(img,[points],0,(0,0,0),2)
    cv2.imshow("img",img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    3.最小包围圆形

    img1 = cv2.imread("shape.jpg")
    cv2.imshow("o",img1)
    gray1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
    ret,binary1 = cv2.threshold(gray1,200,255,0)
    contours1,hierarchy = cv2.findContours(binary1,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    (x,y),r = cv2.minEnclosingCircle(contours[1])
    center = (int(x),int(y))
    r = int(r)
    cv2.circle(img1,center,r,(0,0,0),2)
    cv2.imshow("img",img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    4.最优拟合椭圆

    import cv2
    img = cv2.imread("shape.jpg")
    cv2.imshow("o",img)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    ret,binary = cv2.threshold(gray,127,255,0)
    contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    ellipse = cv2.fitEllipse(contours[1])
    print("ellipse=",ellipse)
    cv2.ellipse(img,ellipse,(0,255,0),3)
    cv2.imshow("img",img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    5.最优拟合直线

    import cv2
    img = cv2.imread("shape.jpg")
    cv2.imshow("o",img)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    ret,binary = cv2.threshold(gray,127,255,0)
    contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,\
                                         cv2.CHAIN_APPROX_SIMPLE)
    r,c = img.shape[:2]
    [vx,vy,x,y] = cv2.fitLine(contours[1],cv2.DIST_L2,0,0.01,0.01)
    lefty = int((-x*vy/vx) + y)
    righty = int(((c-x)*vy/vx) + y)
    cv2.line(img,(c-1,righty),(0,lefty),(0,255,0),2)
    cv2.imshow("img",img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    在这里插入图片描述

    展开全文
  • OpenCV轮廓匹配

    2019-03-19 14:45:03
    把模板图像要匹配的图像(该图像可以包含多个模板图像,可以是旋转拉伸过的)路径输入后即可进行匹配,并画出匹配图像
  • OpenCV 轮廓匹配

    万次阅读 2018-09-11 10:56:54
    3.形状匹配(比较两个形状或轮廓间的相似度)——matchShapes() 先上ppt:         代码:1.计算点到轮廓的距离与位置关系     ///计算点到轮廓的距离与位置关系 #include "...

    1.计算点与轮廓的距离及位置关系——pointPolygonTest()

    2.矩的计算——moments()

    3.形状匹配(比较两个形状或轮廓间的相似度)——matchShapes()

    先上ppt:

     

     

     

     

    代码:1.计算点到轮廓的距离与位置关系

     

     
    1. ///计算点到轮廓的距离与位置关系

    2. #include "opencv2/opencv.hpp"

    3. using namespace cv;

    4. #include <iostream>

    5. using namespace std;

    6. int main()

    7. {

    8. //1.查找轮廓前的预处理

    9. Mat srcImg = imread("00.png",CV_LOAD_IMAGE_COLOR);

    10. Mat copyImg = srcImg.clone();

    11. cvtColor(srcImg,srcImg,CV_BGR2GRAY);

    12. threshold(srcImg,srcImg,100,255,CV_THRESH_BINARY);//确保黑中找白

    13. imshow("thresh",srcImg);

    14. //2.查找轮廓

    15. vector<vector<Point>> contours;

    16. findContours(srcImg,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);//最外层轮廓

    17. drawContours(copyImg, contours, -1, Scalar(0, 255, 0), 2, 8);

    18. //3.计算点到轮廓的距离与位置关系

    19. Point2f p1(20, 20);

    20. circle(copyImg,p1,3,Scalar(0,0,255),-1,8);

    21. double a0 = pointPolygonTest(contours[0], p1, true);//true表示点到轮廓的距离

    22. double b0 = pointPolygonTest(contours[0], p1, false);//false表示计算点与轮廓的位置关系

    23. cout << "a0=" << a0 << endl;

    24. cout << "b0=" << b0 << endl;

    25. imshow("contours",copyImg);

    26. waitKey(0);

    27. return 0;

    28. }

     

    运行结果:

     

    代码:2.轮廓矩的计算

     

     
    1. ///轮廓矩的计算

    2. #include "opencv2/opencv.hpp"

    3. using namespace cv;

    4. #include <iostream>

    5. using namespace std;

    6. int main()

    7. {

    8. //1.查找轮廓前的预处理

    9. Mat srcImg = imread("00.png", CV_LOAD_IMAGE_COLOR);

    10. Mat copyImg = srcImg.clone();

    11. cvtColor(srcImg, srcImg, CV_BGR2GRAY);

    12. threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY);//确保黑中找白

    13. imshow("thresh", srcImg);

    14. //2.查找轮廓

    15. vector<vector<Point>> contours;

    16. findContours(srcImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最外层轮廓

    17. drawContours(copyImg, contours, -1, Scalar(0, 255, 0), 2, 8);

    18. //3.轮廓矩的计算

    19. Moments moments0 = moments(contours[0],false);//计算轮廓矩

    20. cout << moments0.m00<< endl;//输出空间矩之一的m00

    21. imshow("contours", copyImg);

    22. waitKey(0);

    23. return 0;

    24. }

     

    运行结果:

    代码:3.形状匹配---比较两个形状或轮廓间的相似度

     

     
    1. ///形状匹配---比较两个形状或轮廓间的相似度

    2. #include "opencv2/opencv.hpp"

    3. using namespace cv;

    4. #include <iostream>

    5. using namespace std;

    6. int main()

    7. {

    8. //1.查找模版图像的轮廓

    9. Mat templateImg = imread("1.jpg", CV_LOAD_IMAGE_COLOR);

    10. Mat copyImg1 = templateImg.clone();

    11. cvtColor(templateImg, templateImg, CV_BGR2GRAY);

    12. threshold(templateImg, templateImg, 100, 255, CV_THRESH_BINARY);//确保黑中找白

    13. imshow("thresh1", templateImg);

    14. vector<vector<Point>> contours1;

    15. findContours(templateImg, contours1, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最外层轮廓

    16. drawContours(copyImg1, contours1, -1, Scalar(0, 255, 0), 2, 8);

    17. //2.查找待测试图像的轮廓

    18. Mat testImg = imread("2.jpg", CV_LOAD_IMAGE_COLOR);

    19. Mat copyImg2 = testImg.clone();

    20. cvtColor(testImg, testImg, CV_BGR2GRAY);

    21. threshold(testImg, testImg, 100, 255, CV_THRESH_BINARY);//确保黑中找白

    22. imshow("thresh2", testImg);

    23. vector<vector<Point>> contours2;

    24. findContours(testImg, contours2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最外层轮廓

    25. //3.形状匹配---比较两个形状或轮廓间的相似度

    26. for (int i = 0; i < contours2.size();i++)//遍历待测试图像的轮廓

    27. {

    28. //返回此轮廓与模版轮廓之间的相似度,a0越小越相似

    29. double a0 = matchShapes(contours1[0],contours2[i],CV_CONTOURS_MATCH_I1,0);

    30. cout << "模版轮廓与待测试图像轮廓" << i << "的相似度:" << a0 << endl;//输出两个轮廓间的相似度

    31. if (a0<0.1)//如果此轮廓与模版轮廓的相似度小于0.1

    32. {

    33. drawContours(copyImg2, contours2, i, Scalar(0, 255, 0), 2, 8);//则在待测试图像上画出此轮廓

    34. }

    35. imshow("copyImg2", copyImg2);

    36. if (waitKey(0) == 27)//等待按键进行下一个轮廓,ESC则退出

    37. {

    38. cout << "ESC退出" << endl;

    39. break;

    40. }

    41. }

    42. waitKey(0);

    43. return 0;

    44. }

     

    运行结果:

     

     

    展开全文
  • openCV 源码 模式匹配 轮廓匹配 二值化,包括常用图像操作,包含详细注释
  • 利用轮廓,求图像间的offset,图像库是opencv的代码,利用canny求出轮廓,然后找出两幅图的轮廓offset,即得原图的offset。
  • opencv轮廓高级应用(轮廓匹配,几何直方图)   (2012-07-20 10:40:12) 转载▼ 标签:  机器视觉   opencv   轮廓   杂谈   OpenCv轮廓高级应用(轮廓匹配...

    opencv轮廓高级应用(轮廓匹配,几何直方图)

      (2012-07-20 10:40:12)
    标签: 

    机器视觉

     

    opencv

     

    轮廓

     

    杂谈

     

    OpenCv轮廓高级应用(轮廓匹配,几何直方图)

        最近再次用到了opencv轮廓,在这里结合作者冰山一角的博客(http://www.cnblogs.com/slysky/)以及自己的体会在此稍加说明。其程序主要参见冰山一角的Blog,遗憾的是代码是OpenCV1.0写的,等有时间再用2.4.2改写一篇。

        对于轮廓的相关数据结构表示和几本操作(查找轮廓,画轮廓),可参见前面两片关于轮廓的例程,在这里不多讲。

       对于查找轮廓我们一般要对图像Canny检测。但是对于很特殊的场合其实我们还可以直接对二值化的图像进行轮廓的提取,找出的轮廓其实就是Blob(这个可能就是为什么OpenCV高版本里面把blob分析抛弃的原因吧,我猜的话),画上外截矩形就是一个ROI,是不是觉得很有用?下面介绍罗阔的高级应用。

       轮廓的特性:

    1.轮廓的多边形逼近
        轮廓的多边形逼近指的是:使用多边形来近似表示一个轮廓。
        多边形逼近的目的是为了减少轮廓的顶点数目。
        多边形逼近的结果依然是一个轮廓,只是这个轮廓相对要粗旷一些。
       可以使用方法cvApproxPoly()

    2.轮廓的关键点
        轮廓的关键点是:轮廓上包含曲线信息比较多的点。关键点是轮廓顶点的子集。
        可以使用cvFindDominantPoints函数来获取轮廓上的关键点,该函数返回的结果一个包含 关键点在轮廓顶点中索引 的序列。再次强调:是索引,不是具体的点。如果要得到关键点的具体坐标,可以用索引到轮廓上去找。
    3.轮廓的周长和面积
        轮廓的周长可以用cvContourPerimeter或者cvArcLength函数来获取。
        轮廓的面积可以用cvContourArea函数来获取。

    4.轮廓的边界框
        有三种常见的边界框:矩形、圆形、椭圆。
        (1)矩形:在图像处理系统中提供了一种叫Rectangle的矩形,不过它只能表达边垂直或水平的特例;OpenCv中还有一种叫Box的矩形,它跟数学上的矩形一致,只要4个角是直角即可。
        如果要获取轮廓的Rectangle,可以使用cvBoundingRect函数。
        如果要获取轮廓的Box,可以使用cvMinAreaRect2函数。
        (2)圆形
        如果要获取轮廓的圆形边界框,可以使用cvMinEnclosingCircle函数。
        (3)椭圆
        如果要获取轮廓的椭圆边界框,可以使用cvFitEllipse2函数。
    5.轮廓的矩

        矩是通过对轮廓上所有点进行积分运算(或者认为是求和运算)而得到的一个粗略特征。

    在连续情况下,图像函数为 f(x,y),那么图像的p+q阶几何矩(标准矩)定义为:

    p ,q = 0,1,2…… 

    p+q阶中心距定义为:

     p,q = 0,1,2……

     

     

    其中代表图像的重心,

     

    ,

     

    对于离散的数字图像,采用求和号代替积分:

     

    ,,p,q = 0,1,2 ……

     

    N和M分别是图像的高度和宽度;

    归一化的中心距定义为:;其中

    在公式中,p对应x维度上的矩,q对应y维度上的矩,阶数表示对应的部分的指数。该计算是对轮廓界上所有像素(数目为n)进行求和。如果p和q全部为0,那么m00实际上对应轮廓边界上点的数目。

    虽然可以直接计算出轮廓的矩,但是经常会用到归一化的矩(因此不同大小但是形状相同的物体会有相同的值)。同样,简单的矩依赖于所选坐标系,这意味着物体旋转后就无法正确匹配。

    于是就产生了Hu矩以及其他归一化矩的函数。

    Hu矩是归一化中心矩的线性组合。之所以这样做是为了能够获取代表图像某个特征的矩函数。这些矩函数对缩放,旋转和镜像映射出了(h1)具有不变性。

    Hu矩是从中心矩中计算得到。即七个由归一化中心矩组合成的矩:  

     其中中心矩和归一化中心矩的定义为:

     

     

       我们可以使用cvContoursMoments函数、cvMoments函数方便的得到轮廓的矩集,然后再相应的方法或函数获取各种矩。
        特定的矩:cvGetSpatialMoment函数
        中心矩:cvGetCentralMoment函数
        归一化中心矩:cvGetNormalizedCentralMoment函数
        Hu矩:cvGetHuMoments函数
    6.轮廓的轮廓树
        轮廓树用来描述某个特定轮廓的内部特征。注意:轮廓树跟轮廓是一一对应的关系;轮廓树不用于描述多个轮廓之间的层次关系。

        轮廓树的创建过程:

        从一个轮廓创建一个轮廓树是从底端(叶子节点)到顶端(根节点)的。首先搜索三角形突出或者凹陷的形状的周边(轮廓上的每一个点都不是完全和它的相邻点共线的)每个这样的三角形被一条线段代替,这条线段通过连接非相邻点的两点得到;因此实际上三角形或者被削平或者被填满。每个这样的替换都把轮廓的顶点减少,并且给轮廓树创建一个新节点。如果这样的一个三角形的两侧有原始边,那么她就是得到的轮廓树的叶子;如果一侧已是一个三角形,那么它就是那个三角形的父节点。这个过程的迭代最终把物体的外形简称一个四边形,这个四边形也被剖开;得到的两个三角形是根节点的两个子节点。

    结果的二分树最终将原始轮廓的形状性比编码。每个节点被它所对应的三角形的信息所注释。

    这样建立的轮廓树并不太鲁棒,因为轮廓上小的改变也可能会彻底改变结果的树,同时最初的三角形是任意选取的。为了得到较好的描述需要首先使用函数cvApproxPoly()之后将轮廓排列(运用循环移动)成最初的三角形不怎么收到旋转影响的状态。
        可以用函数cvCreateContourTree来构造轮廓树。

     7.轮廓的凸包和凸缺陷
        轮廓的凸包和凸缺陷用于描述物体的外形。凸包和凸缺陷很容易获得,不过我目前不知道它们到底怎么使用。
        如果要判断轮廓是否是凸的,可以用cvCheckContourConvexity函数。
        如果要获取轮廓的凸包,可以用cvConvexHull2函数,返回的是包含顶点的序列。
        如果要获取轮廓的凸缺陷,可以用cvConvexityDefects函数。
     8.轮廓的成对几何直方图
        成对几何直方图(pairwise geometrical histogram PGH)是链码编码直方图(chain code histogram CCH)的一个扩展或者延伸。CCH是一种直方图,用来统计一个轮廓的Freeman链码编码每一种走法的数字。这种直方图的一个优良性质为当物体旋转45度,那么新直方图是老直方图的循环平移。这样就可以不受旋转影响。

        (1)轮廓保存的是一系列的顶点,轮廓是由一系列线段组成的多边形。对于看起来光滑的轮廓(例如圆),只是线段条数比较多,线段长度比较短而已。实际上,电脑中显示的任何曲线都由线段组成。
        (2)每两条线段之间都有一定的关系,包括它们(或者它们的延长线)之间的夹角,两条线段的夹角范围是:(0,180)。
        (3)每两条线段上的点之间还有距离关系,包括最短(小)距离、最远(大)距离,以及平均距离。最大距离我用了一个偷懒的计算方法,我把轮廓外界矩形的对角线长度看作了最大距离。
        (4)成对几何直方图所用的统计数据包括了夹角和距离。

    轮廓的匹配
        如果要比较两个物体,可供选择的特征很多。如果要判断某个人的性别,可以根据他(她)头发的长短来判断,这很直观,在长发男稀有的年代准确率也很高。也可以根据这个人尿尿的射程来判断,如果射程大于0.50米,则是男性。总之,方法很多,不一而足。
        我们在上文中得到了轮廓的这么多特征,它们也可以用于进行匹配。典型的轮廓匹配方法有:Hu矩匹配、轮廓树匹配、成对几何直方图匹配。
    1.Hu矩匹配
        轮廓的Hu矩对包括缩放、旋转和镜像映射在内的变化具有不变性。cvMatchShapes函数可以很方便的实现对2个轮廓间的匹配。
    2.轮廓树匹配
        用树的形式比较两个轮廓。cvMatchContourTrees函数实现了轮廓树的对比。
    3.成对几何直方图匹配
        在得到轮廓的成对几何直方图之后,可以使用直方图对比的方法来进行匹。

     

     

    轮廓匹配源码1:

     

    轮廓匹配源码1

        
        IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
        IplImage* img_edge1 = cvCreateImage(cvGetSize(img_8uc1),8,1);
        IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);

        cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);


        CvMemStorage* storage1 = cvCreateMemStorage();
        CvSeq* first_contour1 = NULL;

        int Nc = cvFindContours(
            img_edge1,
            storage1,
            &first_contour1,
            sizeof(CvContour),
            CV_RETR_LIST
            );

        IplImage* img_8uc12 = cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
        IplImage* img_edge12 = cvCreateImage(cvGetSize(img_8uc12),8,1);
        IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);

        cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);


        CvMemStorage* storage2 = cvCreateMemStorage();
        CvSeq* first_contour2 = NULL;

        int Nc2 = cvFindContours(
            img_edge12,
            storage2,
            &first_contour2,
            sizeof(CvContour),
            CV_RETR_LIST
            );

        double n = cvMatchShapes(first_contour1,first_contour2,CV_CONTOURS_MATCH_I1,0);

        printf("%d",n);

        cvWaitKey();


        IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
        IplImage* img_edge1 = cvCreateImage(cvGetSize(img_8uc1),8,1);
        IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);

       cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);


       CvMemStorage* storage1 = cvCreateMemStorage();
       CvSeq* first_contour1 = NULL;

       int Nc = cvFindContours(
          img_edge1,
          storage1,
          &first_contour1,
         sizeof(CvContour),
         CV_RETR_LIST
          );

        CvContourTree* tree1 = cvCreateContourTree(
        first_contour1,
       storage1,
       200
        );
     
        IplImage* img_8uc12 = cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
        IplImage* img_edge12 = cvCreateImage(cvGetSize(img_8uc12),8,1);
        IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);
     
        cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);
     
     
        CvMemStorage* storage2 = cvCreateMemStorage();
        CvSeq* first_contour2 = NULL;
     
        int Nc2 = cvFindContours(
            img_edge12,
            storage2,
            &first_contour2,
            sizeof(CvContour),
            CV_RETR_LIST
            );
        CvContourTree* tree2 = cvCreateContourTree(
            first_contour2,
            storage2,
            200
            );
        double n = cvMatchContourTrees(tree1,tree1,CV_CONTOURS_MATCH_I1,200);
     
        printf("%d",n);
        cvWaitKey();

     

    几何直方图匹配方:

     

    #include "gesrec.h"
    #include <stdio.h>//
    
    #define PI 3.14159f
    
    //轮廓面积比较函数
    static int gesContourCompFunc(const void* _a, const void* _b, void* userdata)
    {
    int retval;
    double s1, s2;
    CvContour* a = (CvContour*)_a;
    CvContour* b = (CvContour*)_b;
    
    s1 = fabs(cvContourArea(a));
    s2 = fabs(cvContourArea(b));
    //s1 = a->rect.height * a->rect.width;
    //s2 = b->rect.height * b->rect.width;
    
    if(s1 < s2)
    {
    retval = 1;
    }
    else if(s1 == s2)
    {
    retval = 0;
    }
    else
    {
    retval = -1;
    }
    
    return retval;
    }
    
    //src:BGR dst:
    void gesFindContours(IplImage* src, IplImage* dst, CvSeq** templateContour, CvMemStorage* templateStorage, int flag)
    {
    int count;//轮廓数
    IplImage* gray;
    CvMemStorage* first_sto;
    CvMemStorage* all_sto;
    CvSeq* first_cont;
    CvSeq* all_cont;
    CvSeq* cur_cont;
    
    //初始化动态内存
    first_sto = cvCreateMemStorage(0);
    first_cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), first_sto);
    all_sto = cvCreateMemStorage(0);
    all_cont = cvCreateSeq(0sizeof(CvSeq), sizeof(CvSeq), all_sto);
    
    //创建源图像对应的灰度图像
    gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
    cvCvtColor(src, gray, CV_BGR2GRAY);
    
    //得到图像的外层轮廓
    count = cvFindContours(gray, first_sto, &first_cont, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    
    //如果没有检测到轮廓则返回
    if(first_sto == NULL)
    {
    return;
    }
    
    //将所有的轮廓都放到first_cont中
    for(;first_cont != 0;first_cont = first_cont->h_next)
    {
    if(((CvContour* )first_cont)->rect.height * ((CvContour* )first_cont)->rect.width >=625)
    cvSeqPush(all_cont, first_cont);
    }
    
    //对轮廓按照面积进行排序
    cvSeqSort(all_cont, gesContourCompFunc, 0);
    
    //在dst中画出轮廓
    cvZero(dst);
    for(int i = 0;i < min(all_cont->total, 3);i++)///次数待改
    {
    cur_cont = (CvSeq* )cvGetSeqElem(all_cont, i);
    if(flag != 0 && i == 0)
    {
    *templateContour = cvCloneSeq(cur_cont, templateStorage);
    }
    
    CvScalar color = CV_RGB(rand()&255, rand()&255, rand()&255);
    cvDrawContours(dst, (CvSeq* )cur_cont, color, color, -118);
    }
    
    //判断原点位置以确定是否需要反转图像
    if(src->origin == 1)
    {
    cvFlip(dst);
    }
    
    //释放内存
    cvReleaseMemStorage(&first_sto);
    cvReleaseMemStorage(&all_sto);
    cvReleaseImage(&gray);
    }
    
    void gesMatchContoursTemplate(IplImage* src, IplImage* dst, CvSeq** templateContour)
    {
    CvSeq* contour;
    CvMemStorage* storage;
    
    //初始化动态内存
    storage = cvCreateMemStorage(0);
    contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
    
    //得到轮廓并进行匹配
    gesFindContours(src, dst, &contour, storage, 1);
    if(contour->total != 0)//如果得到的轮廓不为空
    {
    double result = cvMatchShapes((CvContour* )contour, (CvContour* )(*templateContour), CV_CONTOURS_MATCH_I3);
    printf("%.2f\n", result);/
    }
    
    //释放内存
    cvReleaseMemStorage(&storage);
    }
    
    //模版匹配法的完整实现
    int gesMatchContoursTemplate2(IplImage* src, IplImage* dst, CvSeq* templateContour)
    {
    CvSeq* contour;
    CvSeq* cur_cont;
    CvMemStorage* storage;
    double minValue, tempValue;
    int i, minIndex;
    
    //初始化动态内存
    storage = cvCreateMemStorage(0);
    contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
    
    //得到轮廓并进行匹配
    minIndex = -1;
    gesFindContours(src, dst, &contour, storage, 1);
    if(contour->total != 0)//如果得到的轮廓不为空
    {
    if(templateContour->total != 0)
    {
    cur_cont = (CvSeq* )cvGetSeqElem(templateContour, 0);
    minValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3);
    minIndex = 0;
    printf("0:%.2f\n", minValue);
    }
    
    for(i = 1;i < templateContour->total;i++)
    {
    cur_cont = (CvSeq* )cvGetSeqElem(templateContour, i);
    tempValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3);
    if(tempValue < minValue)
    {
    minValue = tempValue;
    minIndex = i;
    }
    printf("%d:%.2f\n", i, tempValue);
    }
    
    if(minValue >= 0.3)
    {
    minIndex = -1;
    }
    }
    
    //打印匹配结果
    printf("the result is %d\n", minIndex);
    
    //释放内存
    cvReleaseMemStorage(&storage);
    
    return minIndex;
    }
    
    //找出轮廓最大的5个极大值点
    void gesFindContourMaxs(CvSeq* contour)
    {
    int i;
    CvScalar center;//重心位置
    CvPoint* p;
    CvMat max;//存储5个极大值的数组
    double initMax[] = {-1, -1, -1, -1, -1};//初始极大值设置为-1
    double minValue, maxValue;//5个极大值中的最大值与最小值
    CvPoint minLoc;//最小值的位置
    double preDistance = 0;
    bool isCandidate = false;//是否是候选的极大值点
    
    //初始化重心位置
    center = cvScalarAll(0);
    
    //初始化极大值矩阵
    max = cvMat(15, CV_64FC1, initMax);
    
    //首先求出轮廓的重心
    for(i = 0;i < contour->total;i++)
    {
    p = (CvPoint* )cvGetSeqElem(contour, i);
    center.val[0] += p->x;
    center.val[1] += p->y;
    }
    center.val[0] /= contour->total;
    center.val[1] /= contour->total;
    
    //遍历轮廓,找出所有的极大值点
    for(i = 0;i < contour->total;i++)
    {
    p = (CvPoint* )cvGetSeqElem(contour, i);
    double distance = sqrt(pow(center.val[0] - p->x, 2) + pow(center.val[1] - p->y, 2));
    
    if(distance > preDistance)
    {
    isCandidate = true;
    }
    else if(distance < preDistance && isCandidate == true)
    {
    cvMinMaxLoc(&max, &minValue, &maxValue, &minLoc);
    
    if(distance > minValue)
    {
    cvmSet(&max, minLoc.y, minLoc.x, distance);
    }
    isCandidate = false;
    }
    else
    {
    isCandidate = false;
    }
    
    preDistance = distance;
    }
    
    //打印5个极大值
    printf("%.2f %.2f %.2f %.2f %.2f\n", cvmGet(&max, 00), cvmGet(&max, 01), cvmGet(&max, 02), cvmGet(&max, 03), cvmGet(&max, 04));
    }
    
    //计算轮廓的pair-wise几何直方图
    CvHistogram* gesCalcContoursPGH(CvSeq* contour)
    {
    CvHistogram* hist;//成对几何直方图
    CvContour* tempCont;
    
    //得到成对几何直方图第二个维度上的范围
    tempCont = (CvContour* )contour;
    cvBoundingRect(tempCont, 1);
    
    int sizes[2] = {60200};
    float ranges[2][2] = {{0,PI}, {0,200}};
    float** rangesPtr = new float* [2];
    rangesPtr[0] = ranges[0];
    rangesPtr[1] = ranges[1];
    
    //初始化几何直方图
    hist = cvCreateHist(2, sizes, CV_HIST_ARRAY, rangesPtr, 1);
    
    //计算轮廓的成对几何直方图
    cvCalcPGH(contour, hist);
    
    return hist;
    }
    
    //对轮廓的pair-wise几何直方图进行匹配
    void gesMatchContoursPGH(CvSeq* contour, CvHistogram* templateHist)
    {
    CvHistogram* hist;
    
    //得到轮廓的成对几何直方图
    hist = gesCalcContoursPGH(contour);
    
    //归一化直方图
    cvNormalizeHist(templateHist, 1);
    cvNormalizeHist(hist, 1);
    
    //直方图匹配
    double result = cvCompareHist(hist, templateHist, CV_COMP_INTERSECT);
    printf("result:%.2f\n", result);
    
    //释放内存
    cvReleaseHist(&hist);
    }
    展开全文
  • OpenCV图像的轮廓匹配

    千次阅读 2019-01-25 13:39:00
    OpenCV图像的轮廓匹配
                   

    一个跟轮廓相关的最常用到的功能是匹配两个轮廓.如果有两个轮廓,如何比较它们;或者如何比较一个轮廓和另一个抽象模板.

    比较两个轮廓最简洁的方式是比较他们的轮廓矩.这里先简短介绍一个矩的含义.简单的说,矩是通过对轮廓上所有点进行积分运算(或者认为是求和运算)而得到的一个粗略特征.通常,我们如下定义一个轮廓的(p,q)矩:


    在公式中p对应x纬度上的矩,q对应y维度上的矩,q对应y维度上的矩,阶数表示对应的部分的指数.该计算是对轮廓边界上所有像素(数目为n)进行求和.如果p和q全为0,那么m00实际上对轮廓边界上点的数目.

    下面的函数用于计算这些轮廓矩

    void cvContoursMoments(CvSeq* contour,CvMoments* moments)

    第一个参数是我们要处理的轮廓,第二个参数是指向一个结构,该结构用于保存生成的结果.CvMonments结构定义如下

    /* Spatial and central moments */  typedef struct CvMoments  {      double  m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; /* spatial moments */      double  mu20, mu11, mu02, mu30, mu21, mu12, mu03; /* central moments */      double  inv_sqrt_m00; /* m00 != 0 ? 1/sqrt(m00) : 0 */  }  CvMoments;  
    在cvContourMoments()函数中,只用到m00,m01,...,m03几个参数;以mu开头的参数在其他函数中使用.

    在使用CvMoment结构的时候,我们可以使用以下的函数来方便地一个特定的矩:

    CVAPI(double)  cvGetSpatialMoment( CvMoments* moments, int x_order, int y_order );  
    调用cvContoursMonments()函数会 计算所有3阶的矩(m21和m12会被计算,但是m22不会被计算).

    再论矩

    刚刚描述的矩计算给出了一些轮廓的简单属性,可以用来比较两个轮廓.但是在很多实际使用中,刚才的计算方法得到的矩并不是做比较时的最好的参数.具体说来,经常会用到归一化的矩(因此,不同大小但是形状相同的物体会有相同的值).同样,刚才的小节中的简单的矩依赖于所选坐标系,这意味这物体旋转后就无法正确匹配.

    OpenCV提供了计算Hu不变矩[Hu62]以及其他归一化矩的函数.CvMoments结构可以用cvmoments或者cvContourMoments计算.并且,cvContourMoments现在只是cvMoments
    的一个别名.

    一个有用的小技巧是用cvDrawContour()描绘一幅轮廓的图像后,调用一个矩的函数处理该图像.使用无论轮廓填充与否,你都能用同一个函数处理.

    以下是4个相关函数的定义:

    /* Calculates all spatial and central moments up to the 3rd order */  CVAPI(void) cvMoments( const CvArr* arr, CvMoments* moments, int binary CV_DEFAULT(0));  
    CVAPI(double)  cvGetCentralMoment( CvMoments* moments, int x_order, int y_order );  
    CVAPI(double)  cvGetNormalizedCentralMoment(CvMoments* moments,int x_order, int y_order);  
    /* Calculates 7 Hu's invariants from precalculated spatial and central moments */  CVAPI(void) cvGetHuMoments( CvMoments*  moments, CvHuMoments*  hu_moments );  
    第一个函数除了使用的是图像(而不是轮廓)作为参数,其他方面和cvContoursMoments()函数相同,另外还增加了一个参数.增加的参数isBinary如果为CV_TRUE,cvMoments将把图像当作二值图像处理,所有的非0像素都当作1.当函数被调用的时候,所有的矩被计算(包含中心矩,请看下一段).除了x和y的值被归一化到以0为均值,中心距本质上跟刚才描述的矩一样.

    归一化矩和中心矩也基本相同,除了每个矩都要除以m00的某个幂:

    最后来介绍Hu矩,Hu矩是归一化中心距的线性组合.之所以这样做是为了能够获取代表图像某个特性的矩函数,这些矩函数对于某些变化如缩放,旋转和镜像映射(除了h1)具有不变性.Hu矩是从中心矩中计算得到,其计算公式如下所示:

    参考图8-9和表8-1,我们可以直观地看到每个图像对应的7个Hu矩.通过观察可以发现,当阶数变高时,Hu矩一般会变小.对于这一点不必感到奇怪,因为根据定义,高阶Hu矩由多个归一化矩的高阶幂计算得到,而归一化矩都是小于1的,所以指数越大,计算所得的值越小.


    需要特别注意的是"I",它对于180度旋转和镜面反射都是对称的,它的h3到h7矩都是0;而"O"具有同样的对称特性,所有的Hu矩都是非0的.

    使用Hu矩进行匹配

    /* Compares two contours by matching their moments */  CVAPI(double)  cvMatchShapes( const void* object1, const void* object2,                                int method, double parameter CV_DEFAULT(0)); 
    很自然,使用Hu矩我们想要比较两个物体并且判明他们是否相似.当然,可能有很多"相似"的定义.为了使比较过程变得简单,OpenCV的函数cvMatShapes()允许我们简单地提供两个物体,然后计算他们的矩并根据我们提供的标准进行比较.

    这些物体可以是灰度图图像或者轮廓.如果你提供了图像,cvMatchShape()会在对比的进程之间为你计算矩.cvMatchShapes()使用的方法是表8-2中列出的三种中的一种.

     关于对比度量标准(metric)是如何被计算的,表8-2中的三个常量每个都用了不同的方法.这个度量标准最终决定了cvMatchShapes()的返回值.最后一个参数变量现在不能用,因此我们可以把它设成默认值0.

    等级匹配

    我们经常想要匹配两个轮廓,然后用一个相似度量来计算轮廓所有匹配部分.使用概况参数的方法(比如矩)是相当快的,但是他们能够表达的信息却不是很多.

    为了找到一个更精确的相似度量度,首先考虑一下轮廓树的结构应该会有帮助.请注意,此外的轮廓树是用来表述一个特定形状(不是多个特定形状)内各部分的等级关系.

    类似于cvFindContours()着怎样的函数放回多个轮廓,轮廓树(contout tree)并不会把这些等级关系搞混,事实上,他正是对于某个特定轮廓形状的登记描述.

    理解了轮廓树的创建会比较容易理解轮廓树.从一个轮廓创建一个轮廓树是从底端(叶节点)到顶端(根节点)的.首相搜索三角形突出或凹陷的形状的周边(轮廓上的每一个点都不是完全和它的相邻点共线的).每个这样的三角形被一条线段代替,这条线段通过连接非相邻点的两点得到;因此实际上三角形或者被削平(例如,图8-10的三角形D)或者被填满(三角形C).每个这样的替代把轮廓的顶点减少1,并且给轮廓创建一个新节点.如果这样一个三角形的两侧有原始的边,那么它就是得到的轮廓树的叶子;如果一侧是已存在三角形,那么他就是那个三角形的父节点.这个过程的迭代最终把物体的外形剪成一个四边形,这个四边形也被剖开;得到的两个三角形是根节点的两个子节点.

    结果的二分树(图8-11)最终将原始轮廓的形状信息编码.每个节点被它对应的三角形信息(比如三角形的大小,它的生成是被切出来还是被填进去的,这样的信息)所注释

    这些树一旦被建立,就可以很有效的对比两个轮廓.这个过程开始定义两个树节点的对应关系,然后比较对应节点的特性.对吼的结果就是两个树的相似度.

    事实上,我们基本不需要理解这个过程.OpenCV提供了一个函数从普通的CvContour对象自动生成轮廓树并转换返回;还提供一个函数用来对比两个树.不幸的是,建立的轮廓树并不太鲁棒(例如,轮廓上很小的改变可能会彻底改变结果的树).同事,最初的三角形(树的根节点)是随意选取的.因此,为了得到较好的描述实现使用函数cvApproxPoly()之后将轮廓排列(运用循环移动)成最初的三角形不怎么受到旋转影响的状态.

    CvContourTree* cvCreateContourTree(const CvSeq* contour,CvMemStorage* storage,double  threshold);

    CvSeq * cvContourFromContourTree(const CvContourTree* tree,CvMemStorage* storage, CvTermCriteria  criteris);

    double cvMatchContourTrees(const CvContourTree* tree1,const CvContourTree* tree2,int method,double threshold);

    这个代码提到了CvTremCriteria(),该函数细节将在第9章给出.现在可以用下面的默认值使用cvTermCriteria()简单建立一个结构体.

    CvTermCriteria termcrit = cvTermCriteria(CV_TERMCRIT_ITER | CV_TeRMCRT_EPS,5,1);



    轮廓的凸包和凸缺陷

    另一个理解物体形状或轮廓的有用的方法是计算一个物体的凸包(convex hull)然后计算其凸缺陷(convexity defects)[Homma85].很多复杂物体的特性能很好的被这种缺陷表现出来.

    图8-12用人手举例说明了凸缺陷这一概念.手周围深色的线描画出了凸包,A到H被标出的区域是凸包的各个"缺陷".正如所看到的,这些凸度缺陷提供了手以及手状态的特征表现的方法.

    enum  {      CV_CLOCKWISE         =1,      CV_COUNTER_CLOCKWISE =2  };  
    /* Calculates exact convex hull of 2d point set */  CVAPI(CvSeq*) cvConvexHull2( const CvArr* input,                               void* hull_storage CV_DEFAULT(NULL),                               int orientation CV_DEFAULT(CV_CLOCKWISE),                               int return_points CV_DEFAULT(0));  
    /* Checks whether the contour is convex or not (returns 1 if convex, 0 if not) */  CVAPI(int)  cvCheckContourConvexity( const CvArr* contour );  
    /* Finds convexity defects for the contour */  CVAPI(CvSeq*)  cvConvexityDefects( const CvArr* contour, const CvArr* convexhull,                                     CvMemStorage* storage CV_DEFAULT(NULL));  

    OpenCV有三个关于凸包和凸缺陷的重要函数.第一个函数简单计算已知轮廓的凸包,第二个函数用来检查一个已知轮廓是否是凸的.第三个函数在已知轮廓是凸包的情况下计算凸缺陷.

    函数cvConvexHull2()的第一个参数是点的数组,这个数组是一个n行2列的矩阵(n×2),或者是一个轮廓.如果是点矩阵,点应该是32位整型(CV_32SC1)或者是浮点型(CV_32F1).下一个参数是指向内存存储的一个指针,为结果分配内存空间.下一参数是CV_CLOCkWISE或者是CV_COUNTERCLOCkWISE中的一个.这参数决定了程序返回点的排列方向.最后一个参数returnPoints,可以是0或1.如果设置为1,点会被存储在返回数组中.如果设置为0,只有索引被存储在返回数组中.索引是传递给cvConvexHull2()的原始数组索引.

    读着可能要问:"如果参数hull_storage是内存存储,为什么它的类型是void* 而不是CvMemSotrage* ?",这是因为很多时候作为凸包放回的点的形式,数组可能比序列更加有用.可虑到这一点,参数hull_storage的另一个可能性是传递一个指向矩阵的指针CvMat*. 这种情况下,矩阵应该是一维的且和输入点的个数相同.当cvConvexHull2()被调用的时候,它会修改矩阵头来指明当前的列数.

    有时候,已知一个轮廓但并不知道它是否是凸的.这种情况下,我们可以调用函数cvCheckContourConvexity().这个测试简单快速,但是如果传递的轮廓自身有交叉的时候不会得到正确的结果.

    第三个函数cvConvexityDefects(),计算凸缺陷返回一个缺陷的序列.为了完成这个任务,cvConvexityDefects()要求输入轮廓,凸包和内存空间,从这个内存空间来获得存放结果序列的内存.前两个参数是CvArr*,和传递给cvConvexHull2()的参数input的形式相同.

    typedef struct CvConvexityDefect  {      CvPoint* start; /* point of the contour where the defect begins */      CvPoint* end; /* point of the contour where the defect ends */      CvPoint* depth_point; /* the farthest from the convex hull point within the defect */      float depth; /* distance between the farthest point and the convex hull */  } CvConvexityDefect;  
    函数cvConvexityDefects()返回一个CvConvexityDefect结构体的序列,其中包括一些简单的参数用来描述凸缺陷.start和end是凸包上的缺陷的起始点和终止点.depth_point是缺陷中的距离凸包的边(跟该缺陷有关的凸包便)最远的点.最后一个参数depth是最远点和包的边(edge)的距离.

    成对几何直方图

    Freeman链码编码是对一个多边形的的序列如何"移动"的描述,每个这样的移动有固定长度和特定的方向.但是,我们并没有更多说明为什么需要用到这种描述.

    Freeman链码编码的用处很多,但最常见的一种值得深入了解一下,因为它支持了成对几何直方图(PGH)的基本思想.

    PGH实际上是链码编码直方图(CCH)的一个扩展或延伸.CCH是一种直方图,用来统计一个轮廓的Freeman链码编码每一种走法的数字.这种直方图有一些良好的性质.最显著的是,将物体旋转45度,那么新的直方图是老直方图的循环平移(图8-13).这就提供了一个不被此类旋转影响的形状识别方法.

    PGH的构成如下图所示(图8-14).多边形的每一个边被选择成为"基准边".之后考虑其他的边相对于这些基础边的关系,并且计算三个值:dmin,dmax和θ.dmin是两条边的最小距离,dmax是最大距离,θ是两边的夹角.PGH是一个二维直方图,其两个维度分别是角度和距离.对于每一对边,有两个bin,一个bin为(dmin,θ),另一个bin为(dmax,θ).对于这样的每一组边,这两个bin都被增长,中间值d(dmin和dmax之间的值)同样也被增长.

    PGh的使用和FCC相似.一个重要不同是,PGH的描述能力更强,因此在尝试解决复杂问题的时候很有用,比如说大量形状需要被辨识,并且/或者有很多背景噪声的时候.用来计算PGh的函数是

    void cvCalcPGH(const CvSeq* contour,CvHistogram* hist)

    在这里轮廓可以包含整数值的点的坐标;当然直方图必须是二维的.

               

    再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

    展开全文
  • OpenCv轮廓高级应用(轮廓匹配,几何直方图)  最近再次用到了opencv轮廓,在这里结合作者冰山一角的博客(http://www.cnblogs.com/slysky/)以及自己的体会在此稍加说明。其程序主要参见冰山一角的Blog,遗憾的是...
  • 直方图对比和模板匹配根据色彩及色彩的分布来进行匹配,以下包括:轮廓的查找、表达方式、组织方式、绘制、特性、匹配。 首先回忆下几个结构体: 首先是图像本身的结构体: typedef struct Cv
  • opencv关于视频播放、形态学变换、canny边缘检测、轮廓查找、模板匹配、DFT变换
  • Reference:Opencv轮廓矩【判断形态方向、匹配度】 (细看,比OpenCV中文版中讲解得好)
  • 现在我只知道 matchshapes这个函数,但精度貌似很低,还有其他的算法吗?或者让matchshapes精度更高的一些操作是什么?小白求教
  • OpenCV 轮廓

    千次阅读 2016-05-09 11:51:13
    OpenCV中一般用序列来存储轮廓的信息。序列中的每一个元素是曲线中一个点的位置。  从哪里提取轮廓呢?我们一般可以从cvCanny( )函数得到的有边缘像素的图像,或者是从cvThreshold( )及cvAdaptiveThreshold
  • opencv轮廓高级应用(轮廓匹…

    千次阅读 2017-08-05 17:43:53
    原文地址:opencv轮廓高级应用(轮廓匹配,几何直方图)作者:zhliangOpenCv轮廓高级应用(轮廓匹配,几何直方图)   最近再次用到了opencv轮廓,在这里结合作者冰山一角的博客(http://www.cnblogs.com/slysky/)...
  • OpenCV轮廓属性

    2021-08-28 15:50:27
    范围是轮廓区域与边界矩形区域的比值。 代码示例: area = cv.contourArea(cnt) x,y,w,h = cv.boundingRect(cnt) rect_area = w*h extent = float(area)/rect_area 3.坚实度 坚实度是等高线面积与其凸包面积之比。 ...
  • opencv轮廓学习总结

    千次阅读 多人点赞 2018-04-12 16:32:17
    最近几天学习了好多opencv里有关轮廓查找与绘制的函数,想做一下整理,把所有学的函数列出来,以便于统一思考一下,怎样充分的运用它们做一些自己想做的事情。就比如我最近参加一个比赛,负责做我们项目里的动作行为...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,995
精华内容 1,998
关键字:

opencv轮廓部分匹配