初学OpenCV学习记录(八)

以下内容摘自《OpenCV2计算机视觉编程手册》

引言

特征点(兴趣点、关键点)的概念被大量的用于解决物体识别、图像匹配、视觉跟踪、三维重建等问题,接下来介绍几种常见的特征点和在图像匹配中的应用

Harris角点

角点可以理解为顶点,在人造物体中随处可见,位于两条边缘的交点处,是可以用于精确定位的二维特征.
基本原理:角点检测算法观察像素点周围一个邻域内的方向性强度的平均变化,邻域大小由参数定义,强度变化的平均值在所有可能的方向进行计算,我们首先获取平均强度变化最大值对应的方向,然后再检查位于它垂直方向的变化是否也很强烈,同时满足条件的便是一个角点。
一个可以直接计算所有方向上强度平均变化率的方程如下:
在这里插入图片描述
中间的是协方差矩阵,表现所有方向上强度的变化率,这个矩阵中使用到的一阶导数通过Sobel算子计算得到,Sobel滤波器的大小由函数参数给定,计算出的协方差矩阵的两个特征值给出了最大平均强度变化和其垂直方向上的平均强度变化,由于特征值计算比较复杂,这里利用特征值的性质,计算下面这个公式的值。
在这里插入图片描述
C矩阵为上面的协方差矩阵,实际上,只有当两个特征值都高时,上面的公式结果才会高,那么这个点就是我们需要的角点,k值由函数的参数给定,通常选择0.05~0.5。
示例代码如下:

cv::Mat cornerStrength;
cv::cornerHarris(image,cornerStrength,
                  3,//邻域尺寸
                  3,//滤波器尺寸
                  0.01)//k值大小

如果我们仅使用上面这段代码得到的角点数量过多,最好进行一次局部极大值的筛选,书中具体的操作是对得到的角点图像cornerSrength膨胀,因为膨胀运算会替换每个像素值为相邻范围内的最大值,这样只有局部极大值点才会保留原样,从而筛选出角点中的局部极大值点,这块详细的代码看书中即可。另一种中改进角点的方法是使用扩展的Harris检测器(推荐使用这种):
该函数能够使得角点分布更加均匀,复杂度有所增加,适合用于视觉跟踪,该算法与基本的Harris角点检测的不同点在于:
1.使用浮点处理器直接计算特征值的大小
2.避免了特征点集中在纹理丰富部分的缺点,通过参数设置两个特征点之间的最小距离、角点质量阈值、限制特征点的数目(特征点质量从大到小排列,优先选择质量高的特征点),利用这几种方法获得优质的Harris角点。
示例代码入下:

std::vector<cv::Point2f> corners;
cv::goodFeaturesToTrack(image,corners,
                          500,//返回特征点的最大数目
                          0.01//质量等级
                          10)//两点之间的最小允许距离

检测FAST特征

该算法的显著特点就是运行速度快,仅依赖少数像素就能够确定一个点是否是特征点。
基本原理:检查候选像素周围一圈像素,与中心点像素大小差异较大的像素如果组成圆弧,并且弧长大于圆周长的3/4,那么就认为找到了一个特征点,这个算法还会首先检测上下左右四个点,至少有三个点与中心像素差异较大,否则直接排除,因此速度非常快,同样,也建议在提取出FAST特征点后自己编写非极大值抑制代码提高特征点的质量。
示例代码如下:

std::vector<cv::KeyPoint>keypoints;
cv::FastFeatureDetector fast(40); //检测阈值
fast.detect(image,keypoints);
cv::drawKeypoints(image,keypoints,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);       //绘制特征点

尺度不变的SURF特征

SURF特征点最重要的特点就是尺度不变性,我们分别在近处和远处对同一个物体进行拍照,物体在图像中的大小不同(尺度不同),不同尺度下的特征点都伴随着对应的尺寸因子,如果是其他特征点检测,那么尺度不同的图像检测出来的特征点可能完全不同,而SURF可以检测出尺度不变的特征点,这些特征点可以用于尺度不同的图像之间的匹配。
基本原理:在前几章中,我们使用过高斯滤波器对图像进行滤波, σ \sigma σ参数定义了高斯核的大小,高斯滤波器可以对图像的细节平滑, σ \sigma σ越大,图片越模糊,这就非常类似于我们在对物体拍照时候,离得越远,物体越模糊,我们把 σ \sigma σ定义为高斯滤波的尺度因子。
如果我们使用不同尺度因子的高斯滤波计算图像某一位置的拉普拉斯算子(二阶导数),可以获得不同的值,同一尺度因子下计算图像不同位置的拉普拉斯算子也可以获得不同的值,因此,拉普拉斯算子实际上式关于 ( x , y , σ ) (x,y,\sigma) (x,y,σ)和函数, ( x , y ) (x,y) (x,y)为图像的空间域, σ \sigma σ为尺度域,那么,尺度不变特征既是空间域又是尺度域上的局部极大值。
具体的计算方法:
计算每个像素的Hessian矩阵,矩阵表示像素的局部曲率特征(相当于拉普拉斯算子)
在这里插入图片描述该矩阵的行列式表示特征(曲率)的强度,该矩阵有二阶导数组成,实际计算的时候使用高斯核估算,这里不再赘述,具体看书,默认情况下我们使用12个不同尺寸的核计算该强度,那么我们实际上得到了关于 ( x , y , σ ) (x,y,\sigma) (x,y,σ)三变量的强度函数,当强度值在空间域和尺度域上达到局部极大值时,我们就认为这个像素是一个SURF特征点。
示例代码如下:

std::vector<cv::KeyPoint>keypoints;
cv::SurfFeatureDetector surf(2500.); //检测阈值
surf.detect(image,keypoints);
cv::drawKeypoints(image,keypoints,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);       //绘制特征点

利用SURF特征描述子匹配特征点

SURF算法为每一个检测到的特征点定义了位置和尺度,尺度值表示特征点周围的一个窗口大小,在这个窗口内,不论尺度如何变化都会包含相同的视觉信息,SURF算法默认的特征描述子是一个64维的向量,描述了特征点周围的强度信息,两个特征点越相似,特征向量的距离越近(比如欧式距离),这可以运用在图像匹配上,比如我们对同一物体在远近位置拍了两张照片,我们检测每一幅图片中的SURF特征点,然后提取他们的描述,最后比较两幅图像中的描述子之间的距离,距离越近表示这两个特征点越相似。
示例代码:

cv::SurfDescriptorExtractor surfDesc;
cv::Mat descriptors1,descriptors2;
surfDesc,compute(image1,keypoints1,descriptors1);//提取特征描述子
surfDesc,compute(image2,keypoints2,descriptors2);
cv::BruteForceMatcher<cv::L2<float>> matcher;
std::vector<cv::DMatch> matches;
matcher.match(descriptors1,descriptors2,matches);//匹配特征点
cv::Mat imageMatches;
cv::drawMatches(image1,keypoints1,image2,keypoints2,matches,imageMatches,cv::Scalar(255,255,255));//可视化匹配结果

在使用match函数比较以后,我们还可以做一个工作,就是自己编程利用容器的std::nth_element函数对特征点对的距离进行排序,保留一定数目的最优的特征点,其他的都舍去,这样子特征点的质量更好。
descriptors1是表示特征值的矩阵,行数为特征点的数量,列数为特征描述子的维数
cv::DMatch是匹配向量的结构,包含指向两个特征点的索引和描述距离的浮点数。
基本原理:
首先将特征点出的邻域尺寸定义为该特征点尺度因子的20倍,然后将邻域分为44大小的16个子区域,再对每个子区域分为55的小区域,对小区域应用两个核,分别测量水平和垂直方向的局部强度差值(dx和dy),对子区域中的这些小区域的测量结果求和得到4个值。
在这里插入图片描述
这4个值就是这个子区域的描述子,16个区域共有64个值,这就是一个特征点的描述子向量是64维的原因。

SIFT特征点和特征描述子

SIFT与SURF的不同之处在于不是使用Hessian行列式计算平均强度,而是使用Gaussian滤波器的差值,SIFT特征描述子对于每个子区域构建8个元素的梯度方向直方图,所以描述子的维度为128.
区别:
SIFT的运行速度更慢但是更加精确,SURF计算速度快,精度相对来说低一点。

猜你喜欢

转载自blog.csdn.net/weixin_42411702/article/details/123914163