目录
- 概要
- 一、角点检测
-
- [1. Harris角点检测](#1. Harris角点检测)
- [2. Shi-Tomas算法](#2. Shi-Tomas算法)
- [3. SIFT算法](#3. SIFT算法)
- [4. FAST算法](#4. FAST算法)
概要
本博客梳理了几种常见的**角点检测**、** 直线检测**、** 椭圆检测**、** 矩形检测**算法,本博客只关注代码,不关注每种算法的原理。
一、角点检测
常见的角点检测方法有Harris角点检测 、Shi-Tomas算法角点检测 、sift算法角点检测 、fast角点检测 、ORM算法角点检测 。
1. Harris角点检测
python
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正确显示中文
# Harris角点检测
'''
Harris角点检测的思想是通过图像的局部小窗口观察图像,角点的特征是窗口沿任意方向移动都会导致
图像灰度的明显变化
将上述思想转化为数学表达式,即将局部窗口向各个方向移动(u,v)并计算所有灰度差异的总和,表达式
如下:
E(u,v)=∑w(x,y)[I(x+u,y+v)-I(x,y)]²
其中I(x,y)是局部窗口的灰度图,I(x+u,y+v)是平移后的灰度图像,w(x,y)是窗口函数,其可以是矩形
窗口,也可以是对每一个像素赋予不同权重的高斯窗口
角点检测中使E(u,v)的值最大.利用一阶泰勒展开有:
I(x+u,y+v)=I(x,y)+Ix*u+Iy*v
其中:Ix和Iy是沿x和y方向的导数,可用sobel算子计算.
E(u,v)=∑w(x,y)[I(x+u,y+v)-I(x,y)]²
=∑w(x,y)[Ix²u²+2Ix*Iy*u*v+Iy²*v²]
|Ix² Ix*Iy|
=∑w(x,y)[u,v]|Ix*Iy Iy²|
=[u v]M|u|
|v|
M矩阵决定了E(u,v)的取值,M是Ix和Iy的二次函数,可以表示成椭圆的形状,椭圆的长短半轴由M的特征
值a1和a2决定,方向由特征矢量决定
共可分为三种情况:
.图像中的直线.一个特征值大,另一个特征值小,a1>>a2或者a2>>a1.椭圆函数值在某一个方向上大,
在其他方向小
.图像中的平面.两个特征值都小,且近似相等,椭圆函数值再各个方向都有
.图像中的角点.两个特征值都大,且近似相等,椭圆函数在所有方向都增大.
Harris给出的角点计算方法并不需要计算具体的特征值,而是计算一个角点响应值R来判断角点.R的计算公式
为:
R=detM-α(traceM)²
式中,detM为矩阵M的行列式,traceM为矩阵M的迹,α为常数取值范围为0.04~0.06.事实上,特征是隐含
在detM和traceM中,因为:
detM=a1a2
traceM=a1+a2
那么认为:
.当R为大数时的正数是角点
.当R为大数时的负数为边界
.当R为小数认为是平坦区域
API:
cv.cornerHarris(src,blockSize,ksize,k)
参数:
img:数据类型为float32的输入图像
blockSize:角点检测中要考虑的领域大小
ksize:sobel算子求导使用的核大小
k:角点检测中的自由参数取值范围为[0.04,0.06]
'''
# 读取图片
img = cv.imread('image/house.jpg')
# 转换成灰度图
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 数据类型转换成float32
gray_float32 = np.float32(gray)
# 角点检测
dst = cv.cornerHarris(gray_float32, 2, 3, 0.04)
#设置阈值,将角点绘制出来,阈值根据图像进行选择
R=dst.max() * 0.01
#这里将阈值设为dst.max()*0.01 只有大于这个值的数才认为数角点
img[dst > R] = [0, 0, 255]
plt.subplot(2,2,1)
plt.imshow(cv.imread('image/house.jpg')[:,:,::-1])
plt.title('原图')
plt.subplot(2,2,2)
plt.imshow(gray,cmap=plt.cm.gray)
plt.title('灰度图(uint8型)')
plt.subplot(2,2,3)
plt.imshow(gray_float32,cmap=plt.cm.gray)
plt.title('灰度图(float32型)')
plt.subplot(2,2,4)
plt.imshow(img[:,:,::-1])
plt.title('检测结果')
plt.savefig('image/角点检测(Harris).jpg')
plt.show()
2. Shi-Tomas算法
python
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正确显示中文
#shi-Tomas算法
'''
shi-Tomas算法是对Harris角点检测算法的改进,一般会比Harris算法得到更好的角点.
Harris算法的角点响应函数是将矩阵M的行列式值与M的迹相减,利用差值判断是否为角点,
后来shi和Tomas提出改进的方法是,若矩阵M的两个特征值中较小的一个大于阈值,则认为
他是角点,即:
R=min(a1,a2)
API:
corners=cv.goodFeaturesToTrack(image,maxcorners,qualityLevel,minDistance)
参数:
image:输入的灰度图像
maxCorners:获取角点数的数目
qualityLevel:该参数指出最低可接受的角点质量水平,在0~1之间
minDistance:角点之间的最小欧氏距离,避免得到相邻特征点
返回:
corners:搜索到的角点,在这里所有低于质量水平的角点被排除,然后把合格的角点按照质量排序,
然后将质量较好的角点附近(小于最小欧氏距离)的角点删除,最后找到maxCorners个角点返回
'''
#读取图片
img=cv.imread('image/house.jpg')
#转成灰度图
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#shi-Tomas角点检测
corners=cv.goodFeaturesToTrack(gray,1000,0.05,10)
#绘制角点
for corner in corners:
x,y=corner.ravel()
cv.circle(img,(int(x),int(y)),2,(0,0,255),-1)
plt.subplot(2,1,1)
plt.imshow(cv.imread('image/house.jpg')[:,:,::-1])
plt.title('原图')
plt.subplot(2,1,2)
plt.imshow(img[:,:,::-1])
plt.title('检测结果')
plt.savefig('image/角点检测(shi-Tomas).jpg')
plt.show()
3. SIFT算法
python
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正确显示中文
# Sift算法
'''
Harris和shi-Tomas角点检测算法,这两种算法具有旋转不变性,但不具有尺度不变性,在原本能检测
到角点的位置,当图像放大后,就检测不到角点了.
尺度不变特征转换即SIFT(Scale-invariant feature transform).它用来侦测与描述影响中
局部性特征,它在空间尺度中寻找极值点,并提取出其位置,尺度,旋转不变量,此算法由David Lowe
在1999年所发表,2004年完善总结.应用范围包含物体识别,机器人地图感知与导航,影像缝合,3D模型
建立,手势识别,影像追踪和动作对比等领域.
SIFT算法实质是在不同的尺度空间上寻找关键点(特征点),并计算出关键点的方向.SIFT所查找到的关键点
是一些十分突出,不会因为光照,仿射变换和噪音等因素影响而变化的点,如角点,边缘点,暗区的亮点及亮区
的暗点.
'''
# 实现SIFT检测关键点的步骤
# 1.实例化sift
# sift = cv.SIFT_create()
# 2.利用sift.detectAndCompute()检测关键点并计算
'''
kp,des=sift.detectAndCompute(gray,None)
参数:
gray:进行关键点检测的图像(灰度图)
返回:
kp:关键点信息,包括位置,尺度,方向信息
des:关键点描述,每个关键点对应128个梯度信息的特征向量
'''
#3.将关键点检测结果绘制在图像上
'''
cv.drawKeyPoints(image,keypoints,outputimage,color,flags)
参数:
image:原始图像
keypoints:关键点信息
outputimage:输出图片,可以是原始图
color:颜色设置,通过(b,g,r)修改颜色
flags:绘图功能的标识设置
1.cv.DRAW_MATCHES_FLAGS_DEFAULT:创建输出图像矩阵,使用现存的输出图像绘制匹配
对和特征点,对每一个特征点只绘制中间点
2.cv.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTING:不创建输出图像矩阵,而是在输出图像
上绘制匹配对
3.cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS:对每一个特征点绘制带大小和方向
的关键点图形
4.cv.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS:单点的特征点不被绘制
'''
#读取图像
img=cv.imread('image/house.jpg')
#转换成灰度图像
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#实例化sift
sift=cv.SIFT_create()
#检测关键点并计算
kp,des=sift.detectAndCompute(gray,None)
#绘制关键点
# cv.drawKeypoints(img,kp,img,(0,255,0),cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv.drawKeypoints(img,kp,img,(0,255,0),cv.DRAW_MATCHES_FLAGS_DEFAULT)
plt.subplot(2,1,1)
plt.imshow(cv.imread('image/house.jpg')[:,:,::-1])
plt.title('原图')
plt.subplot(2,1,2)
plt.imshow(img[:,:,::-1])
plt.title('检测结果')
plt.savefig('image/角点检测(sift).jpg')
plt.show()
4. FAST算法
python
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正确显示中文
# fast算法
'''
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个要符合阈值要求,否则直接剔除,
对保留下来的点再继续进行测试
虽然这个检测器的效率很高,但它有一下几个缺点:
.获得的候选点比较多
.特征点的选取不是最优的,因为它的效果取决于与要解决的问题的角点的分布情况
.进行非特征点判别时大量的点被丢弃
.检测到的很多特征点都是相邻的
前3个问题可以通过机器学习的方法解决,最后一个问题可以使用非最大值抑制的方法解决
'''
# 机器学习的角点检测
'''
1.选择一组训练图片(最好是跟最后应用相关的图片)
2.使用Fast算法找出每幅图像的特征点,对图像中每一个特征点,将其周围的16个像素存储构成
一个向量p
3.每一个特征点的16个像素点都属于下列三类中的一种:
| d Ip->x≤Ip-t (darker)
Sp->x=| s Ip-t≤Ip->x≤Ip+t (similar)
| b Ip->x≥Ip+t (brighter)
4.根据这些像素点的分类,特征向量P也被分为3个子集:Pd,Ps,Pb
5.定义一个新的布尔变量Kp,如果p是角点就设置为True,否则为False
6.利用特征向量p,目标值Kp,训练ID3树
7.将构建好的决策树运用于其他图像的快速检测
'''
# 非极大值抑制
'''
在筛选出来的候选点中有很多是紧挨在一起的,需要通过非极大值抑制来消除这种影响.
为所有的候选点后确定一个打分函数V,V的值可以这样计算:先分别计算Ip与圆上16个
点的像素值差值,取绝对值,再将这16个绝对值相加,就得到了V值
V=∑|Ip-Ii| (i=1,2...16)
最后比较毗邻候选点的V值,把V值较小的候选点pass掉
Fast算法的思想与我们对角点的直观认识非常接近,化繁为简.Fast算法比其他角点的检测
算法快,但是在噪声比较高时不够稳定,这需要设置合适的阈值.
'''
#Fast算法的实现
'''
1.实例化fast
fast=cv.FastFeatureDetector_create(threshold,nonmaxSuppression)
参数:
threshold:阈值t,默认值为10(特征点与周围像素值差值的阈值)
nonmaxSuppression:是否进行非极大值抑制,默认为True
返回:
fast:创建的FastFeatureDetector对象
2.利用fast.detect检测关键点
kp=fast.detect(grayImg,None)
参数:
grayImg:输入的灰度图片(彩色图像也可以)
返回:
kp:关键点信息,包括位置,尺度,方向信息
3.将关键点监测结果绘制在图像上,与sift中的一样
cv.drawKeypoints(image,keypoints,outputimage,color,flags)
'''
img=cv.imread('image/house.jpg')
img2=cv.imread('image/house.jpg')
#阈值设为20 进行非极大值抑制
fast=cv.FastFeatureDetector_create(threshold=30,nonmaxSuppression=True)
fast_not_nonmaxSuppression=cv.FastFeatureDetector_create(threshold=30,nonmaxSuppression=False)
#检测出关键点
kp=fast.detect(img,None)
kp_not_nonmaxSuppression=fast_not_nonmaxSuppression.detect(img2,None)
#将关键点绘制在图像上
cv.drawKeypoints(img,kp,img,(0,255,0),cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv.drawKeypoints(img2,kp_not_nonmaxSuppression,img2,(0,255,0),cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
plt.subplot(2,1,1)
plt.imshow(img2[:,:,::-1])
plt.title('未进行非极大值抑制')
plt.subplot(2,1,2)
plt.imshow(img[:,:,::-1])
plt.title('进行了非极大值抑制')
plt.savefig('image/fast算法.jpg')
plt.show()