OpenCV特征检测,角检测,特征点检测,特征匹配,对象查找

特征检测

图像的特征是指图像中具有独特性和易于识别性的区域,角、边缘等都属于有意义的特征。

8.1角检测

角是两条边的交点,也可称为角点或拐角,它是图像中各个方向上强度变化最大的区域。
OpenCV的cv2.cornerHarris()、cv2.cornerSubPix()和cv2.goodFeaturesToTrack()函数用于角检测。

8.1.1哈里斯角检测

cv2.cornerHarris()函数根据哈里斯角检测器算法检测图像中的角,其基本格式如下。

dst=cv2.cornerHarris(src,blockSize,ksize,k)

参数说明如下。

  • dst是返回结果,它是一个numpy.ndarray对象,大小和src相同,每一个数组元素对应着一个像素点,其值越大,对应像素点是角的概率越高。
  • src为8位单通道或浮点值图像。
  • blockSize为领域大小,值越大,检测出的角占的区域越大。
  • ksize为哈里斯角检测器使用的Sobel算子的中孔参数。
  • k为哈里斯角检测器的自由参数。ksize和k影响检测的敏感度,值越小,检测出的角越多,但检测准确率越低。
    实例代码如下。
#哈里斯角检测
import cv2
import numpy as np
img=cv2.imread('cube.jpg')#打开输入图像
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)

原图像
在这里插入图片描述
结果图像如下。红点位置为检测出的角的位置。
在这里插入图片描述

8.1.2优化哈里斯角

使用cv2.cornerHarris()函数检测出的角称为哈里斯角,它包含了一定数量的像素。有时,可能需要对哈里斯角进行进一步处理,找出角的更精确位置。
cv2.cornerSubPix()函数用于哈里斯角进行优化,找出更精确的角的位置,其基本格式如下。

dst=cv2.cornerSubPix(src,corners,winSize,zeroZone,criteria)

参数说明如下。

  • dst为返回结果,存储优化后的角信息。
  • src为8位单通道或浮点值图像。
  • corners为哈里斯角的质心坐标。
  • winSize为搜索窗口边长的一半。
  • zeroZone为零值边长的一半。
  • criteria为优化查找的终止条件。

实例代码如下。

#优化哈里斯角
import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('cube.jpg')#打开图像,默认为BGR格式
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#转换为灰度图像
gray=np.float32(gray)#转换为浮点类型
dst=cv2.cornerHarris(gray,8,7,0.04)#查找哈里斯角
r,dst=cv2.threshold(dst,0.01*dst.max(),255,0)#二值化阈值处理
dst=np.uint8(dst)#转换为整型
r,l,s,cxys=cv2.connectedComponentsWithStats(dst)#查找质点坐标
cif=(cv2.TermCriteria_EPS+cv2.TermCriteria_MAX_ITER,100,0.001)#定义优化查找条件
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]]=[0,255,0]#将优化结果像素设置为绿色
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)#转换为RGB格式
plt.imshow(img)
plt.axis('off')
plt.show()#显示检测结果

下图是使用工具中的缩放功能放大图像后立方体的一个角,可看出红色和绿色角位置。
在这里插入图片描述

8.1.3 Shi-Tomasi角检测

Shi-Tomasi角检测是在哈里斯角检测基础上提出的改进角检测的方法。
OpenCV的cv.goodFeaturesToTrack()函数使用Shi-Tomasi角检测器查找图像的N个最强角,其基本格式如下。

dst=cv2.goodFeaturesToTrack(src,maxCorners,qualityLevel,minDistance)

参数说明如下。

  • dst为返回结果,保存了检测到的角在原图像中的坐标。
  • src为8位单通道或浮点值图像。
  • maxCorners为返回的角的最大数量。
  • qualityLevel为可接受的角的最低质量。
  • minDistance为返回的角之间的最小欧几里得距离。
    实例代码如下。
#Shi-Tomasi角检测
import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('five.jpg')#打开图像,默认为BGR格式
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#转换为灰度图像
gray=np.float32(gray)#转换为浮点类型
corners=cv2.goodFeaturesToTrack(gray,6,0.1,100)#检测角,最多6个
corners=np.int0(corners)#转换为整型
for i in corners:
    x,y=i.ravel()
    cv2.circle(img,(x,y),4,(0,0,255),-1)#用红色圆点标注找到的角
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)#转换为RGB格式
plt.imshow(img)
plt.axis('off')
plt.show()#显示检测结果

结果图像如图所示。
在这里插入图片描述

8.2特征点检测

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

8.2.1 FAST特征检测

FAST特征检测器主要根据像素周围16个像素的强度和阈值等参数来判断像素是否为关键点。
可调用cv2.FastFeatureDetector_create()函数创建一个FAST对象 ,然后调用FAST对象的detect()方法执行关键点检测,该方法将返回一个关键点列表。每个关键点对象包含了关键点的强度、坐标、响应强度和邻域大小等信息。
示例代码如下。

#FAST关键点检测
import cv2
img=cv2.imread('cube.jpg')#打开图像,默认为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)#设置阈值,默认阈值为1
0
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('Threshold20',img3)#显示绘制了关键点的图像
cv2.waitKey(0)

程序运行结果如图所示,左图显示FAST对象使用阈值(10)时检测到的关键点,右图是阈值设置为20时检测的关键点。
阈值越大,返回的关键点越少。
在这里插入图片描述
程序输出了阈值为20后的关键点信息,如下所示。
在这里插入图片描述
FAST算法返回的关键点的响应强度值代表了该点属于角的频率,响应强度越大,该点越有可能属于角。

8.2.2 SIFT特征检测

图像中的角具有旋转不变特征,即旋转图像时角不会发生变化;但在放大或者缩小图像时,角可能发生变化。
SIFT是指尺度不变特征变换,SIFT算法用于查找图像中的尺度不变特征,返回图像中的关键点。
OpenCV提供的cv2.SIFT_create()函数用于创建SIFT对象,然后调用SIFT对象的detect()方法执行SIFT算法检测关键点。
示例代码如下。

#SIFT关键点检测
import cv2
import matplotlib.pyplot as plt
img=cv2.imread('five.jpg')#打开图像,默认为BGR格式
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()#显示绘制了关键点的图像

程序运行结果如下。
在这里插入图片描述

在绘制关键点时,将cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS设置为drawKeypoints()函数的flags参数时,可以根据响应强度值在关键点位置绘制大小不同的圆,并可同时标注方向。

8.2.3 ORB特征检测

ORB特征检测以FAST特征检测器和BRIEF描述符为基础进行了改进,以获得更好的特征检测性能。OpenCV提供的cv2.ORB_create()函数用于创建ORB对象,然后调用ORB对象的detect()方法进行ORB算法检测关键点。
实例代码如下。

#ORB关键点检测
import cv2
img=cv2.imread('cube.jpg')
orb=cv2.ORB_create()#创建ORB检测器
kp=orb.detect(img,None)#检测关键点
img2=cv2.drawKeypoints(img,kp,None,color=(0,0,255))#绘制关键点
cv2.imshow('ORB',img2)
cv2.waitKey(0)

程序运行结果如下图。
在这里插入图片描述

8.3 特征匹配

获得图像的关键点后,可通过计算得到关键点的描述符。关键点描述符可用于图像的特征匹配。通常,在计算图A是否包含图B的特征区域时,将图A称为训练图像,将图B称为查询图像。图A的关键点描述符称为训练描述符,图B的关键点描述符称为查询描述符。

8.3.1 暴力匹配器

暴力匹配器使用描述符进行特征比较。在比较时,暴力匹配器首先在查询描述符中取一个关键点的描述符,将其与训练描述符中的所有关键点描述符进行比较,每次比较会给出一个距离值,距离最小的值对应最佳匹配结果。所有描述符比较完后,匹配返回结果列表。
OpenCV的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个距离最近的匹配描述符。crossCheck为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[,singlePointColor[,matchesMask[,flags]]]])
outImg=cv2.drawMatchesKnn(img1,keypoints1,img2,keypoints2,matches1to2,outImg[,matchColor[,singlePointColor[,matchesMask[,flags]]]])

参数说明如下。

  • outImg为返回的绘制结果图像,图像中查询图像与训练图像中匹配的关键点和两点之间的连线为彩线。
  • img1为查询图像。
  • keypoints1为img1的关键点。
  • img2为训练图像。
  • keypoints2为img2的关键点。
  • matches1to2为img1与img2的匹配结果。
  • matchColor为关键点和连接线的颜色,默认使用随机颜色。
  • singlePointColor为单个关键点的颜色,默认使用随机颜色。
  • matchesMask为掩模,用于决定绘制哪些匹配结果,默认为空,表示绘制所有匹配结果。
  • flags为标志,可设置为下列参数值。
    cv2.DrawMatchesFlags_DEFAULT:默认方式,绘制两个图像源、匹配项和单个关键点,没有围绕关键点的圆以及关键点的大小和方向。
    cv2.DrawMatchesFlags_DRAW_OVER_OUTIMG:根据输出图像的现有内容进行绘制。
    cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS:不会绘制单个关键点。
    cv2.DrawMatchesFlags_DRAW_RICH_KEYPOINTS:在关键点周围绘制具有关键点大小和方向的圆圈。
    示例代码如下。
#暴力匹配器、ORB描述符和match()方法匹配
import cv2
import matplotlib.pyplot as plt
img1=cv2.imread('shu1.png',cv2.IMREAD_GRAYSCALE)#打开灰度图像
img2=cv2.imread('shu3.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)#创建匹配器
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.DRAW_MATCHES_FLAGS_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为返回的最佳匹配个数。
    示例代码如下。
#暴力匹配器、ORB描述符和knnMatch()方法匹配
import cv2
import matplotlib.pyplot as plt
img1=cv2.imread('shu1.png',cv2.IMREAD_GRAYSCALE)#打开灰度图像
img2=cv2.imread('shu3.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)#创建匹配器
ms=bf.knnMatch(des1,des2,k=2)#应用比例测试选择要使用的匹配结果
good=[]
for m,n in ms:
    if m.distance < 0.75*n.distance:
        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()

结果如下图。
在这里插入图片描述

8.3.2 FLANN匹配器

FLANN为近似最近邻的快速库,FLANN特征匹配算法比其他的最近邻算法更快。
在创建FLANN匹配器时,需要传递两个字典参数:index_params和search_params。
index_params用于指定索引树的算法类型和数量。SIFT和SURF可使用下面代码来设置。

FLANN_INDEX_KDTREE=1
index_params=dict(algorithm=FLANN_INDEX_KDTREE,trees=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)

实例代码如下。

#FLANN匹配
import cv2
import matplotlib.pyplot as plt
img1=cv2.imread('shu1.png',cv2.IMREAD_GRAYSCALE)#打开灰度图像
img2=cv2.imread('shu3.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,
                     **draw_params)
plt.imshow(img3)
plt.axis('off')
plt.show()

结果如下。
在这里插入图片描述

8.4对象查找

经过特征匹配后,可找到查询图像在训练图像中的最佳匹配,从而可在训练图像中精确查找到查询图像。获得最佳图像后,调用cv2.findHomegraphy()函数来执行查询图像和训练图像的透视变换,可获得查询图像在训练图像中的位置。
cv2.findHomography()函数的基本格式如下。

retv,mask=cv2.findHomography(srcPoints,dstPoints[,method[,
ransacReprojThreshold]])

参数说明如下。

  • retv为返回的转换矩阵。
  • mask为返回的查询图像在训练图像中的最佳匹配结果掩模。
  • srcPoints为查询图像匹配结果的坐标。
  • dstPoints为用于计算透视转换矩阵的方法。
  • ransacReprojThreshold为可允许的最大重投影误差。
    cv2.perspectiveTransform()函数的基本格式如下。
dst=cv2.perspectiveTransform(src,m)

参数说明如下。

  • src为输入的2通道或3通道浮点类型的数组。
  • m是大小为3X3或4X4的浮点类型的转换矩阵,如使用cv2.findHomography()函数返回的转换矩阵。
  • dst为输出结果数组,大小和类型与src相同。
    示例代码如下。
#对象查找
import cv2
import numpy as np
import matplotlib.pyplot as plt
img1=cv2.imread('shu1.png',cv2.IMREAD_GRAYSCALE)#打开灰度图像
img2=cv2.imread('shu3.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)#创建匹配器
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([kp1[m.queryIdx].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.cv2.drawMatches(img1,kp1,img2,kp2,ms,None,
                         matchColor=(0,255,0),
                         singlePointColor=None,
                         matchesMask=matchesMask,
                         flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3)
plt.axis('off')
plt.show()

结果如下图。
在这里插入图片描述

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

版权声明:本文为CSDN博主「Lucky_1129」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43705733/article/details/122812919

Lucky_1129

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

实战:使用 PyTorch 和 OpenCV 实现实时目标检测系统

点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达一、引言自动驾驶汽车可能仍然难以理解人类和垃圾桶之间的区别,但这并没有使最先进的物体检测模型在过去十年中取得的惊人进步相去甚远。将其

Python如何优雅地可视化目标检测框

1 引言 随着计算机视觉算法工程师的内卷,从事目标检测的小伙伴们越来越多了. 很多时候我们费了九牛二虎之力训练了一版模型,可是可视化出来的效果平淡无奇. 是不是有点太不给力啦,作为计算机视觉工程师,我们是不是应该关注下如何优雅地可视化我们模型