特征检测
哈里斯角检测
cv.cornerHarris()
输入:
• img - 数据类型为 float32 的输入图像。
• blockSize - 角点检测中要考虑的领域大小。
• ksize - Sobel 求导中使用的窗口大小
• k - Harris 角点检测方程中的自由参数,取值参数为 [0,04,0.06].
返回:
是具有特征分数的灰度图像。
import numpy as np
import cv2 as cv
filename = 'C:\\Users\\Administrator\\Desktop\\pic\\jiao.jpg'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
#result用于标记角点,并不重要
dst = cv.dilate(dst,None) #最佳值的阈值,它可能因图像而异。
img[dst>0.01*dst.max()]=[0,0,255]
cv.imshow('dst',img)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
SubPixel精度的转角
有时可能需要找到最精确的角落。即细化了以亚像素精度检测到的角落。
cv.connectedComponentsWithStats()
connectedComponentsWithStats()创建了一个标记图(图中不同连通域使用不同的标记,和原图宽高一致),可以完成上面任务,除此之外,还可以返回每个连通区域的重要信息。
输入:
img-8位单通道二值图像;
输出:
ret:连通区域的数量
abel:输出和原图image一样大的标记图,label对应于表示是当前像素是第几个轮廓,背景置0;
centroids:对应的是轮廓的中心点。nccomps×2的矩阵 表示每个连通区域的质心
stats:输出nccomps×5的矩阵 ,表示每个连通区域的外接矩形和面积
cv.cornerSubPix()
该函数可以为检测到的角点作进一步的优化计算,可使角点的精度达到亚像素级别。
输入:
第一个参数是输入图像
第二个参数是检测到的角点,一般是提前检测到的哈里斯角的质心。
第三个参数是计算亚像素角点时考虑的区域的大小,大小为NXN; N=(winSize*2+1)。
第四个参数作用类似于winSize,但是总是具有较小的范围,通常忽略(Size(-1, -1))。
第五个参数用于表示计算亚像素时停止迭代的标准,可选的值有cv::TermCriteria::MAX_ITER 、cv::TermCriteria::EPS(可以是两者其一,或两者均选),前者表示迭代次数达到了最大次数时停止,后者表示角点位置变化的最小值已经达到最小时停止迭代。二者均使用cv::TermCriteria()构造函数进行指定。
import numpy as np
import cv2 as cv
filename = 'C:\\Users\\Administrator\\Desktop\\pic\\djiao.png'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 寻找哈里斯角
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
dst = cv.dilate(dst,None)
ret, dst = cv.threshold(dst,0.01*dst.max(),255,0)
dst = np.uint8(dst)
# 寻找质心
ret, labels, stats, centroids = cv.connectedComponentsWithStats(dst)
# 定义停止和完善拐角的条件
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)
# 绘制
res = np.hstack((centroids,corners))
res = np.int0(res)
img[res[:,1],res[:,0]]=[0,0,255]
img[res[:,3],res[:,2]] = [0,255,0]
cv.imshow('dst',img)
cv.waitKey(1000000)
Shi-tomas拐角检测器
OpenCV有一个函数cv.goodFeaturesToTrack()。它通过Shi-Tomasi方法(或哈里斯角检测,可以找到图像中的拐角。低于平均质量的所有拐角点均被拒绝。后,它会根据质量以降序对剩余的角进行排序。然后函数首先获取最佳拐角,然后丢弃最小距离范围内的所有附近拐角,然后返回N个最佳拐角。
输入:
image: 输入图像,是八位的或者32位浮点型,单通道图像,所以有时候用灰度图
maxCorners: 返回最大的角点数,是最有可能的角点数,如果这个参数不大于0,那么表示没有角点数的限制。
qualityLevel: 图像角点的最小可接受参数,质量测量值乘以这个参数就是最小特征值,小于这个数的会被抛弃。
minDistance: 返回的角点之间最小的欧式距离。
mask: 检测区域。如果图像不是空的(它需要具有CV_8UC1类型和与图像相同的大小),它指定检测角的区域。
blockSize: 用于计算每个像素邻域上的导数协变矩阵的平均块的大小。
useHarrisDetector:选择是否采用Harris角点检测,默认是false.
k: Harris检测的自由参数。
返回
是一个数组n12的数组,n是检测到的角的数量。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\rentou.png')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
corners = cv.goodFeaturesToTrack(gray,25,0.01,10)
corners = np.int0(corners)
print(corners)
for i in corners:
x,y = i.ravel()
cv.circle(img,(x,y),3,255,-1)
plt.imshow(img),plt.show()
SIFT尺度不变特征变换
前面部分的一些像Harris这样的拐角检测器。它们是旋转不变的,这意味着即使图像旋转了,我们也可以找到相同的角。很明显,因为转角在旋转的图像中也仍然是转角。但是如果缩放图像,则拐角可能不是角。因为某些角度在被放大后角度会变平坦如下图。因此,Harris拐角不是尺度不变的。接下来会介绍尺度不变的拐角检测器。
该算法分为五个步骤:
- 尺度空间极值检测
- 关键点定位
- 方向分配
- 关键点描述
- 关键点匹配
sift.detect()
输出
des为 128 为向量组成的列表
kp 为关键点列表,每个元素为一个 KeyPoint,其包含信息有:
- angle:角度,表示关键点的方向. 为了保证方向不变形,SIFT算法通过对关键点周围邻域进行梯度运算,求得该点方向. -1为初值.
- class_id:当要对图片进行分类时,可以用 class_id 对每个特征点进行区分. 未设定时为-1,需要自己设定.
- .octave:代表是从金字塔哪一层提取的得到的数据.
- pt:关键点的坐标x,y.
- response:响应程度,代表该点强壮大小,更确切的说,是该点角点的程度.
- size:该点直径的大小
cv.drawKeyPoints()
cv.drawKeyPoints()函数,该函数在关键点的位置绘制小圆圈。 如果将标志
cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS传递给它,它将绘制一个具有关键点大小的圆,甚至会显示其方向。
计算描述符
要计算描述符,OpenCV提供了两种方法。
- 由于已经找到关键点,因此可以调用sift.compute(),该函数根据我们找到的关键点来计算描述符。例如: kp,des = sift.compute(gray,kp)
- 如果找不到关键点,则可以使用sift.detectAndCompute()函数在单
步骤中直接找到关键点和描述符。
import numpy as np
import cv2 as cv
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\rentou.png')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
sift = cv.xfeatures2d.SIFT_create()
#方法一
kp = sift.detect(gray,None)
kp,des = sift.compute(gray,kp)
#方法二
kp, des = sift.detectAndCompute(gray,None)
img=cv.drawKeypoints(gray,kp,img,flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv.imshow('dst',img)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
SURF
SIFT用于关键点检测和描述符,SURF是一个加速版本的SIFT。使用方法和SIFT类似。
import numpy as np
import cv2 as cv
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\rentou.png')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 这里设置海森矩阵的阈值为400
surf = cv.xfeatures2d.SURF_create(400)
# 我们将其设置为50000。记住,它仅用于表示图片。
# 在实际情况下,最好将值设为300-500
surf.setHessianThreshold(50000)
kp, des = surf.detectAndCompute(img,None)
img2 = cv.drawKeypoints(img,kp,None,(255,0,0),4)
cv.imshow('dst',img2)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
角点检测FAST算法
FAST算法比其他现有的拐角检测器快几倍。但是它对高水平的噪声并不鲁棒。
使用FAST进行特征检测:
1.选择图像中是否要识别为兴趣点的像素p,使其强度为I_p
2.选择适当的阈值t
3.考虑被测像素周围有16个像素的圆圈。
4.现在,如果圆中存在一组(共16个像素)n个连续的像素,它们均比I_p + t亮,或者比I_p-t都暗,则像素p是一个角。(在上图中显示为白色虚线)。n被选12。
5.建议使用高速测试以排除大量的非角区域。此测试仅检查1、9、5和13处的四个像素(如果第一个1和9太亮或太暗,则对其进行测试。如果是,则检查5和13)。如果p是一个角,则其中至少三个必须全部比I_p + t亮或比I_p-t暗。如果以上两种情况都不是,则p不能为角。然后,可以通过检查圆中的所有像素,将完整的分段测试标准应用于通过的候选项。该检测器本身具有很高的性能,但有几个缺点:
(1)它不会拒绝n <12的候选对象。
(2)像素的选择不是最佳的,因为其效率取决于问题的顺序和角落外观的分布。
(3)高速测试的结果被丢弃了。
(4)彼此相邻地检测到多个特征。
机器学习一个角检测器
1.选择一组图像进行训练(最好从目标应用程序域中进行训练)
2.在每个图像中运行FAST算法以查找特征点。
3.对于每个特征点,将其周围的16个像素存储为矢量。对所有图像执行此操作以获得特征向量P。
4.这16个像素中的每个像素(例如x)可以具有以下三种状态之一:
1.取决于这些状态,特征矢量P被细分为3个子集,P_d, P_s, P_b。
2.定义一个新的布尔变量K_p,如果p是一个角,则为true,否则为false。
3.使用ID3算法(决策树分类器)使用变量K_p查询每个子集,以获取有关真实类的知识。它选择x,该x通过K_p的熵测得的有关候选像素是否为角的信息最多。
递归地将其应用于所有子集,直到其熵为零为止。
5.这样创建的决策树用于其他图像的快速检测。
非最大抑制
在相邻位置检测多个兴趣点是另一个问题。通过使用非极大抑制来解决。
1.计算所有检测到的特征点的得分函数V。V是p与16个周围像素值之间的绝对差之和。
2.考虑两个相邻的关键点并计算它们的V值。
3.丢弃较低V值的那个。
代码例子
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\mei.png',0)
# 用默认值初始化FAST对象
fast = cv.FastFeatureDetector_create()
# 寻找并绘制关键点
kp = fast.detect(img,None)
img2 = cv.drawKeypoints(img, kp, None, color=(255,0,0))
# 打印所有默认参数
print( "Threshold: {}".format(fast.getThreshold()) )
print( "nonmaxSuppression:{}".format(fast.getNonmaxSuppression()) )
print( "neighborhood: {}".format(fast.getType()) )
print( "Total Keypoints with nonmaxSuppression: {}".format(len(kp)) )
cv.imshow('dst',img2)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
# 关闭非极大抑制
fast.setNonmaxSuppression(0)
kp = fast.detect(img,None)
print( "Total Keypoints without nonmaxSuppression: {}".format(len(kp)) )
img3 = cv.drawKeypoints(img, kp, None, color=(255,0,0))
cv.imshow('dst2',img3)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
BRIEF
BRIEF是一种更快的方法特征描述符计算和匹配。除了平面内旋转较大的情况,它将提供很高的识别率。
它提供了一种直接查找二进制字符串而无需查找描述符的快捷方式。它需要平滑
的图像补丁,并以独特的方式(在纸上展示)选择一组n_d(x,y)位置对。然后,在这些位置对上进行一些像素强度比较。例如,令第一位置对为p和q。如果I§<I(q),则结果为1,否则为0。将其应用于所有n_d个位置对以获得n_d维位串。
该n_d可以是128、256或512。OpenCV支持所有这些,但默认情况下将256(OpenCV以字节为单位表示,因此值将为16、32和64)。因此,一旦获得此信息,就可以使用汉明距离来匹配这些描述符。重要的一点是,BRIEF是特征描述符,它不提供任何查找特征的方法。因此,您将不得不使用任何其他特征检测器,例如SIFT,SURF等。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\rentou.png',0)
# 初始化FAST检测器
star = cv.xfeatures2d.StarDetector_create()
# 初始化BRIEF提取器
brief = cv.xfeatures2d.BriefDescriptorExtractor_create()
# 找到STAR的关键点
kp = star.detect(img,None)
# 计算BRIEF的描述符
kp, des = brief.compute(img, kp)
img2 = cv.drawKeypoints(img, kp, None, color=(0,255,0), flags=0)
cv.imshow('dst',img2)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
ORB(面向快速和旋转的BRIEF)
ORB计算中SIFT和SURF的良好替代方案成本,匹配性能以及主要是专利。SIFT和SURF已获得专利,使用其需要付费,但是ORB不是。
ORB基本上是FAST关键点检测器和Brief描述符的融合,首先,它使用FAST查找关键点,然后应用Harris角测度在其中找到前N个点。它还使用金字塔生成多尺度特征。但是一个问题是,FAST无法计算方向。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\rentou.png',0)
# 初始化ORB检测器
orb = cv.ORB_create()
# 用ORB寻找关键点
kp = orb.detect(img,None)
# 用ORB计算描述符
kp, des = orb.compute(img, kp)
# 仅绘制关键点的位置,而不绘制大小和方向
img2 = cv.drawKeypoints(img, kp, None, color=(0,255,0), flags=0)
plt.imshow(img2), plt.show()