• 【python版CV】图像轮廓&模板匹配


    1、图像轮廓

    1.1 findContours函数:

    cv.findContours(img,mode,method)
    
    • 1

    mode:轮廓检索模式

    • RETR_EXTERNAL:只检索最外面轮廓
    • RETR_LIST:检索所有轮廓,并将其保存到一条链表当中
    • RETR_CCOMP:检索所有轮廓,并将它们组织为两层;顶层是各部分的外部边界,第二层是空洞的边界
    • RETR_TREE:检索所有轮廓,并重构嵌套轮廓层次(常用)

    method:轮廓逼近方法

    • CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点序列)
    • CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是函数保留它们终点部分

    为了更高的准确率,使用二值图像

    image=cv.imread("E:\\Pec\\lan.jpg")
    #转化为灰度图
    gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
    #转化为二值图
    ret,thresh=cv.threshold(gray,127,255,cv.THRESH_BINARY)
    #返回值一:计算好或者设定好的阈值
    #返回值二:处理好的图像
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    1.2 获取轮廓信息(可能会报错原因)

    #binary,contours,hierarchy=cv.findContours(,,)
    #因为OpenCV库的更新,会报错“not enough values to unpack (expected 3, got 2)”
    #把返回值从三个改成两个即可(删除第一个返回值)
    contours,hierarchy=cv.findContours(thresh,cv.RETR_TREE,cv.CHAIN_APPROX_NONE)
    #返回值一:轮廓信息
    #返回值二:层级
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.3 绘制轮廓:

    #绘制图像轮廓函数drawContours():图像、轮廓信息、轮廓索引(需要多少轮廓,-1默认全部)、颜色模式、线条厚度
    draw_img=image.copy()
    res=cv.drawContours(draw_img,contours,-1,(0,255,0),2)
    cv_show("res",res)
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    1.4 轮廓特征:

    #轮廓特征
    cnt=contours[0]#第0个轮廓
    #面积
    area=cv.contourArea(cnt)
    print(area)
    #周长,True表示闭合的轮廓图像
    girth=cv.arcLength(cnt,True)
    print(girth)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.5 轮廓近似:

    说明:曲线AB,假设C点是到直接AB最大的点,然后过C点做直线AB的垂直线d,若d点小于T(自己设置的阈值),直线AB可以取代曲线AB,若d大于T,不能取代,则同理在曲线CB取一点D,做D到直线CB的曲线,然后再比较阈值。。。。。

    在这里插入图片描述

    #轮廓近似
    #二值图获取
    img=cv.imread("E:\\Pec\\lunk.jpg")
    gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    ret,thresh=cv.threshold(gray,127,255,cv.THRESH_BINARY)
    #获取轮廓
    contours,hierarchy=cv.findContours(thresh,cv.RETR_TREE,cv.CHAIN_APPROX_NONE)
    cnt=contours[2]
    draw_img=img.copy()
    res=cv.drawContours(draw_img,contours,-1,(0,255,0),5)
    cv_show("res",res)
    #取近似
    epsilon=0.15*cv.arcLength(cnt,True)#设置的比较阈值,一般是周长的百分比
    #百分比越小,变化越不明显
    approx=cv.approxPolyDP(cnt,epsilon,True)#做近似
    draw_img=img.copy()
    res=cv.drawContours(draw_img,[approx],-1,(0,0,255),2)
    cv_show("res",res)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    1.6 边界矩形

    img=cv.imread("E:\\Pec\\lunk.jpg")
    gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    ret,thresh=cv.threshold(gray,127,255,cv.THRESH_BINARY)
    contours,hierarchy=cv.findContours(thresh,cv.RETR_TREE,cv.CHAIN_APPROX_NONE)
    cnt=contours[3]
    x,y,w,h=cv.boundingRect(cnt)
    image=cv.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
    cv_show("image",image)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    2、模板匹配

    说明:模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv中有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。如原图形是AxB大小,而模板是axb大小,则输出结果是(A-a+1)x(B-b+1)

    6种差别程度的计算方法:(尽量用归一化)

    • TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
    • TM_CCORR:计算相关性,计算出来的值越大,越相关
    • TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
    • TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
    • TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
    • TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
    #模板匹配
    img=cv.imread("E:\\Pec\\lida.jpg",0)
    template=cv.imread("E:\\Pec\\face.jpg",0)
    #cv_show("lida",img)
    #cv_show("tem",template)
    h,w=template.shape[:2]
    print(img.shape)
    print(template.shape)
    methods=['cv.TM_CCOEFF','cv.TM_CCORR','cv.TM_CCOEFF','cv.TM_SQDIFF_NORMED'
        ,'cv.TM_CCORR_NORMED','cv.TM_CCOEFF_NORMED']
    #进行模板匹配
    res=cv.matchTemplate(img,template,3)
    #第三个参数是一个数值,1对应上面的TM_CCOEFF,同理下面
    print(res.shape)
    min_val,max_val,min_loc,max_loc=cv.minMaxLoc(res)
    print(min_val)#最小值
    print(max_val)#最大值
    print(min_loc)#最小值位置
    print(max_loc)#最大值位置
    
    for meth in methods:
        img2=img.copy()
        #匹配方法的真值
        method=eval(meth)#eval函数是以字符串的形式运行代码,如a1,a2...分别赋予1,2..
        print(method)
        res=cv.matchTemplate(img,template,method)
        min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
        #如果是平方差匹配TM_SQDIFF或者归一化平方差匹配,取最小值
        if method in [1,4]:
            top_left=min_loc
        else:
            top_left=max_loc
        bottom_right=(top_left[0]+w,top_left[1]+h)
        #画矩形
        cv.rectangle(img2,top_left,bottom_right,255,2)
        #位置由三个整型数值构成:第一个代表行数,第二个代表列数,第三个代表索引位置。
        #举例:plt.subplot(2, 3, 5) 和plt.subplot(235) 一样。
        plt.subplot(121),plt.imshow(res)
        plt.xticks([]),plt.yticks([])#隐藏坐标轴
        plt.subplot(122),plt.imshow(img2)
        plt.xticks([]), plt.yticks([])  # 隐藏坐标轴
        plt.suptitle(meth)
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在这里插入图片描述

    (255, 255)
    (146, 153)
    (110, 103)
    0.7732675671577454
    0.9017052054405212
    (8, 109)
    (67, 35)
    4
    2
    4
    1
    3
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    匹配多个对象:

    python zip()函数:

    zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
    如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。zip(*loc[::-1])

    img_jb1=cv.imread("E:\\Pec\\jinbi1.jpg")
    #cv_show("res",img_jb1)
    img_jb2=cv.imread("E:\\Pec\\jinbi2.jpg")
    #cv_show("res",img_jb2)
    h,w=img_jb2.shape[:2]
    res=cv.matchTemplate(img_jb1,img_jb2,cv.TM_CCOEFF_NORMED)
    #取匹配度大于80%的坐标
    threshold=0.7
    loc=np.where(res>threshold)
    for pt in zip(*loc[::-1]): #*表示可选参数
        bootom_right=(pt[0]+w,pt[1]+h)
        cv.rectangle(img_jb1,pt,bootom_right,(0,255,0),2)
    cv_show("jb",img_jb1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    3、图像金字塔

    • 高斯金字塔
    • 拉普拉斯金字塔

    3.1 高斯金字塔

    (1)向下采样方法(缩小)

    • 将Gi与高斯内核卷积
    • 将所有偶数行和列去除,两边各缩小一半,面积缩小四分之一
    down=cv.pyrDown(image)
    #cv_show("down",down)
    
    • 1
    • 2

    (2)向上采样方法(放大)

    在这里插入图片描述

    • 将图像在每个方向扩大到原来的两倍,新增的行和列以0填充
    • 使用先前同样的内核(乘以4)与放大后的图像卷积,获取近似值
    up=cv.pyrUp(image)
    #cv_show("up",up)
    
    • 1
    • 2

    3.2 拉普拉斯金字塔

    L i = G i − P y r U p ( P y r D o w n ( G i ) ) Li=Gi-PyrUp(PyrDown(Gi)) Li=GiPyrUp(PyrDown(Gi))

  • 相关阅读:
    列表进行处理,组成句子
    ELK - CentOS7 安装 elasticsearch 7.17.5
    最近5年133个Java面试问题列表
    MPP 与 SMP 的区别,终于有人讲明白了【文末送书】
    React - Ant Design3.x版本安装使用,并按需引入和自定义主题
    二叉树的自下而上、从右到左的层次遍历算法实现
    【Linux】服务器间免登陆访问
    用HTML+CSS做一个漂亮简单的音乐网站
    Java基础入门·对存储文件File的相关操作
    24. 图论 - 图的表示&种类
  • 原文地址:https://blog.csdn.net/qq_44859533/article/details/126068675