一、cornerHarris() 函数

cornerHarris() 角点检测具有旋转不变特性。关于角点检测用一幅图来讲解:

  • 红框进行移动:无论是往哪个方向进行偏移,都会对框框内的像素值造成很大的变动。那么红框 框住的区域的边角点,就是检测到的角点。
  • 黑框进行移动:水平方向上移动像素值的变化不大,垂直方向上移动那么就会变化很大;或者相反,则这种一般称为边缘区域。
  • 蓝框进行移动:无论是水平还是垂直的方向移动,框内像素值的变化不大。这种是内部区域。
    在这里插入图片描述
    1988 年的文章《A CombinedCorner and Edge Detector》中就已经提出了焦点检测的方法,被称为Harris 角点检测。他把这个简单的想法转换成了数学形式。将窗口向各个方向移动 ( u , v ) (u,v) (u,v) 然后计算所有差异的总和。表达式如下:
    E ( u , v ) = ∑ x , y w ( x , y ) ⎵ w i n d o w   f u n c t i o n [ I ( x + u , y + v ) ⎵ s h i f t e d   i n t e n s i t y − I ( x , y ) ] 2 ⎵ i n t e n s i t y E(u,v) = \sum_{x,y} \underbrace{w(x,y)}_{window\ function} \underbrace{[I(x+u,y+v)}_{shifted\ intensity}-\underbrace{I(x,y)]^2}_{intensity} E(u,v)=x,ywindow function w(x,y)shifted intensity [I(x+u,y+v)intensity I(x,y)]2

窗口函数(Sobel求导中使用的窗口) :可以是正常的矩形窗口,也可以是对每一个像素给予不同权重的高斯窗口
角点检测中要使 E ( μ , ν ) E(μ,ν) E(μ,ν) 的值最大。这就是说必须使方程右侧的第二项的取值最大。对上面的等式进行泰勒级数展开然后再通过几步数学换算(可以参考其他标准教材),我们得到下面的等式:
E ( u , v ) = [ u v ] ≈ M [ u v ] E(u,v) = \begin{bmatrix} u & v \end{bmatrix}\approx M\begin{bmatrix} u\\ v\end{bmatrix} E(u,v)=[uv]M[uv]

其中:
E ( u , v ) = ∑ x , y w ( x , y ) [ I x 2 I x I y I x I y I y 2 ] E(u,v) = \sum_{x,y}w(x,y)\begin{bmatrix}I_x^2 & I_xI_y\\I_xI_y & I_y^2\end{bmatrix} E(u,v)=x,yw(x,y)[Ix2IxIyIxIyIy2]

这里 I x \rm I_x Ix I y \rm I_y Iy 是图像在 x 和 y 方向的导数。(可以使用函数 cv2.Sobel(),计算得到)详情请点击。

他们根据一个用来判定窗口内是否包含角点的等式进行打分。

R = d e t ( M ) − k ( t r a c e ( M ) ) 2 R = det(M) - k(trace(M))^2 R=det(M)k(trace(M))2

其中:
d e t ( M ) = λ 1 λ 2 det(M) = \lambda_1 \lambda_2 det(M)=λ1λ2

t r a c e ( M ) = λ 1 + λ 2 trace(M) = \lambda_1+\lambda_2 trace(M)=λ1+λ2

λ 1 λ1 λ1 λ 2 λ2 λ2 是矩阵 M 的特征值:我们可以判断一个区域是否是角点,边界或者是平面。

  • λ 1 λ1 λ1 λ 2 λ2 λ2 都小时,|R| 也小,这个区域就是一个平坦区域
  • λ 1 ≫ λ 2 λ1\gg λ2 λ1λ2 或者 λ 1 ≪ λ 2 λ1 \ll λ2 λ1λ2 ,时 R 小于 0,这个区域是边缘
  • λ 1 λ1 λ1 和 λ2 都很大,并且 λ 1 λ1 λ1 λ 2 λ2 λ2 中的时,R 也很大,( λ 1 λ1 λ1 λ 2 λ2 λ2中的最小值都大于阈值)说明这个区域是角点
    可以用下图来表示我们的结论:
    对比图
    http://www.bubuko.com/infodetail-2498014.html
    cv2.cornerHarris()函数的返回值其实就是R值构成的灰度图像 灰度图像坐标会与原图像对应  R值就是角点分数 当R值很大的时候 就可以认为这个点是一个角点
cv2.cornerHarris(src=gray, blockSize, ksize, k, dst=None, borderType=None)
"""
cornerHarris参数:
	src       - 数据类型为 float32 的输入图像。(输入单通道图)
	blockSize - 角点检测中要考虑的领域大小。也就是计算协方差矩阵时的窗口大小
	ksize     - Sobel求导中使用的窗口大小
	k         - Harris 角点检测方程中的自由参数,取值参数为 [0.04,0.06].
	dst       - 输出图像
	borderType  - 边界的类型
"""

示例一:

# encoding:utf-8
import cv2
import numpy as np

filename = 'D:\\image_person\\00001.png'

img = cv2.imread(filename)
img = cv2.resize(img, (640, 480))

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)

# 输入图像必须是float32, 最后一个参数[0.04,0.06]
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
cv2.imshow('dst', dst)
dst = cv2.dilate(dst, None)

img[dst > 0.01 * dst.max()] = [0, 0, 255]
cv2.imshow('img', img)
cv2.imshow('dst2', dst)

cv2.waitKey(0)
cv2.destroyAllWindows()

左侧为原图,右侧为检测之后的图像。
在这里插入图片描述
参数测试:(我写了两组数据测试,使大家一目了然,BlockSize ,Ksize 这两个参数的含义)

import cv2
import numpy as np


img = cv2.imread('D:\\image_person\\06_01.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

gray = np.float32(gray)                 # cornerHarris函数图像格式为 float32

BlockSize =(2,3,4,5,7,9)
Ksize =(3,5,5,9,11,23)

for i,j in zip(BlockSize,Ksize):
    dst = cv2.cornerHarris(src=gray, blockSize=i, ksize=j, k=0.04)
    # 变量a的阈值为0.01 * dst.max(),如果dst的图像值大于阈值,那么该图像的像素点设为True,否则为False
    # 将图片每个像素点根据变量a的True和False进行赋值处理,赋值处理是将图像角点勾画出来
    a = dst>0.01 * dst.max()
    img[a] = [0, 0, 255]
    cv2.imshow('corners_' + str(i) + '_' + str(j), img)
    cv2.waitKey(0)           # 按Esc查看下一张

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

有时候我们检验时有很多角点都是粘连在一起的,通过加入非极大值抑制来进一步去除一些粘在一起的角点。也就是在一个窗口内,如果有多个角点则用值最大的那个角点,其他的角点都删除。
请看如下示例:

import cv2
import numpy as np

img = cv2.imread('./lou.png')
cv2.imshow('raw_img', img)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)                 # cornerHarris函数图像格式为 float32

J = (0.05,0.01,0.005)
for j in J:    # 遍历设置阈值:j * dst.max()
    dst = cv2.cornerHarris(src=gray, blockSize=5, ksize=7, k=0.04)
    a = dst>j * dst.max()
    img[a] = [0, 0, 255]
    cv2.imshow('corners_'+ str(j), img)
    cv2.waitKey(0)           # 按Esc查看下一张

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

二、Shi-Tomasi 角点检测 goodFeaturesToTrack()

goodFeaturesToTrack()是cornerHarris() 函数升级版。该函数的角点检测效果与cornerHarris()函数效果差不多。
Harris 角点检测的打分公式为:
R = λ 1 λ 2 − k ( λ 1 + λ 2 ) 2 R =λ_1λ_2-k(λ_1+λ_2)^2 R=λ1λ2k(λ1+λ2)2

Shi-Tomasi 使用的打分函数为:
R = m i n ( λ 1 , λ 2 ) R =min(λ_1,λ_2) R=min(λ1,λ2)

如果打分超过阈值,我们就认为它是一个角点。我们可以把它绘制到 λ 1 ~ λ 2 λ_1 ~λ_2 λ1λ2 空间中,就会得到下图:(角点:绿色区域)可对比上图得出差异)
在这里插入图片描述
opencv中C++ 函数及参数如下:

void cv::goodFeaturesToTrack(
		cv::InputArray image,    // 输入图像(CV_8UC1 CV_32FC1)
		cv::OutputArray corners, // 输出角点vector
		int maxCorners,          // 最大角点数目
		double qualityLevel,     // 质量水平系数(小于1.0的正数,一般在0.01-0.1之间)
		double minDistance,      // 最小距离,小于此距离的点忽略
		// 以下为可选参数
		cv::InputArray mask = noArray(), //指定感兴趣区域。 mask=0的点忽略
		int blockSize = 3,       // 使用的邻域数:计算协方差矩阵时的窗口大小
		bool useHarrisDetector = false,   // false ='Shi Tomasi metric'
		double k = 0.04         // Harris角点检测时使用
	);

// 常用如下参数:
void cv::goodFeaturesToTrack(image,corner,
                     		500,     // 最多检测到的角点数
                     		0.01,    // 阈值系数
                     		10);     // 角点间的最小距离

示例如下:(适合在目标跟踪中使用)

import numpy as np
import cv2


def getkpoints(imag, input1):
    mask1 = np.zeros_like(input1)
    x = 0
    y = 0
    w1, h1 = input1.shape
    input1 = input1[0:w1, 200:h1]

    try:
        w, h = imag.shape
    except:
        return None

    mask1[y:y + h, x:x + w] = 255          # 整张图片像素
    keypoints = []
    kp = cv2.goodFeaturesToTrack(input1, 200, 0.04, 7)
    if kp is not None and len(kp) > 0:
        for x, y in np.float32(kp).reshape(-1, 2):
            keypoints.append((x, y))
    return keypoints

def process(image):
    grey1 = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    grey = cv2.equalizeHist(grey1)
    keypoints = getkpoints(grey, grey1)

    if keypoints is not None and len(keypoints) > 0:
        for x, y in keypoints:
            cv2.circle(image, (int(x + 200), y), 3, (255, 255, 0))
    return image

if __name__ == '__main__':
    cap = cv2.VideoCapture("IMG_1521.mp4")
    while (cap.isOpened()):
        ret, frame = cap.read()
        frame = process(frame)
        cv2.imshow('frame', frame)
        if cv2.waitKey(27) & 0xFF == ord('q'):
            break
    cap.release()
    cv2.waitKey(0)
    cv2.destroyAllWindows()

关于cornerSubPix()亚像素角点检测请查看:https://blog.csdn.net/guduruyu/article/details/69537083

三、FAST() 特征检测

OpenCV提供了一个快速检测角点的类FastFeatureDetector。FAST(Features from Accelerated Segment Test)这个算法效率确实比较高。
FAST类下面的detect方法来检测对应的角点,输出格式都是vector。

优点:FAST算法很快
缺点:在噪声高的时候鲁棒性差,性能依赖阈值的设定。

cv2.drawKeypoints() 函数讲解
"""
cv2.drawKeypoints() 函数主要包含五个参数:
	image:       原始图片
	keypoints:  从原图中获得的关键点,这也是画图时所用到的数据
	outputimage:输出图片
	color:      颜色设置(b,g,r)的值,b=蓝色,g=绿色,r=红色。
	
	flags:      绘图功能的标识设置,标识如下:
				cv2.DRAW_MATCHES_FLAGS_DEFAULT  默认值
				cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
				cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG
				cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS
"""
关于FAST()函数示例,参数如下

可以调用OpenCV中的函数,指定阈值,知否使用非极大值抑制,使用邻域大小等等。

type参数:(可选如下参数)
cv2.FAST_FEATURE_DETECTOR_TYPE_5_8
cv2.FAST_FEATURE_DETECTOR_TYPE_7_12
cv2.FAST_FEATURE_DETECTOR_TYPE_9_16
"""
    etval = cv2.FastFeatureDetector_create([, threshold[, nonmaxSuppression[, type]]])  # 创建FAST检测器
    retval = cv2.FastFeatureDetector.getNonmaxSuppression()  # 返回布尔型 是否使用非极大值抑制
    retval = cv2.FastFeatureDetector.getThreshold()          # 返回阈值
    None = cv2.FastFeatureDetector.setNonmaxSuppression()    # 设定非极大值抑制 bool型
    None = cv2.FastFeatureDetector.setThreshold()            # 设定阈值
"""

import cv2


def Fast_detect_fault(img_01):
    fast = cv2.FastFeatureDetector_create()    # 初始化(参数可不写,也可以写入数字)
    keypoint = fast.detect(img_01,None)
    img_01 = cv2.drawKeypoints(img_01,keypoint,img_01,color=(255,0,0))
    cv2.imshow('fault.png',img_01)

    # Print all default params
    print ("Threshold: ", fast.getThreshold())
    print ("nonmaxSuppression: ", fast.getNonmaxSuppression())
    print ("neighborhood: ", fast.getType())
    print ("Total Keypoints with nonmaxSuppression: ", len(keypoint))


def Fast_detect_Setparam(img_02):
    # fast = cv2.FastFeatureDetector_create()
    # fast.setNonmaxSuppression(100) 使用fast.setNonmaxSuppression来设置默认参数

    threshold=(5,10,100)
    for thre in threshold:
        fast_02 = cv2.FastFeatureDetector_create(threshold=thre, nonmaxSuppression=True,
                                              type=cv2.FAST_FEATURE_DETECTOR_TYPE_5_8)  # 获取FAST角点探测器

        kp = fast_02.detect(img_02, None)                                 # 描述符
        img_0 = cv2.drawKeypoints(img_02, kp, img_02, color=(255, 0, 0))    # 画到img上面
        cv2.imshow('sp_'+str(thre),img_02)

        # Print all set params
        print("Threshold: ", fast_02.getThreshold())                   # 输出阈值
        print("nonmaxSuppression: ", fast_02.getNonmaxSuppression())   # 是否使用非极大值抑制
        print("neighborhood: ", fast_02.getType())
        print("Total Keypoints with nonmaxSuppression: ", len(kp))  # 特征点个数
        cv2.waitKey(0)


if __name__ == '__main__':
    img_01 = cv2.imread('./shanghai_01.png')
    img_02 = cv2.imread('./shanghai_02.png')
    Fast_detect_fault(img_01)
    Fast_detect_Setparam(img_02)

    cv2.waitKey(0)
    cv2.destroyAllWindows()
    # 展示效果如下。

在这里插入图片描述

参考文档:
https://my.oschina.net/u/3702502/blog/1815338
FAST() 特征检测:https://www.cnblogs.com/wyuzl/p/7834159.html
关于cornerSubPix()亚像素角点检测请查看:https://blog.csdn.net/guduruyu/article/details/69537083

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐