• Python中OpenCV库(二)


    OpenCV库(二)

    五、 图像直方图

    1、 基本概念

    在统计学中,直方图是一种对数据分布情况的图形表示,是一种二维统计图表

    图像直方图是用一表示数字中亮度分布的直方图,标绘了图像中每个亮度值的像素数,可以借助观察该直方图了需要如何调整亮度分布的直方图。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此,一张较暗图片的图像直方图中的数据多集中于左侧和中间部分,而整体明亮,只有少量明显的图像则相反

    • 横坐标:图像中各个像素点的灰度级
    • 纵坐标:具有该灰度的像素个数

    2、 统计函数

    语法:calcHist(imgs, channels, mask, histSize, ranges[, hist[, accumulate]])

    参数:

    • imgs:图像集,传入一个图像列表

    • channels:指定通道

      需要用中括号括起来,输入图像是灰度图像的值是[0],彩色图像可以是[0], [1], [2],分别对应B, G, R

    • mask:掩码图像

      • 统计整副图像的直方图,设为None
      • 统计图像某一部分的直方图时,需要设置掩码图像
    • histSize:BINS的数量

      需要用中括号括起来,例如[256]

    • ranges:像素值范围,例如[0, 255]

    • accumulate:累积标识

      • 默认值为False
      • 如果被设置为True,则直方图在开始分配时不会被清零
      • 该参数允许从多个对象中计算单个直方图,或者用于实时更新直方图
      • 多个直方图的累积结果,用于对一组图像计算直方图
    # !/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo9.py"
    __time__ = "2022/7/18 18:09"
    import cv2.cv2 as cv2
    import numpy as np
    
    # 读取图像
    img = cv2.imread("./img/1.jpg")
    assert isinstance(img, np.ndarray)
    
    hist = cv2.calcHist([img], [0], None, [256], [0, 255])
    print(hist.shape)
    # 如何绘制直方图
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3、绘制直方图

    3.1 matplotlib
    # !/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo9.py"
    __time__ = "2022/7/18 18:09"
    import cv2.cv2 as cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 读取图像
    img = cv2.imread("./img/1.jpg", cv2.IMREAD_GRAYSCALE)  # 读取黑白效果
    assert isinstance(img, np.ndarray)
    # print(img.ravel())
    
    # 统计直方图数据
    plt.hist(img.ravel(), 256, [0, 255])  # 转换为一维数据,
    plt.show()  # 绘制直方图数据
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    3.2 OpenCV
    # !/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo9.py"
    __time__ = "2022/7/18 18:09"
    import cv2.cv2 as cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 读取图像
    img = cv2.imread("./img/1.jpg")  # 读取黑白效果
    assert isinstance(img, np.ndarray)
    # 统计直方图数据
    histB = cv2.calcHist([img], [0], None, [256], [0, 255])  # 蓝色通道
    histG = cv2.calcHist([img], [1], None, [256], [0, 255])  # 绿色通道
    histR = cv2.calcHist([img], [2], None, [256], [0, 255])   # 红色通道
    
    # 画图
    plt.plot(histB, color="b", label="blue")
    plt.plot(histG, color="g", label="green")
    plt.plot(histR, color="r", label="red")
    plt.legend()
    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

    4、 使用掩膜的直方图

    如何生成掩膜:

    • 先生成一个全黑的图像和原图图像大小一样大的图片,mask = np.zeros(img.shape, np.uint8)
    • 将想要的区域通过索引方式设置为255, mask[100: 200, 200: 300] = 255
    # !/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo9.py"
    __time__ = "2022/7/18 18:09"
    import cv2.cv2 as cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 读取图像
    img = cv2.imread("./img/1.jpg", cv2.IMREAD_GRAYSCALE)  # 读取黑白效果
    assert isinstance(img, np.ndarray)
    
    # 生成掩膜
    mask = np.zeros(img.shape, np.uint8)
    mask[100:700, 200: 900] = 255
    
    # # 进行与操作:img和img做与运算,结果再和mask做与运算
    # new_img = cv2.bitwise_and(img, img, mask=mask)
    
    # 掩膜直方图,专门对某一个区域
    hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 255])
    hist_gray = cv2.calcHist([img], [0], None, [256], [0, 255])
    
    # 统计结果
    plt.plot(hist_mask, label="mask")
    plt.plot(hist_gray, label="gray")
    plt.legend()
    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

    六、 视频采集

    1、 打开视频

    • 视频是有图片组成的,视频的每一帧都是一副图片,一般是30fps,表示每秒显示30张图片
    • cv2.VideoCapture可以捕获摄像头,用数字来表示不同的设备,比如0,1
    • 如果是视频文件,可以直接指定路径即可
    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo10.py"
    __time__ = "2022/7/29 19:20"
    import cv2.cv2 as cv
    
    # 打开视频文件
    vc = cv.VideoCapture("./img/1.mp4")
    # 打开摄像头
    vc_ = cv.VideoCapture(0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2、 读取数据

    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo10.py"
    __time__ = "2022/7/29 19:20"
    import cv2.cv2 as cv
    
    # 打开摄像头,并读取数据
    vc = cv.VideoCapture(0)  # 如果打开失败,或者是没有读取到数据,不会报错
    while vc.isOpened():  # 如果摄像头成功打开
        # 读每一帧数据,返回标记,True表示读到了数据,False表示是没有读到数据
        flag, frame = vc.read()
        if not flag:
            break  # 如果没有读到数据,直接退出
        # 显示数据
        cv.imshow("video", frame)
        key = cv.waitKey(30)  # 注意,不能写0,不然要点退出才能展示下一帧的数据,每隔10毫秒显示数据
        if key & 0xFF == ord('q'):  # 使用 key 来监控键盘中输入的内容
            break
    
    # 别忘了释放资源
    vc.release()
    cv.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3、 视频属性

    参数propld功能
    cv2.CAP_PROP_POS_MSEC0视频文件的当前位置(以毫秒为单位)或视频捕获时间戳
    cv2.CAP_PROP_POS_FRAMES1基于0的索引将被解码/捕获下一帧
    cv2.CAP_PROP_POS_AVI_RATIO2视频文件的相对位置:0 - 视频的开始,1 - 视频的结束
    cv2.CAP_PROP_FRAME_WIDTH3帧的宽度
    cv2.CAP_PROP_FRAME_HEIGHT4帧的高度
    cv2.CAP_PROP_FPS5帧速
    cv2.CAP_PROP_FOURCC64个字符表示的视频编码器格式
    cv2.CAP_PROP_FRAME_COUNT7帧数
    cv2.CAP_PROP_FORMAT8byretrieve()返回的Mat对象的格式
    cv2.CAP_PROP_MODE9指示当前捕获模式的后端特定值
    cv2.CAP_PROP_BRIGHTNESS10图像的亮度(仅适用于相机)
    cv2.CAP_PROP_CONTRAST11图像对比度(仅适用于相机)
    cv2.CAP_PROP_SATURATION12图像的饱和度(仅适用于相机)
    cv2.CAP_PROP_HUE13图像的色相(仅适用于相机)
    cv2.CAP_PROP_GAIN14图像的增益(仅适用于相机)
    cv2.CAP_PROP_EXPOSURE15曝光(仅适用于相机)
    cv2.CAP_PROP_CONVERT_RGB16表示图像是否应转换为RGB的布尔标志
    cv2.CAP_PROP_WHITE_BALANCE17
    cv2.CAP_PROP_RECTIFICATION18立体摄像机的整流标志

    获取和修改属性值:

    vc = cv.VideoCapture(0)  # 如果打开失败,或者是没有读取到数据,不会报错
    vc.set(propId, value)  # 设置视频的属性值
    """
    :propId: 上面表格中的id值
    :value: 修改后的值
    """
    vc.get(propId)  # 获取对应功能的值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4、 视频录制

    cv2.VideoWriter()

    • 参数:
      1. 输出文件
      2. 多媒体文件的格式(VideoWriter_fourcc)
      3. 帧率
      4. 分辨率

    write编码,并写入缓存

    release函数将缓存内容写入磁盘,并释放资源

    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo10.py"
    __time__ = "2022/7/29 19:20"
    
    import cv2.cv2 as cv
    
    # 打开摄像头,并读取数据
    cap = cv.VideoCapture(0)
    
    fourcc = cv.VideoWriter_fourcc(*'mp4v')  # 文件格式,*表示解包操作
    
    vw = cv.VideoWriter("./img/outer.mp4", fourcc, 30, (640, 480))  # (640, 480) 表示摄像头拍摄视频,这个大小不能搞错
    cap.set(cv.CAP_PROP_FPS, 30)  # 设置摄像头的帧率
    
    while cap.isOpened():  # 根据flag来判断是否读到了视频数据
        # 读每一帧数据,返回标记,True表示读到了数据,False表示是没有读到数据
        flag, frame = cap.read()
        if not flag:
            print("没有捕获到视频数据")
            break  # 如果没有读到数据,直接退出
        # 将数据写入文件中
        vw.write(frame)
        cv.imshow("frame", frame)
        if cv.waitKey(1000 // 30) == ord('q'):
            break
    
    vw.release()  # 将数据内容写入文件中
    cap.release()  # 并释放缓存内容
    cv.destroyAllWindows()  # 摧毁窗口
    
    • 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

    VideoWriter_fourcc

    • CV_FOURCC('I','4','2','0') :未压缩的YUV编码,4:2:0色度子采样。这种编码广泛兼容,但会产生大文件。文件扩展名应为.avi
      CV_FOURCC('P','I','M','1') :MPEG-1编码。文件扩展名应为.avi。
      CV_FOURCC('X','V','I','D') :相对较旧的MPEG-4编码。如果要限制结果视频的大小,这是一个很好的选择。文件扩展名应为.avi。
      CV_FOURCC('M','P','4','V') :另一个相对较旧的MPEG-4编码。如果要限制结果视频的大小,这是一个很好的选择。文件扩展名应为.mp4
      CV_FOURCC('X','2','6','4'): 一种比较新的MPEG-4编码方式。如果你想限制结果视频的大小,这可能是最好的选择。文件扩展名应为.mp4
      CV_FOURCC('F','L','V','1') :此选项为Flash视频。文件扩展名应为.flv
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    5、 控制鼠标

    OpenCV 允许我们对窗口上的鼠标动作做出响应

    • setMouseCallbask(winname, callback, userdata)
      • winname:窗口名字
      • callback:回调函数
      • userdata:给回调函数传递的参数
    • callback(event, x, y, flags, userdata)
      • event:鼠标事件
      • x, y:坐标点
      • flags:主要用于组合键
      • userdata:给回调函数传递的参数

    鼠标事件:

    EVENT_MOUSEMOVE 0            # 滑动
    EVENT_LBUTTONDOWN 1          # 左键点击
    EVENT_RBUTTONDOWN 2          # 右键点击
    EVENT_MBUTTONDOWN 3          # 中键点击
    EVENT_LBUTTONUP 4            # 左键放开
    EVENT_RBUTTONUP 5            # 右键放开
    EVENT_MBUTTONUP 6            # 中键放开
    EVENT_LBUTTONDBLCLK 7        # 左键双击
    EVENT_RBUTTONDBLCLK 8        # 右键双击
    EVENT_MBUTTONDBLCLK 9        # 中键双击
    
    """组合键"""
    EVENT_FLAG_LBUTTON 1       # 左键拖曳  
    EVENT_FLAG_RBUTTON 2       # 右键拖曳  
    EVENT_FLAG_MBUTTON 4       # 中键拖曳  
    EVENT_FLAG_CTRLKEY 8       # (8~15)按Ctrl不放事件  
    EVENT_FLAG_SHIFTKEY 16     # (16~31)按Shift不放事件  
    EVENT_FLAG_ALTKEY 32       # (32~39)按Alt不放事件  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    示例:

    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo10.py"
    __time__ = "2022/7/29 19:20"
    
    import numpy as np
    import cv2.cv2 as cv
    
    
    # 回调函数函数名可以随便取
    def callback_mouse(event, x, y, flags, userdata):
        """
        用来监听鼠标事件的函数
        :param event:  鼠标事件
        :param x: x坐标
        :param y: y坐标
        :param flags: 用于组合键
        :param userdata: 传入的数据
        :return: None
        """
        print(event, x, y, flags, userdata)
    
    
    # 创建窗口
    cv.namedWindow("mouse", cv.WINDOW_NORMAL)
    cv.resizeWindow("mouse", 640, 360)
    
    # 设置鼠标回调函数
    cv.setMouseCallback("mouse", callback_mouse, "这个为传入的用户数据")  # 给mouse窗口添加回调函数
    img = np.zeros((360, 640, 3), np.uint8)
    while True:
        cv.imshow("mouse", img)
        if cv.waitKey(1) == ord("q"):
            break
    
    cv.destroyAllWindows()
    
    • 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

    七、 特征检测

    1、 基本概念

    特征检测是计算机视觉和图像处理中的一种概念,它指的是使用计算机提取图像信息,决定每个图像的点是否属于一个图像特征。特征检测的结果是把图像上的点分为不同的子集,这些子集往往属于孤立的点,连续的曲线或者连续的区域

    特征检测包括边缘检测、角检测、区域检测和脊检测

    特征检测应用场景:

    • 图像搜索,比如以图搜图
    • 拼图游戏
    • 图像拼接

    平坦部分很难找到它在原图中的位置

    边缘相对于平坦要好找一些,但是也不能立马确定

    角点可以马上找到其在原图中的位置

    图像特征就是值有意义的图像区域,具有独特性,易于识别,比较角点,斑点及高密度区

    在图像特征中最重要的就是角点,哪些是角点呢?

    • 灰度梯度的最大值对应的像素
    • 两条线的交点
    • 极值点

    2、 Harris角点检测

    人眼对角点的识别通常是在一个局部的小区域或小窗口完成的。如果在各个方向上移动这个特征的小窗口,窗口内区域的灰度发生了较大的变化,那么就认为在窗口内遇到了角点。如果这个特定的窗口在图像各个方向上移动时,窗口内图像的灰度没有发生变化,那么窗口内就不存在角点;如果窗口在某一个方向移动时,窗口内图像的灰度发生了较大的变化,而在另一些方向上没有发生变化,那么,窗口内的图像可能就是一条直线的线段。

    在这里插入图片描述

    对于图像I(x,y)I(x,y),当在点(x,y)(x,y)处平移(Δx,Δy)(Δx,Δy)后的自相似性,可以通过自相关函数给出:

    https://images.cnblogs.com/cnblogs_com/blogs/722174/galleries/2074790/o_220730023020_Snipaste_2022-07-30_10-29-50.png

    W(x,y)W(x,y)是以点(x, y)(x, y)为中心的窗口,即加权函数,例如高斯加权函数

    https://images.cnblogs.com/cnblogs_com/blogs/722174/galleries/2074790/o_220730023219_image.png

    基于泰勒展开式,对图像(x, y)I(x, y)在平移(Δx,Δy)(Δx,Δy)后进行一阶近似

    https://images.cnblogs.com/cnblogs_com/blogs/722174/galleries/2074790/o_220730023401_Snipaste_2022-07-30_10-29-50.png

    其中,lx, lylx, ly 是图像I(x,y)I(x,y)的偏导数

    近似可得:

    https://images.cnblogs.com/cnblogs_com/blogs/722174/galleries/2074790/o_220730024613_image.png

    其中M:

    https://images.cnblogs.com/cnblogs_com/blogs/722174/galleries/2074790/o_220730024832_Snipaste_2022-07-30_10-29-50.png

    化简可得:

    https://images.cnblogs.com/cnblogs_com/blogs/722174/galleries/2074790/o_220730025350_Snipaste_2022-07-30_10-29-50.png

    二次项函数本质上就是一个椭圆函数,椭圆方程为:

    https://images.cnblogs.com/cnblogs_com/blogs/722174/galleries/2074790/o_220730025618_Snipaste_2022-07-30_10-29-50.png

    椭圆函数特征值与图像中的角点、直线(边缘)和平面之间的关系如下图所示。共可分为三种情况:

    • 图像中的直线。一个特征值大,另一个特征值小,λ1≫λ2λ1≫λ2或λ2≫λ1λ2≫λ1。自相关函数值在某一方向上大,在其他方向上小。
    • 图像中的平面。两个特征值都小,且近似相等;自相关函数数值在各个方向上都小。
    • 图像中的角点。两个特征值都大,且近似相等,自相关函数在所有方向都增大。

    根据二次项函数特征值的计算公式,我们可以求M(x,y)M(x,y)矩阵的特征值。但是Harris给出的角点差别方法并不需要计算具体的特征值,而是计算一个角点响应值RR来判断角点。RR的计算公式为:
    R = d e t M − α ( t r a c e M ) 2 d e t M = λ 1 λ 2 = A C − B 2 t r a c e M = λ 2 + λ 2 = A + C R=detM−α(traceM)^2\\ detM=\lambda 1λ2=AC−B^2\\ traceM=λ2+λ2=A+C R=detMα(traceM)2detM=λ1λ2=ACB2traceM=λ2+λ2=A+C
    语法:cornerHarris(src, blockSize, ksize, k[, dist[, borderType]])

    参数:

    • blockSize:检测窗口大小
    • ksize:sobel的卷积核
    • k:权重系数,即上面公式中的 α \alpha α,是一个经验值,一般取0.04~0.06之间,一般默认0.04
    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo10.py"
    __time__ = "2022/7/29 19:20"
    import cv2.cv2 as cv2
    
    # 读取图片
    img = cv2.imread("./img/1.jpg")
    # 转换为灰度图
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 角点检测
    # blockSize 没有要求是必须奇数的,返回角点响应,R值
    dst = cv2.cornerHarris(img_gray, 2, 3, 0.04)
    # 显示角点
    # 设定阈值:dst > 0.01 * dst.max()(经验值)
    img[dst > (0.01 * dst.max())] = [0, 0, 255]  # 找到这些角点,并且将角点的颜色改为红色
    
    cv2.imshow("img", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3、 SIFT 关键点检测

    SIFT,即尺度不变换特征转换,适用于图像处理领域的一种描述,这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子

    Harris角点具有旋转不变的特性,但是缩放后,原来的角点有可能不就是角点了

    算法原理:原理中公式太多,观摩大佬的文章

    使用算法的步骤:

    • 创建SIFT对象:sift = cv2.xfeatures2d.SIFT_create()
    • 进行检测:kp = sift.detect(img, ...)
    • 绘制关键点:cv2.drawKeypoints(gray, kp, img)
    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo10.py"
    __time__ = "2022/7/29 19:20"
    
    import cv2.cv2 as cv2
    
    # 读取图片
    img = cv2.imread("./img/1.jpg")
    # 转换为灰度图
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 注意:xfeatures2d是OpenCV的扩展包,需要安装opencv-contrib-python才有
    # 创建sift对象
    sift = cv2.xfeatures2d.SIFT_create()  # type: cv2.SIFT
    # 进行检测
    kp = sift.detect(img_gray)  # 是一个列表,里面存放的是封装的KeyPoint对象
    # 绘制关键点
    cv2.drawKeypoints(img_gray, kp, img)  # 创建角点的对象 角点列表 输出图像
    cv2.imshow("img", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    关键点和描述子:

    • 关键点:位置、大小和方向
    • 描述子:记录了关键点周围对其有共享的像素点的一组向量值,其不受仿射变换,光照变换等影响。描述子的作用就是特征匹配,在后面进行特征匹配的时候会用上
    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo10.py"
    __time__ = "2022/7/29 19:20"
    
    import cv2.cv2 as cv2
    
    # 读取图片
    img = cv2.imread("./img/1.jpg")
    # 转换为灰度图
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 注意:xfeatures2d是OpenCV的扩展包,需要安装opencv-contrib-python才有
    # 创建sift对象
    sift = cv2.xfeatures2d.SIFT_create()  # type: cv2.SIFT
    
    # 进行检测
    # kp = sift.detect(img_gray)  # 是一个列表,里面存放的是封装的KeyPoint对象
    # 检测关键点,并计算描述子
    # kp, des = sift.compute(img, kp)
    # 或者一步到位,把关键点和描述子一起检测出来
    kp, des = sift.detectAndCompute(img_gray, None)
    # 绘制关键点
    cv2.drawKeypoints(img_gray, kp, img)  # 创建角点的对象 角点列表 输出图像
    cv2.imshow("img", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 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

    4、 Shi-Tomasi角点检测

    • Shi-Tomasi是Harris角点检测的改进
    • Harris角点检测计算的稳定性和K有关,而K是一个经验值,不太好设定最佳的K值
    • Shi-Tomasi发现,角点的稳定性其实和矩阵M的较小特征值有关,于是直接用较小的那个特征值作为分数,这样就不用调整K值了
      • Shi-Tomasi将分数公式改为如下形式: R = m i n ( λ 1 λ 2 ) R=min(\lambda_1\lambda_2) R=min(λ1λ2)
      • 和Harris一样,如果该分数大于设定的阈值,我们就认为它是一个角点

    语法:goodFeaturesToTrack(img, maxComers, qualityLevel, minDistance[, corners[, mask[, blockSize[, userHarrisDetect[, k]]]]])

    • maxCorner:角点的最大数,值为0表示无限制
    • qualityLevel:角点质量,小于1.0的整数,一般在0.01~0.1之间
    • minDiatance:教之间最小欧氏距离,忽略小于此距离的点
    • mask:掩膜
    • blockSize:检测窗口的大小
    • userHarris:是否使用Harris算法
    • k:默认是0.04
    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo10.py"
    __time__ = "2022/7/29 19:20"
    import numpy as np
    import cv2.cv2 as cv2
    
    # 读取图片
    img = cv2.imread("./img/1.jpg")
    # 转换为灰度图
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    corners = cv2.goodFeaturesToTrack(img_gray, 100, 0.01, 10)  # 最大找出100个角点
    corners = np.int0(corners)  # 这个为坐标
    # 画出角点
    for i in corners:
        # i 相当于corners中的每一行数据
        # ravel() 二维数据变为一维数据
        x, y = i.ravel()
        cv2.circle(img, (x, y), 3, (0, 255, 0), -1)  # 实心圆
    
    cv2.imshow("shi-tomasi", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 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

    5、 Fast算法

    5.1 原理
    1. 在图像中选取一个像素点 p,来判断它是不是关键点。 Ip 等于像素点 p的灰度值。
    2. 选择适当的阈值 t。
    3. 如下图所示在像素点 p 的周围选择 16 个像素点进行测试。
    1. 如果在这 16 个像素点中存在 n 个连续像素点的灰度值都高于 Ip + t,或者低于 Ip − t,那么像素点 p 就被认为是一个角点。如上图中的虚线所示,n 选取的值为 12。

    2. 为了获得更快的效果,还采用了而外的加速办法。首先对候选点的周围每个 90 度的点: 1, 9, 5, 13 进行测试(先测试 1 和 19, 如果它们符合阈值要求再测试 5 和 13)。如果 p 是角点,那么这四个点中至少有 3 个要符合阈值要求。如果不是的话肯定不是角点,就放弃。对通过这步测试的点再继续进行测试(是否有 12 的点符合阈值要求)。

    这个检测器的效率很高,但是它有如下几条缺点:
    • 当 n<12 时它不会丢弃很多候选点 (获得的候选点比较多)。
    • 像素的选取不是最优的,因为它的效果取决与要解决的问题和角点的分布情况。
    • 高速测试的结果被抛弃
    • 检测到的很多特征点都是连在一起的。

    前 3 个问题可以通过机器学习的方法解决,最后一个问题可以使用非最大值抑制的方法解决。

    5.2 机器学习的角点检测器
    1. 选择一组训练图片(最好是跟最后应用相关的图片)

    2. 使用 FAST 算法找出每幅图像的特征点

    3. 对每一个特征点,将其周围的 16 个像素存储构成一个向量。对所有图像都这样做构建一个特征向量 P

    4. 每一个特征点的 16 像素点都属于下列三类中的一种

    5. 根据这些像素点的分类,特征向量 P 也被分为 3 个子集: Pd, Ps, Pb

    6. 定义一个新的布尔变量 Kp,如果 p 是角点就设置为 Ture,如果不是就设置为 False。

    7. 使用 ID3 算法(决策树分类器)使用ID3算法(决策树分类器)使用变量Kp查询每个子集,了解真实类的知识。 它选择产生关于候选像素是否是由Kp的熵测量的角的最多信息的x

    8. 上述方法递归地应用于所有子集,直到其熵为零。

    9. 将构建好的决策树运用于其他图像的快速的检测

    5.3 非极大值抑制

    使用极大值抑制的方法可以解决检测到的特征点相连的问题:

    1. 对所有检测到到特征点构建一个打分函数 V。 V 就是像素点 p 与周围 16个像素点差值的绝对值之和。
    2. 计算临近两个特征点的打分函数 V,把V值较小的候选点去除掉
    3. 忽略 V 值最低的特征点
      总结:
    • FAST算法比其他角点检测法都快;
    • 在噪声很高时不稳定,这是由阈值决定的。
    5.4 实现
    import cv2.cv2 as cv2
    
    # 实例化对象
    fast = cv2.FastFeatureDetector_create(threshold=10, nonmaxSuppression=True)  # 实例化一个对象
    """
    	:threshold: 阈值,默认为10
    	:nonmaxSuppression: 是否进行非极大值抑制,默认为True
    	:return 返回创建的对象
    """
    # 进行关键点检测
    kp = fast.detect(img1)  # 可以用传入彩色图像,返回关键点信息
    # fast.setNonmaxSuppression(False)  # 取消非极大值抑制
    cv2.drawKeypoints(img1, kp, img2)  # 进行角点的绘制
    cv2.imshow("a", img2)  # 展示图片
    cv2.waitKey(0)  # 绘制图片
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6、 ORB算法

    6.1 原理

    ORB算法是FAST算法和BRIEF算法的结合,ORB可以用来对图像中的关键点快速创建特征向量,并用这些特征向量来识别图像中的对象。即,提出了构造金字塔,为Fast特征点添加了方向,从而使得关键点具有了尺度不变性。

    具体实现原理较为复杂,请到百度自行搜索

    6.2 实现
    import cv2.cv2 as cv2
    
    # 创建ORB对象
    orb = cv2.ORB_create(nfeatures)  # 实例化ORB对象
    """
    	:nfeatrues: 最大特征数量
    """
    # 进行关键点检测
    kp, des = orb.detectAndCompute(gray)
    """
        :gray: 进行关键点检测的图像,注意是灰度图像
        :return: 
            :kp: 关键点信息
            :des: 关键点描述符,每个关键点BRIEF特征向量,二进制字符串
    """
    # 将关键点检测结果绘制在图像上
    cv2.drawKeypoints(img1, kp, img2)
    cv2.imshow("a", img2)  # 展示图片
    cv2.waitKey(0)  # 绘制图片
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    如何确定论文研究方向,看了很多论文还是没有头绪?
    【面试经典150 | 数组】跳跃游戏 II
    rabbitmq发送消息通用接口
    网络安全(黑客技术)—小白自学笔记
    手写数组方法之数组静态方法
    iOS 提高Xcode运行速度
    第一行代码Android 第九章9.3 解析XML格式(Pull解析方式,SAX解析方式)
    WiFi在Settings中的热点开启流程小结
    短视频SEO优化教程 自媒体SEO优化技巧方法
    MySQL数据库详解 二:数据库的高级语句(高级查询语句)
  • 原文地址:https://blog.csdn.net/qq_62789540/article/details/126643012