Python-OpenCV实现角点、直线、椭圆和矩形检测

目录

  • 概要
  • 一、角点检测
  • 1. Harris角点检测
  • 2. Shi-Tomas算法
  • 3. SIFT算法
  • 4. FAST算法
  • 概要

    本博客梳理了几种常见的**角点检测直线检测椭圆检测矩形检测**算法,本博客只关注代码,不关注每种算法的原理。

    一、角点检测

    常见的角点检测方法有Harris角点检测Shi-Tomas算法角点检测sift算法角点检测fast角点检测ORM算法角点检测

    1. Harris角点检测

    import numpy as np
    import cv2 as cv
    import matplotlib.pyplot as plt
    
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正确显示中文
    
    # Harris角点检测
    '''
    Harris角点检测的思想是通过图像的局部小窗口观察图像,角点的特征是窗口沿任意方向移动都会导致
    图像灰度的明显变化
    
    将上述思想转化为数学表达式,即将局部窗口向各个方向移动(u,v)并计算所有灰度差异的总和,表达式
    如下:
        E(u,v)=∑w(x,y)[I(x+u,y+v)-I(x,y)]²
    其中I(x,y)是局部窗口的灰度图,I(x+u,y+v)是平移后的灰度图像,w(x,y)是窗口函数,其可以是矩形
    窗口,也可以是对每一个像素赋予不同权重的高斯窗口
    角点检测中使E(u,v)的值最大.利用一阶泰勒展开有:
        I(x+u,y+v)=I(x,y)+Ix*u+Iy*v
        其中:Ix和Iy是沿x和y方向的导数,可用sobel算子计算.
        
        E(u,v)=∑w(x,y)[I(x+u,y+v)-I(x,y)]²
              =∑w(x,y)[Ix²u²+2Ix*Iy*u*v+Iy²*v²]
                           |Ix² Ix*Iy|
              =∑w(x,y)[u,v]|Ix*Iy Iy²|
              
              =[u v]M|u|
                     |v|
                     
        M矩阵决定了E(u,v)的取值,M是Ix和Iy的二次函数,可以表示成椭圆的形状,椭圆的长短半轴由M的特征
        值a1和a2决定,方向由特征矢量决定
        
     
     共可分为三种情况:
        .图像中的直线.一个特征值大,另一个特征值小,a1>>a2或者a2>>a1.椭圆函数值在某一个方向上大,
        在其他方向小
        .图像中的平面.两个特征值都小,且近似相等,椭圆函数值再各个方向都有
        .图像中的角点.两个特征值都大,且近似相等,椭圆函数在所有方向都增大.
    
     Harris给出的角点计算方法并不需要计算具体的特征值,而是计算一个角点响应值R来判断角点.R的计算公式
     为:
        R=detM-α(traceM)²
        式中,detM为矩阵M的行列式,traceM为矩阵M的迹,α为常数取值范围为0.04~0.06.事实上,特征是隐含
        在detM和traceM中,因为:
            detM=a1a2
            traceM=a1+a2
    
        那么认为:
        .当R为大数时的正数是角点
        .当R为大数时的负数为边界
        .当R为小数认为是平坦区域
        
    
    API:
        cv.cornerHarris(src,blockSize,ksize,k)
        参数:
            img:数据类型为float32的输入图像
            blockSize:角点检测中要考虑的领域大小
            ksize:sobel算子求导使用的核大小
            k:角点检测中的自由参数取值范围为[0.04,0.06]
       
    '''
    
    # 读取图片
    img = cv.imread('image/house.jpg')
    
    # 转换成灰度图
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    
    # 数据类型转换成float32
    gray_float32 = np.float32(gray)
    
    # 角点检测
    dst = cv.cornerHarris(gray_float32, 2, 3, 0.04)
    
    #设置阈值,将角点绘制出来,阈值根据图像进行选择
    R=dst.max() * 0.01
    #这里将阈值设为dst.max()*0.01 只有大于这个值的数才认为数角点
    img[dst > R] = [0, 0, 255]
    
    
    plt.subplot(2,2,1)
    plt.imshow(cv.imread('image/house.jpg')[:,:,::-1])
    plt.title('原图')
    
    plt.subplot(2,2,2)
    plt.imshow(gray,cmap=plt.cm.gray)
    plt.title('灰度图(uint8型)')
    
    plt.subplot(2,2,3)
    plt.imshow(gray_float32,cmap=plt.cm.gray)
    plt.title('灰度图(float32型)')
    
    
    
    plt.subplot(2,2,4)
    plt.imshow(img[:,:,::-1])
    plt.title('检测结果')
    
    plt.savefig('image/角点检测(Harris).jpg')
    plt.show()
    
    

    2. Shi-Tomas算法

    import numpy as np
    import cv2 as cv
    import matplotlib.pyplot as plt
    
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正确显示中文
    
    #shi-Tomas算法
    '''
    shi-Tomas算法是对Harris角点检测算法的改进,一般会比Harris算法得到更好的角点.
    Harris算法的角点响应函数是将矩阵M的行列式值与M的迹相减,利用差值判断是否为角点,
    后来shi和Tomas提出改进的方法是,若矩阵M的两个特征值中较小的一个大于阈值,则认为
    他是角点,即:
        R=min(a1,a2)
    
    API:
        corners=cv.goodFeaturesToTrack(image,maxcorners,qualityLevel,minDistance)
        参数:
            image:输入的灰度图像
            maxCorners:获取角点数的数目
            qualityLevel:该参数指出最低可接受的角点质量水平,在0~1之间
            minDistance:角点之间的最小欧氏距离,避免得到相邻特征点
        返回:
            corners:搜索到的角点,在这里所有低于质量水平的角点被排除,然后把合格的角点按照质量排序,
            然后将质量较好的角点附近(小于最小欧氏距离)的角点删除,最后找到maxCorners个角点返回
    
    '''
    
    #读取图片
    img=cv.imread('image/house.jpg')
    
    #转成灰度图
    gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    
    #shi-Tomas角点检测
    corners=cv.goodFeaturesToTrack(gray,1000,0.05,10)
    
    #绘制角点
    for corner in corners:
        x,y=corner.ravel()
        cv.circle(img,(int(x),int(y)),2,(0,0,255),-1)
    
    
    
    
    plt.subplot(2,1,1)
    plt.imshow(cv.imread('image/house.jpg')[:,:,::-1])
    plt.title('原图')
    
    
    plt.subplot(2,1,2)
    plt.imshow(img[:,:,::-1])
    plt.title('检测结果')
    
    plt.savefig('image/角点检测(shi-Tomas).jpg')
    plt.show()
    

    3. SIFT算法

    import numpy as np
    import cv2 as cv
    import matplotlib.pyplot as plt
    
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正确显示中文
    
    # Sift算法
    '''
    Harris和shi-Tomas角点检测算法,这两种算法具有旋转不变性,但不具有尺度不变性,在原本能检测
    到角点的位置,当图像放大后,就检测不到角点了.
    
    尺度不变特征转换即SIFT(Scale-invariant feature transform).它用来侦测与描述影响中
    局部性特征,它在空间尺度中寻找极值点,并提取出其位置,尺度,旋转不变量,此算法由David Lowe
    在1999年所发表,2004年完善总结.应用范围包含物体识别,机器人地图感知与导航,影像缝合,3D模型
    建立,手势识别,影像追踪和动作对比等领域.
    
    
    SIFT算法实质是在不同的尺度空间上寻找关键点(特征点),并计算出关键点的方向.SIFT所查找到的关键点
    是一些十分突出,不会因为光照,仿射变换和噪音等因素影响而变化的点,如角点,边缘点,暗区的亮点及亮区
    的暗点.
    
    '''
    
    # 实现SIFT检测关键点的步骤
    
    # 1.实例化sift
    # sift = cv.SIFT_create()
    
    # 2.利用sift.detectAndCompute()检测关键点并计算
    '''
    kp,des=sift.detectAndCompute(gray,None)
    参数:
        gray:进行关键点检测的图像(灰度图)
    返回:
        kp:关键点信息,包括位置,尺度,方向信息
        des:关键点描述,每个关键点对应128个梯度信息的特征向量
    '''
    
    #3.将关键点检测结果绘制在图像上
    '''
    cv.drawKeyPoints(image,keypoints,outputimage,color,flags)
    参数:
        image:原始图像
        keypoints:关键点信息
        outputimage:输出图片,可以是原始图
        color:颜色设置,通过(b,g,r)修改颜色
        flags:绘图功能的标识设置
            1.cv.DRAW_MATCHES_FLAGS_DEFAULT:创建输出图像矩阵,使用现存的输出图像绘制匹配
            对和特征点,对每一个特征点只绘制中间点
            2.cv.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTING:不创建输出图像矩阵,而是在输出图像
            上绘制匹配对
            3.cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS:对每一个特征点绘制带大小和方向
            的关键点图形
            4.cv.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS:单点的特征点不被绘制
    '''
    
    #读取图像
    img=cv.imread('image/house.jpg')
    
    #转换成灰度图像
    gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    
    #实例化sift
    sift=cv.SIFT_create()
    
    
    #检测关键点并计算
    kp,des=sift.detectAndCompute(gray,None)
    
    #绘制关键点
    # cv.drawKeypoints(img,kp,img,(0,255,0),cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
    cv.drawKeypoints(img,kp,img,(0,255,0),cv.DRAW_MATCHES_FLAGS_DEFAULT)
    
    
    plt.subplot(2,1,1)
    plt.imshow(cv.imread('image/house.jpg')[:,:,::-1])
    plt.title('原图')
    
    
    plt.subplot(2,1,2)
    plt.imshow(img[:,:,::-1])
    plt.title('检测结果')
    
    plt.savefig('image/角点检测(sift).jpg')
    plt.show()
    
    

    4. FAST算法

    import numpy as np
    import cv2 as cv
    import matplotlib.pyplot as plt
    
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正确显示中文
    
    # fast算法
    '''
    Fast(全称Features from accelerated segment test)是一种用于角点检测的算法,
    该算法的原理是取图像中检测点,以改点为圆心的周围邻域内像素点判断检测点是否为角点,
    通俗的讲就是若一个像素周围有一定数量的像素与该点像素不同,则认为其为角点.
    
    基本流程:
        1.在图像中选取一个像素点p,来判断它是不是关键点.Ip等于点p的灰度值.
        2.以r为半径,覆盖p点周围M个像素,通常情况下,设置r=3,则M=16
        3.设置一个阈值t,如果在这16个像素点中存在n个连续像素点的灰度值都高于Ip+t,或者低于
          Ip-t,那么像素点p就被认为是一个角点,n一般取值为12
        4.由于检测特征点时是需要对所有的像素点进行监测,然而图像中绝大多数点都不是特征点,如
          果对每个像素点都进行上述的检测过程,那显然浪费许多时间,因此采用一种进行非特征点判别
          的方法:首先对候选点的周围每个90度的点:1,9,5,13进行测试(先测1和9,如果他们符合阈值
          要求,再测5和13).如果p是角点,那么这是个点中至少有3个要符合阈值要求,否则直接剔除,
          对保留下来的点再继续进行测试
    
    
    虽然这个检测器的效率很高,但它有一下几个缺点:
        .获得的候选点比较多
        .特征点的选取不是最优的,因为它的效果取决于与要解决的问题的角点的分布情况
        .进行非特征点判别时大量的点被丢弃
        .检测到的很多特征点都是相邻的
    前3个问题可以通过机器学习的方法解决,最后一个问题可以使用非最大值抑制的方法解决
    
    
    '''
    
    # 机器学习的角点检测
    '''
    1.选择一组训练图片(最好是跟最后应用相关的图片)
    2.使用Fast算法找出每幅图像的特征点,对图像中每一个特征点,将其周围的16个像素存储构成
      一个向量p
    3.每一个特征点的16个像素点都属于下列三类中的一种:
              | d Ip->x≤Ip-t (darker)
        Sp->x=| s Ip-t≤Ip->x≤Ip+t (similar)
              | b Ip->x≥Ip+t (brighter)
    4.根据这些像素点的分类,特征向量P也被分为3个子集:Pd,Ps,Pb
    5.定义一个新的布尔变量Kp,如果p是角点就设置为True,否则为False
    6.利用特征向量p,目标值Kp,训练ID3树
    7.将构建好的决策树运用于其他图像的快速检测
    
    '''
    
    # 非极大值抑制
    '''
    在筛选出来的候选点中有很多是紧挨在一起的,需要通过非极大值抑制来消除这种影响.
    为所有的候选点后确定一个打分函数V,V的值可以这样计算:先分别计算Ip与圆上16个
    点的像素值差值,取绝对值,再将这16个绝对值相加,就得到了V值
            V=∑|Ip-Ii| (i=1,2...16)
    
    最后比较毗邻候选点的V值,把V值较小的候选点pass掉
    Fast算法的思想与我们对角点的直观认识非常接近,化繁为简.Fast算法比其他角点的检测
    算法快,但是在噪声比较高时不够稳定,这需要设置合适的阈值.
    '''
    
    #Fast算法的实现
    
    
    '''
    1.实例化fast
        fast=cv.FastFeatureDetector_create(threshold,nonmaxSuppression)
        参数:
            threshold:阈值t,默认值为10(特征点与周围像素值差值的阈值)
            nonmaxSuppression:是否进行非极大值抑制,默认为True
        返回:
            fast:创建的FastFeatureDetector对象
    
    2.利用fast.detect检测关键点
        kp=fast.detect(grayImg,None)
        参数:
            grayImg:输入的灰度图片(彩色图像也可以)
        返回:
            kp:关键点信息,包括位置,尺度,方向信息
    
    3.将关键点监测结果绘制在图像上,与sift中的一样
    cv.drawKeypoints(image,keypoints,outputimage,color,flags)
    
    '''
    
    img=cv.imread('image/house.jpg')
    img2=cv.imread('image/house.jpg')
    
    #阈值设为20 进行非极大值抑制
    fast=cv.FastFeatureDetector_create(threshold=30,nonmaxSuppression=True)
    fast_not_nonmaxSuppression=cv.FastFeatureDetector_create(threshold=30,nonmaxSuppression=False)
    
    
    #检测出关键点
    kp=fast.detect(img,None)
    kp_not_nonmaxSuppression=fast_not_nonmaxSuppression.detect(img2,None)
    
    #将关键点绘制在图像上
    cv.drawKeypoints(img,kp,img,(0,255,0),cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
    cv.drawKeypoints(img2,kp_not_nonmaxSuppression,img2,(0,255,0),cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
    
    
    plt.subplot(2,1,1)
    plt.imshow(img2[:,:,::-1])
    plt.title('未进行非极大值抑制')
    
    plt.subplot(2,1,2)
    plt.imshow(img[:,:,::-1])
    plt.title('进行了非极大值抑制')
    
    plt.savefig('image/fast算法.jpg')
    plt.show()
    
    

    作者:童鸢

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python-OpenCV实现角点、直线、椭圆和矩形检测

    发表回复