【OpenCV-Python】——哈里斯/Shi-Tomas角检测&FAST/SIFT/ORB特征点检测&暴力/FLANN匹配器&对象查找

目录

前言:

1、角检测

1.1 哈里斯角检测

 1.2 优化哈里斯角

 1.3 Shi-Tomasi角检测

2、特征点检测

2.1 FAST特征点检测

2.2 SIFT特征检测

2.3 ORB特征检测

3、特征匹配

扫描二维码关注公众号,回复: 15163654 查看本文章

3.1 暴力匹配器

3.2 FLANN匹配器

4、对象查找

总结:


前言:

图像的特征是指图像中具有独特性和易识别性的区域,如角和边缘等。提取特征并对其进行描述,便于图像匹配和搜索。

1、角检测

1.1 哈里斯角检测

cv2.conerHarris()函数根据哈里斯角检测器算法检测图像中的角:

dst=cv2.conerHarris(src,blocksize,ksize,k)

src:8位单通道或浮点值图像

blocksize:邻域大小,越大检测出的角占的区域越大

ksize:使用Sobel算子的中孔参数

k:自由参数。Ksize和k影响检测的敏感度,越小检测出的角越多,但准确率降低

dst:返回的numpy.ndarray对象,大小与src相同,每个数组元素对应一个像素点,值越大对应像素点是角的概率越高。即保存了角的坐标信息。

import cv2
import numpy as np
img=cv2.imread('lifangti.png')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
gray=np.float32(gray)    #转换为浮点类型
dst=cv2.cornerHarris(gray,8,7,0.01)  #执行角检测
#将检测结果中值大于“最大值0.02”对应的像素设置为红色
img[dst>0.02*dst.max()]=[0,0,255]
cv2.imshow('dst',img)   #显示检测结果
cv2.waitKey(0)
cv2.destroyAllWindows()

 1.2 优化哈里斯角

上面检测出的哈里斯角包含了一定数量的像素,有些情况下需要进一步处理找出角的更精确位置,利用cv2.cornerSubPix()函数实现:

dst=cv2.cornerSubPix(src,corners,winsize,zeroZone,criteria)
src:8位单通道或浮点值图像
Corners:哈里斯角的质心坐标
Winsize:搜索窗口边长的一半
zeroZone:零值边长的一半
Criteria:优化查找的终止条件
Dst:返回的numpy.ndarray对象,存储优化后的角信息
import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('lifangti.png')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
gray=np.float32(gray)    #转换为浮点类型
dst=cv2.cornerHarris(gray,8,7,0.01)  #执行角检测
r,dist=cv2.threshold(dst,0.01*dst.max(),255,0)  #二值化阈值处理
dst=np.uint8(dst)  #转换为整型
r,l,s,cxys=cv2.connectedComponentsWithStats(dst)  #查找质点坐标
cif=(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,100,0.01)  #定义优化查找条件
corners=cv2.cornerSubPix(gray,np.float32(cxys),(5,5),(-1,-1),cif)  #执行优化查找
res=np.hstack((cxys,corners))   #堆叠构造新数组,便于标注角
res=np.int0(res)  #转换为整型
img[res[:,1],res[:,0]]=[0,0,255]  #将哈里斯角对应像素设置为红色
img[res[:,3],res[:,2]]=[255,0,0]  #将优化结果像素设置为蓝色
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)   #转换为RGB格式
plt.imshow(img)
plt.axis('off')
plt.show()

 1.3 Shi-Tomasi角检测

 Shi-Tomasi角检测改进的角检测方法,cv2.goodFeaturesToTrack()函数使用Shi-Tomasi角检测器查找图像中N个最强角:

dst=cv2.goodFeaturesToTrack(src,maxCorner,qualityLevel,minDistance)
dst是返回的结果,保存了检测到的角在原图像中的坐标
src是8位单通道或浮点值图像
maxCorner:返回的角的最大数量
qualityLevel:可接受角的最低质量
minDistance:返回的角之间的最小欧几里得距离
import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('six.png')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
gray=np.float32(gray)    #转换为浮点类型
corners=cv2.goodFeaturesToTrack(gray,7,0.1,100) #检测角,最多7个
corners=np.int0(corners)  #转换为整型
for i in corners:
    x,y=i.ravel()
    cv2.circle(img,(x,y),4,(255,0,0),-1) #用蓝色圆点标出找到的点
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)   #转换为RGB格式
plt.imshow(img)
plt.axis('off')
plt.show()


2、特征点检测

特征点即图像中具有唯一性的像素,也称兴趣点或关键点,角是特殊的特征点。

2.1 FAST特征点检测

该检测器主要根据像素周围16个像素的强度和阈值等参数来判断像素是否为关键点。

首先用cv2.FastFeatureDetector_create()函数创建一个FAST对象,然后调用FAST对象的detect()方法执行关键点检测。结果返回一个关键点阵表,每个关键点对象均包含了关键点的角度、坐标、响应强度和领域大小等信息。响应强度越大约有可能属于角:

import cv2
img=cv2.imread('lifangti.png')   #打开图像,默认时BGR格式
fast=cv2.FastFeatureDetector_create() #创建FAST检测器
kp=fast.detect(img,None)#检测关键点,不使用掩模
img2=cv2.drawKeypoints(img,kp,None,color=(0,0,255))#绘制关键点
cv2.imshow('FAST points',img2)#显示绘制了关键点的图像
fast.setThreshold(20)#设置阈值,默认阈值10
kp=fast.detect(img, None)#检测关键点,不使用掩模
n=0
for p in kp:#输出关键点信息
    print("第%s个关键点,坐标:"%(n+1),p.pt,'响应强度:',p.response,'领域大小:',p.size,'角度:',p.angle)
    n+=1
img3=cv2.drawKeypoints(img,kp,None,color=(0,0,255))
cv2.imshow('Treshold20',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

2.2 SIFT特征检测

图像中的角具有旋转不变的特性,但在放大或缩小图象时角可能发生变化。SIFT是指尺度不变特征变换,用于查找图像尺度不变的特征,返回图像中的关键点。

首先用cv2.SIFT_create()函数创建一个SIFT对象,然后调用SIFT对象的detect()方法执行关键点检测:

import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('six.png')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
sift=cv2.SIFT_create() #创建SIFT检测器,已申请专利要购买才可运行
kp=sift.detect(gray,None)   #检测关键点
img2=cv2.drawKeypoints(img,kp,None,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) #绘制关键点
img2=cv2.cvtColor(img2,cv2.COLOR_BGR2RGB)   #转换为RGB格式
plt.imshow(img2)
plt.axis('off')
plt.show()

2.3 ORB特征检测

以FAST特征检测器和BRIEF描述符为基础进行了改进,检测性能更好。过程同理。


3、特征匹配

通过上面的特征检测得到关键点的描述符,可用于图像的特征匹配。在计算图1中是否包含图2时,图1为训练图像,图2为查询图像,图1的关键点描述符为训练描述符,图2的关键点描述符为查询描述符。

3.1 暴力匹配器

使用描述符进行特征比较。首先在查询描述符中取一个关键点的描述符,与训练描述符中的所有关键点描述符进行比较,每次比较后会给出一个距离值,距离最小的值对应最佳匹配结果,结果返回匹配结果列表。

使用过程也是先使用cv2.BFMatcher_create()函数创建匹配器,然后调用对象的match方法返回每个关键点的最佳匹配结果。(knnMatch方法可返回指定数量的最佳匹配结果)

①cv2.BFMatcher_create()函数

bf=cv2.BFMatcher_create(normType,crossCheck)
bf:返回的暴力匹配器对象。两个参数都是可选的:
normType:距离测量类型,默认是cv2.NORM_L2。通常SIFT、SURF等描述符使用cv2.NORM_L1或cv2.NORM_L2;ORB、BRISK或BRIEF等描述符使用cv2.NORM_HAMMING。
crossCheck:默认False,匹配器为每个查询描述符找到k个距离最近的匹配描述符。为True时,只返回满足交叉验证条件的匹配结果。

②match()方法

ms=bf.match(des1,des2)
ms是返回的匹配结果,是一个DMatch对象列表。每个DMatch对象表示关键点的一个匹配结果,其distance属性表示距离,值越小匹配度越高。
des1是查询描述符
des2是训练描述符

获得结果后可用cv2.drawMatches()或cv2.drawMatchesKnn()函数绘制匹配结果图像:

outImg=cv2.drawMatches(img1,keypoints1,img2,keypoints2,matches1to2,outImg,matchcolor,singlePointsColor,matchesMask,flags)
outImg=cv2.drawMatchesKnn(img1,keypoints1,img2,keypoints2,matches1to2,outImg,matchcolor,singlePointsColor,matchesMask,flags)
outImg是返回的绘制结果图像,图像中查询图像与训练图像中匹配的关键点和两点之间连线为彩色。
img1是查询图像;img2是训练图像。
keypoints是关键点。
matches 1to2是img1和img2的匹配结果。
后四个是可选参数,matchColor是关键点和连接线的颜色,默认随机;singlePointColor是单个关键点的颜色,默认随机;matchesMask是掩模,用于决定绘制哪些匹配结果,默认为空,表示绘制所有匹配结果;flags是标志,如cv2.DrawMatchFlags_DEFAULT等。

示例代码:

import cv2
import numpy as np
import matplotlib.pyplot as plt
img1=cv2.imread('cat.png',cv2.IMREAD_GRAYSCALE)
img2=cv2.imread('catface.png',cv2.IMREAD_GRAYSCALE)
orb=cv2.ORB_create()   #创建ORB检测器
kp1,des1=orb.detectAndCompute(img1,None)  #检测关键点和计算描述符
kp2,des2=orb.detectAndCompute(img2,None)  #检测关键点和计算描述符
bf=cv2.BFMatcher_create(cv2.NORM_HAMMING,crossCheck=True)  #创建匹配器,True
ms=bf.match(des1,des2)    #执行特征匹配
ms=sorted(ms,key=lambda x:x.distance)  #按距离排序
img3=cv2.drawMatches(img1,kp1,img2,kp2,ms[:20],None,flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)  #绘制前20个匹配结果
plt.imshow(img3)
plt.axis('off')
plt.show()

暴力匹配器对象的knnMatch方法可返回指定数量的最佳匹配结果:

ms=bf.knnMatch(des1,des2,k=n)
ms是返回的匹配结果列表,每个列表元素是一个子列表,包含了由参数k指定个数的DMatch对象;
des1查询描述符;des2训练描述符;k是返回的最佳匹配个数
import cv2
import numpy as np
import matplotlib.pyplot as plt
img1=cv2.imread('cat.png',cv2.IMREAD_GRAYSCALE)
img2=cv2.imread('catface.png',cv2.IMREAD_GRAYSCALE)
orb=cv2.ORB_create()   #创建ORB检测器
kp1,des1=orb.detectAndCompute(img1,None)  #检测关键点和计算描述符
kp2,des2=orb.detectAndCompute(img2,None)  #检测关键点和计算描述符
bf=cv2.BFMatcher_create(cv2.NORM_HAMMING,crossCheck=False)  #创建匹配器,Flase
ms=bf.knnMatch(des1,des2,k=2)    #执行特征匹配
#应用比例测试选择要使用的匹配结果
good=[]
for m,n in ms:
    if m.distance<0.75*n.distance:    #因为k=2,所以这里要比较两个匹配结果的距离
        good.append(m)
img3=cv2.drawMatches(img1,kp1,img2,kp2,good[:20],None,flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)  #绘制前20个匹配结果
plt.imshow(img3)
plt.axis('off')
plt.show()

3.2 FLANN匹配器

FLANN是近似最近邻的快速库,该算法比其他最近邻算法更快。创建该匹配器时需传递两个字典参数:index_params、search_params。

①index_params用于指定索引树的算法类型和数量。SIFT和SURF与ORB特征算法的指定代码不同。SIFT和SURF用下面代码:

FLANN_INDEX_KDTREE=1
index_params=dict(algorithm=FLANN_INDEX_KDTREE,tree=5)

ORB算法可用下面代码:

FLANN_INDEX_LSH=6
index_params=dict(algorithm=FLANN_INDEX_LSH,
                  table_number=6,
                  key_size=12,
                  multi_probe_level=1)

②search_params用于指定索引树的遍历次数,遍历次数越多匹配越精确,通常50即可:

search_params=dict(checks=50)

示例代码:

import cv2
import numpy as np
import matplotlib.pyplot as plt
img1=cv2.imread('cat.png',cv2.IMREAD_GRAYSCALE)
img2=cv2.imread('catface.png',cv2.IMREAD_GRAYSCALE)
orb=cv2.ORB_create()   #创建ORB检测器
kp1,des1=orb.detectAndCompute(img1,None)  #检测关键点和计算描述符
kp2,des2=orb.detectAndCompute(img2,None)  #检测关键点和计算描述符
#定义FLANN参数
FLANN_INDEX_LSH=6
index_params=dict(algorithm=FLANN_INDEX_LSH,
                  table_number=6,
                  key_size=12,
                  multi_probe_level=1)
search_params=dict(checks=50)
flann=cv2.FlannBasedMatcher(index_params,search_params)  #创建FLANN匹配器
matches=flann.match(des1,des2)  #执行匹配操作
draw_params=dict(matchcolor=(0,255,0),#设置关键点和连接线为绿色
                 singlePointColor=(255,0,0),#设置单个点为红色
                 matchesMask=None,
                 flags=cv2.DrawMatchesFlags_DEFAULT)
img3=cv2.drawMatches(img1,kp1,img2,kp2,matches[:20],None,flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3)
plt.axis('off')
plt.show()


4、对象查找

经过上面的操作得到最佳匹配结果后,可调用cv2.findHomography()函数执行查询图像和训练图像的透视关系,再调用cv2.perspectiveTransform()函数执行向量的透视矩阵转换,即可得查询图像再训练图像中的位置。

①cv2.findHomography()函数格式:

retv,mask=cv2.findHomography(srcPoints,dstPoints,method,ransacReproThreshold)

retv为返回的转换矩阵;mask是返回的查询图像在训练图像中的最佳匹配结果掩膜。srcPoints和dstPoints分别是查询和训练图像匹配结果的坐标。后两个是可选参数:method是用于计算透视转换矩阵的方法;ransacReproThreshold是可允许的最大重投影误差。

②cv2.perspectiveTransform()函数格式:

dst=cv2.perspectiveTransform(src,m)

dst是输出结果数组,大小和类型与src相同。src是输入的2通道或3通道浮点类型的数组;m是大小为3x3或4x4的浮点类型的转换矩阵,例如使用cv2.findHomography()函数返回的转换矩阵。

示例代码:

import cv2
import numpy as np
import matplotlib.pyplot as plt
img1=cv2.imread('cat.png',cv2.IMREAD_GRAYSCALE)
img2=cv2.imread('catface.png',cv2.IMREAD_GRAYSCALE)
orb=cv2.ORB_create()   #创建ORB检测器
kp1,des1=orb.detectAndCompute(img1,None)  #检测关键点和计算描述符
kp2,des2=orb.detectAndCompute(img2,None)  #检测关键点和计算描述符
bf=cv2.BFMatcher_create(cv2.NORM_HAMMING,crossCheck=True)  #创建匹配器,True
ms=bf.match(des1,des2)    #执行特征匹配
ms=sorted(ms,key=lambda x:x.distance)  #按距离排序
matchesMask=None
if len(ms) > 10:  #在有足够数量的匹配结果后,才计算查询图像在训练图像中的位置
    querypts=np.float32([kp1[m.queryIdx].pt for m in ms]).reshape(-1,1,2)#计算查询图像匹配结果的坐标
    trainpts=np.float32([kp2[m.trainIdx].pt for m in ms]).reshape(-1, 1, 2)  # 计算训练图像匹配结果的坐标
    retv,mask=cv2.findHomography(querypts,trainpts,cv2.RANSAC)  #执行查询图像与训练图像的透视转换
    matchesMask=mask.ravel().tolist()  #计算最佳匹配结果的掩模,用于绘制匹配结果
    h,w=img1.shape
    pts=np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1, 1, 2)
    dst=cv2.perspectiveTransform(pts,retv)  #执行向量的透视矩阵转换,获得查询图像在训练图像中的位置
    img2=cv2.polylines(img2,[np.int32(dst)],True,(255,255,255),5)  #用白色矩形在训练图像中绘制出查询图像的范围
img3=cv2.drawMatches(img1,kp1,img2,kp2,ms,None,
                     matchColor=(0,255,0),  #用绿色画出匹配结果
                     singlePointColor=None,
                     matchesMask=matchesMask,  #绘制掩模内的匹配结果
                     flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3)
plt.axis('off')
plt.show()

只有在找到足够多的匹配结果后,才能确定查询图像在训练图像中的位置,此处if设置为10.满足条件后根据特征匹配结果执行透视变换,获得查询图像在训练图像中的位置,再用绘图函数绘制出位置。为满足条件时,此例只绘制特征匹配结果,不会绘制位置。


总结:

由于是初学者可能很多地方没有总结完全或者有误,后续深入学习后会不断回来该删,也欢迎各位朋友指正!下次学习人脸检测和识别

猜你喜欢

转载自blog.csdn.net/weixin_51658186/article/details/130420949