AB的特征是平面:在图像中很难找到具体位置
CD的特征是边缘:在图像中同样很难找具体的位置
EF的特征是角点:很容易被找到
角点是图像很重要的特征,对图像图形的理解和分析有很重要的作用,角点在三维场景重建、运动估计,目标跟踪,目标识别,图像配准与匹配等计算机视觉领域起着非常重要的作用。
学习目标:
理解Harris和shi-Tomas算法原理
能够利用Harris和Shi-Tomas进行角点检测
思想:Harris角点检测的思想是通过图像的局部的小窗口观察图像,角点的特征是窗口沿任意方向移动都会导致图像灰度的明显变化。
思想转换为数学形式:
既将局部窗口向各个方向移动(u,v)并计算所有灰度差异的综合,表达式如下:
其中I(x,y)是局部窗口的图像灰度;I(x+u,y+v)是平移后的图像灰度;w(x,y)是窗口函数,可以是矩形窗口也可以是对每一个像素赋予不用权重的高斯窗口,如下图所示:
E(u,v)的值最大处为最终的角点。移动之后的灰度值I(x+u,y+v)利用一阶泰勒公式展开有:
其中Ix和Iy是沿x和y方向的导数,可用sobel算子计算。
利用M来求角点,M矩阵决定了E(u,v)的取值,其为Ix,Iy的二次项函数,可以表示成椭圆,椭圆的长短半轴由M的特征值λ1和λ2决定,方向由特征矢量决定,如下图所示:
椭圆函数(M)特征值(λ1和λ2)与图像中的角点、直线(边缘)和平面之间的关系如下图所示:
共计分为三种情况:
(1)图像中的直线。一个特征值很大,另一个特征值很小,λ1>>λ2或λ2>>λ1,椭圆函数值在某一方向上大,在其他地方小
(2)图像中的平面。两个特征值都很小,且近似相等;椭圆函数数值在各个方向上都小
(3)图像中的角点。两个特征值都很大,且近似相等。椭圆函数数值在所有方向都大
Harris角点检测的计算方法不需要计算具体的特征值,而是根据M矩阵计算一个角点响应值R来判断角点。R的计算公式为:
式中detM为矩阵M的行列式:traceM为矩阵M的迹;α为常数,取值范围为0.04-0.06.事实上,特征是隐含在detM和traceM中,因为:
(1)当R为大数值的正数时是角点
(2)当R为大数值的负数时是边界
(3)当R为小数时认为是平坦区域
API: dst=cv2.cornerHarris(src,blockSize,ksize,k)
参数:
img:数据类型为float32
blockSize:角点检测中要考虑的邻域大小
ksize:sobel求导使用的核大小
k:角点检测方程中的自由参数,取值参数为[0.04-0.06]
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img2=cv2.imread('harris.png')#
img=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
#2.harris角点检测
## 2.1 首先转换为float32类型
img=np.float32(img)
## 2.2 角点检测
dst=cv2.cornerHarris(img,2,3,0.04)
img2[dst>0.001*dst.max()]=(0,0,255)
cv2.imshow('origin',img2)
cv2.waitKey(0)
优点:
(1)旋转不变性:图像旋转还是可以进行角点检测的
(2)图像和灰度的仿射变换具有部分不变性,因为只使用一阶导数计算角点矩阵M,
缺点:
(1)对尺度变换敏感,不具备几何尺度不变性,提取的角点是像素级的。
Harris算法: 角点响应函数是矩阵M的行列式值和矩阵M的迹相减,利用差值判断是否为角点。
Shi-Tomasi算法: 是对Harris角点检测算法的改进,一般会比Harris算法得到更好的角点。它提出若响应矩阵M的两个特征值中较小的一个大于阈值,则认为它是角点,既:
(1)λ1和λ2均大于阈值,则认为是角点
(2)λ1和λ2均小于阈值,则认为是平坦区域
(3)只有一个大于阈值,则认为是边缘
API: corners=cv2.goodfeaturesToTrack(image,maxcorners,qualitylevel,minDistance)
参数:
img:输入灰度图像
maxCorners:获取角点数的数目
qualityLevel:该参数指出最低可接受的角点质量水平,在0-1之间
minDistance:角点之间最小的欧式距离,避免得到相邻特征点。
返回:
搜索到的角点,在这里所有低于质量水平的角点被排除掉,然后把合格的角点按质量排序,然后将质量好的角点附近的角点删掉,最后找到maxCorners个角点返回。
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img2=cv2.imread('conner2.png')#
img=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
#2.Shi-Tomas角点检测
# 角点检测
corner=cv2.goodFeaturesToTrack(img,1000,0.01,10)
print(corner)
for i in corner:
x,y=i.ravel()
cv2.circle(img2,(x,y),2,(0,0,255),-1)
cv2.imshow('origin',img2)
cv2.waitKey(0)
Harris算法和Shi-Tomas算法,具有旋转不变性,但不具有尺度不变性。
SIFT(尺度不变特征转换): 用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值,并提取出其位置、尺度、旋转不变量。应用范围包含物体识别、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作对比等领域。
SIFT算法实质: 在不同尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找的关键点是一些非常突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区域的亮点及亮区域的暗点。
(1)尺度空间极值检测: 搜索所有尺度上的候选关键点位置。通过高斯查分函数来识别潜在的对于尺度和旋转不变的关键点。
(2)关键点定位: 在每个候选区域的关键点位置,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。
(3)关键点方向确定: 基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向,所有后面的对图像数据的操作都相对于关键点方向、尺度和位置进行变换,从而保证了对于这些变换的不变性。
(4)关键点描述: 在每个关键点周围的邻域内,在选定的尺度上测量图像的局部梯度。这些梯度作为关键点的描述符,它允许比较大的局部形状的变形或光照变化。
在不同的尺度空间是不能使用相同的窗口检测极值点,对于小的关键点使用小的窗口,对大的关键点使用大的窗口,为了达到上述目的,我们使用尺度空间滤波器。(高斯核是唯一可以产生多尺度空间的核函数)
一个图像的尺度空间L(x,y,σ),定义为原始图像I(x,y)与一个可变尺度的2倍高斯函数G(x,y,σ)卷积运算,即:
其中:
σ是尺度空间因子,他决定了图像的模糊程度,在大尺度下(σ值大)表现的是图像的概貌信息,在小尺度下(σ值较小)表现的是图像的细节信息。
在opencv中实现关键点检测流程:
(1)实例化SIFT:shif=cv2.xfeature2d.SIFT_creat()
(2)检测关键点:kp,des=sift.detectAndCompute(gray,None)
参数:
gray:进行关键点检测的图像,注意是灰度图像
返回值:
kp:关键点信息,包括位置、尺度、方向信息
des:关键点描述符,每个关键点对应128个梯度信息的特征向量。
(3)将关键点绘制在图像上:cv2.drawKeypoint(image,keypoints,outputimage,color,flags)
参数:
image:原始图像
keypoints:步骤2得到的关键点信息
outputimage:输出图片,可以是原始图像
color:颜色,rgb格式
flags:绘制功能的标识设置
1.cv2.DRAW_MATCHES_FLAGS_DEFAULT:创建输出图像矩阵,使用现存的输出图像,绘制匹配对和特征点,对每一个关键点只绘制中间点。
2.cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG:不创建输出图像矩阵,而是在输出图像上绘制匹配对。
3.cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINT:单点的特征点,不被检测
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img2=cv2.imread('conner2.png')#
gray=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
#2.SIFT角点检测
# 2.1 实例化Sift
sift=cv2.xfeatures2d.SIFT_creat()
# 2.2 关键点检测
kp,des=sift.detectAndCompute(gray,None)
# 2.3 绘制图像
cv2.drawKeypoints(img2,kp,gray,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow('origin',img2)
cv2.waitKey(0)
SIFT在图像的不变特征提取方面拥有无语伦比的优势,但存在实时性不高的问题,有时特征点较少,对边缘光滑的目标无法准确提取特征点。自SIFT算法提出以来,人们就一直对其进行优化和改进,其中著名的就是SURF算法。
使用SIFT算法进行关键点检测和描述的执行速度比较慢,需要速度更快的算法。2006年提出了SUPF算法,是SIFT算法的增强版本,他计算量小,运行速度快,提取的特征与SIF几乎相同,将其与SIFT算法进行对比如下:
SIFT | SURF | |
---|---|---|
特征点检测 | 使用不同尺度的图片与高斯函数进行卷积 | 使用不同大小的核滤波器与原始图像进行卷积,易于并行 |
方向 | 关键点邻接矩阵内,利用梯度直方图进行计算 | 关键点邻接圆域内,计算x,y方向的haar小波 |
描述符生成 | 关键点邻域内划分d*d个子区域,每个子区域内计算8个方向的直方图 | 关键点邻域内划分d*d个子区域,每个子区域计算采样点的haar小波响应 |
同SIFT代码类似
前面介绍的几个特征检测器效果很好,特别是SIFT和SURF算法,但是从实时性的角度上来看,速度较慢。为了解决这个问题,2006年提出了FAST算法,并在2010年进行了修正。
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个符合阈值要求,否者就直接移除,对保留下来的点再继续进行测试(是否有12个点符合阈值要求)
(1)获取的候选点较多
(2)特征点的选取不是最优的,因为它的效果取决于要解决的问题和角点的分布情况
(3)进行非特征点判别时,大量的点被丢弃
(4)检测到的很多特征点都是相邻的(可用非极大值抑制解决)
前3个问题可以通过机器学习的方法来解决
(1)选择一组训练图片
(2)使用FAST算法找出每幅图片的特征点,对图像中的每个特征点,将其周围的16个像素存储构成一个向量p;
(3)每一个特征点的16个像素点都属于下列三类中的一种:
(4)根据像素点的分类,特征向量P被分为3个子集:Pd,Ps,Pb
(5)定义一个布尔变量Kp,如果p是角点就设置为True,如果不是就设置为False。
(6)利用特征向量p,目标值是 K p K _ p Kp,训练ID3树(决策树分类器)
(7)将构建好的决策树运用其他图像的快速的检测
在筛选出来的候选角点中,有很多是紧挨在一起的,需要通过非极大值抑制来消除这种影响。
为所有的候选角点都确定一个打分函数V,V的值可以这样计算:先分别计算Ip与圆上16个点的像素值差值,取绝对值,再将这16个绝对值相加,就得到V的值:
最后比较毗邻候选角点的V值,吧V值小的候选角点pass掉
FAST算法相比其他检测器更快,但是在噪声较高时不够稳定,需要设置合适的阈值。
OpenCV中关键点检测是通过传统算法实现的:
(1)实例化fast
fast=cv2.FastFeatureDetector_create(threshold,nonmaxSuppression)
参数:
threshold:阈值t,有默认值10
nonmaxSuppression:是否进行非极大值抑制,默认True
fast:创建的FastFeatureDetector对象
(2)检测关键点
kp=fast.detect(grayImg,None)
参数:
gray:进行关键点检测的图像,注意是灰度图像
返回:
kp:检测到的关键点信息,包括位置、尺度、方向信息
(3)将关键点检测结果绘制在图像上,与在sift中是一样的
cv2.drawKeypoints(image,keypoints,outputimage,color,flags)
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img2=cv2.imread('conner2.png')#
#2.FAST角点检测
# 2.1 实例化fast
fast=cv2.FastFeatureDetector_create(threshold=30)
# 2.2 关键点检测
kp=fast.detect(img2,None)
# 2.3 绘制图像
img2=cv2.drawKeypoints(img2,kp,None,color=(0,0,255))
cv2.imshow('origin',img2)
cv2.waitKey(0)
SIFT和SURF算法是受专利保护的,在使用他们时是需要付费的,但是ORB算法不需要,他可以用来对图像中的关键点快速创建特征向量,并利用这些特征向量来识别图像中的对象。
ORB算法结合了Fast和Brief算法,提出了构造金字塔,为Fast特征点添加了方向,从而使关键点具有了尺度不变和旋转不变性,具体流程如下:
(1)构造尺度金字塔,金字塔共有n层,与SIFT不同的是,每一层仅有一副图像。第s层的尺度为:
σ0是初始尺度,默认为诶1.2,原图在0层
第s层图像的大小:
(2)在不同尺度上利用Fast算法检测特征点,采用Harris角点响应函数,根据角点的响应值排序,选取前N个特征点,作为本尺度的特征点。
(3)计算特征点的主方向,计算以特征点为圆心半径为r的圆形邻域内的灰度质心位置,将从特征点位置到质心位置的方向做特征点的主方向。
(4)为了解决旋转不变性,将特征点的邻域旋转到主方向上,利用Brif算法构建特征描述符,自此就得到了ORB的特征描述向量。
(1)实例化ORB
orb=cv2.ORB_create(nfeatures)
参数:
nfeatures:特征点的最大数量
(2)利用检测关键点
kp,des=orb.detectAndCompute(gray,None)
参数:
gray:进行关键点检测,注意是灰度图像
返回:
kp:关键点信息,包括位置、尺度、方向信息
des:关键点描述符,每个关键点BRIEF特征向量,二进制字符串
(3)将关键点检测结果绘制在图像上
cv2.drawKeypoints(image,keypoints,outputimage,color,flags)
import cv2
import matplotlib.pyplot as plt
import numpy as np
#1. 读取图像,灰度形式
img2=cv2.imread('conner2.png')#
#2.ORB角点检测
# 2.1 实例化orb
orb=cv2.ORB_create(nfeatures=50000)
# 2.2 关键点检测
kp,des=orb.detectAndCompute(img2,None)
# 2.3 绘制图像
img2=cv2.drawKeypoints(img2,kp,None,flags=0)
cv2.imshow('origin',img2)
cv2.waitKey(0)