cornerHarris() 角点检测具有旋转不变特性。关于角点检测用一幅图来讲解:
窗口函数(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,y∑w(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 的特征值:我们可以判断一个区域是否是角点,边界或者是平面。
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()
goodFeaturesToTrack()是cornerHarris() 函数升级版。该函数的角点检测效果与cornerHarris()函数效果差不多。
Harris 角点检测的打分公式为:
R = λ 1 λ 2 − k ( λ 1 + λ 2 ) 2 R =λ_1λ_2-k(λ_1+λ_2)^2 R=λ1λ2−k(λ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
OpenCV提供了一个快速检测角点的类FastFeatureDetector。FAST(Features from Accelerated Segment Test)这个算法效率确实比较高。
FAST类下面的detect方法来检测对应的角点,输出格式都是vector。
优点:FAST算法很快
缺点:在噪声高的时候鲁棒性差,性能依赖阈值的设定。
"""
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
"""
可以调用OpenCV中的函数,指定阈值,知否使用非极大值抑制,使用邻域大小等等。
"""
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