Python中OpenCV库(二)

文章目录

  • OpenCV库(二)
    • 五、 图像直方图
      • 1、 基本概念
      • 2、 统计函数
      • 3、绘制直方图
        • 3.1 matplotlib
        • 3.2 OpenCV
      • 4、 使用掩膜的直方图
    • 六、 视频采集
      • 1、 打开视频
      • 2、 读取数据
      • 3、 视频属性
      • 4、 视频录制
      • 5、 控制鼠标
    • 七、 特征检测
      • 1、 基本概念
      • 2、 Harris角点检测
      • 3、 SIFT 关键点检测
      • 4、 Shi-Tomasi角点检测
      • 5、 Fast算法
        • 5.1 原理
        • 5.2 机器学习的角点检测器
        • 5.3 非极大值抑制
        • 5.4 实现
      • 6、 ORB算法
        • 6.1 原理
        • 6.2 实现

OpenCV库(二)

五、 图像直方图

1、 基本概念

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

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

  • 横坐标:图像中各个像素点的灰度级
  • 纵坐标:具有该灰度的像素个数
Python中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)
# 如何绘制直方图

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()  # 绘制直方图数据

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()

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、 打开视频

  • 视频是有图片组成的,视频的每一帧都是一副图片,一般是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)

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()

3、 视频属性

参数 propld 功能
cv2.CAP_PROP_POS_MSEC 0 视频文件的当前位置(以毫秒为单位)或视频捕获时间戳
cv2.CAP_PROP_POS_FRAMES 1 基于0的索引将被解码/捕获下一帧
cv2.CAP_PROP_POS_AVI_RATIO 2 视频文件的相对位置:0 - 视频的开始,1 - 视频的结束
cv2.CAP_PROP_FRAME_WIDTH 3 帧的宽度
cv2.CAP_PROP_FRAME_HEIGHT 4 帧的高度
cv2.CAP_PROP_FPS 5 帧速
cv2.CAP_PROP_FOURCC 6 4个字符表示的视频编码器格式
cv2.CAP_PROP_FRAME_COUNT 7 帧数
cv2.CAP_PROP_FORMAT 8 byretrieve()返回的Mat对象的格式
cv2.CAP_PROP_MODE 9 指示当前捕获模式的后端特定值
cv2.CAP_PROP_BRIGHTNESS 10 图像的亮度(仅适用于相机)
cv2.CAP_PROP_CONTRAST 11 图像对比度(仅适用于相机)
cv2.CAP_PROP_SATURATION 12 图像的饱和度(仅适用于相机)
cv2.CAP_PROP_HUE 13 图像的色相(仅适用于相机)
cv2.CAP_PROP_GAIN 14 图像的增益(仅适用于相机)
cv2.CAP_PROP_EXPOSURE 15 曝光(仅适用于相机)
cv2.CAP_PROP_CONVERT_RGB 16 表示图像是否应转换为RGB的布尔标志
cv2.CAP_PROP_WHITE_BALANCE 17
cv2.CAP_PROP_RECTIFICATION 18 立体摄像机的整流标志

获取和修改属性值:

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

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()  # 摧毁窗口

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
    

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不放事件  

示例:

#!/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、 Harris角点检测

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

Python中OpenCV库(二)_第2张图片

对于图像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)为中心的窗口,即加权函数,例如高斯加权函数

Python中OpenCV库(二)_第3张图片

基于泰勒展开式,对图像(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

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

Python中OpenCV库(二)_第4张图片

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

  • 图像中的直线。一个特征值大,另一个特征值小,λ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()

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()

关键点和描述子:

  • 关键点:位置、大小和方向
  • 描述子:记录了关键点周围对其有共享的像素点的一组向量值,其不受仿射变换,光照变换等影响。描述子的作用就是特征匹配,在后面进行特征匹配的时候会用上
#!/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()

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()

5、 Fast算法

5.1 原理

  1. 在图像中选取一个像素点 p,来判断它是不是关键点。 Ip 等于像素点 p的灰度值。
  2. 选择适当的阈值 t。
  3. 如下图所示在像素点 p 的周围选择 16 个像素点进行测试。
Python中OpenCV库(二)_第5张图片
  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 像素点都属于下列三类中的一种

    Python中OpenCV库(二)_第6张图片
  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()

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()

你可能感兴趣的:(OpenCV,opencv,python,计算机视觉)