图像特征—FAST关键点
一、引言
最近在看视觉slam十四讲这本书,里面关于图像特征点的一些总结非常值得学习一下。
1.特征点所应具有的性质
(1)可重复性:相同的特征可以在不同的图像中找到;
(2)可区别性:不同的特征具有不同的表达;
(3)高效率:同一图像中,特征点的数量应远小于像素的数量;
(4)本地性:特征仅与一小片图像区域有关。
2.常见的一些特征提取方法的区别
(1)SIFT(尺度不变特征变换)特征:属于最为经典的一种,该方法充分地考虑了在图像变换过程中出现的光照、尺度、旋转等变化,因此也使得这种方法的计算
量极大,使用非常耗时。
(2)FAST关键点:属于计算特别快的一种特征点检测方法,之所以快是因为这种方法只是只是找到了关键点,并没有相应的描述子。
(3)ORB特征:该方法改进了FAST检测不具有方向性的问题,并且采用速度极快的二进制描述子BRIEF,使整个图像特征的提取速度大大加快。
补充:特征点是由关键点和描述子两部分组成。关键点是指该特征点在图像中的位置,而描述子是描述特征点周围像素的信息。
二、FAST关键点
1.检测思路
如果一个像素与邻域的像素差别较大(过亮或过暗),那么它更可能是角点。
2.检测过程
(1)在图像中选取像素p,假设它的亮度为Ip;
(2)设置一个阈值T,比如为Ip的五分之一;
(3)以像素p为中心,选取半径为3的圆上的16个像素点;
(4)假如选取的圆上有连续的N个点的亮度大于Ip+T或者小于Ip-T,那么像素p可以被认为是特征点(N通常取12,即FAST-12。其他的常用的N取值为9和11,
即FAST-9和FAST-11);
(5)循环以上四步,对每个像素执行相同的操作。
补充:在FAST-12中,为提升检测的效率,通常会增加一步预筛选,具体操作如下:
对每个像素,直接检测邻域圆上的第1、5、9、13个像素的亮度,只有当这4个像素中有3个同时大于Ip+T或小于Ip-T时,当前像素才有可能是一个角点,否则就
直接排除。
3.FAST-12代码如下
#include <iostream>
#include <opencv2/opencv.hpp>
class FastKeyPointDec{
public:
/* 功能:判断当前像素点是否为角点;
* 输入:arg1:Ip当前像素点的亮度值
* arg2:t为筛选阈值
* arg3:IpVec为以当前像素点为中心,半径为3的周围16个像素
* 输出:true or false
* */
bool isKeyPoint(double Ip,double T,std::vector<double>IpVec){
assert(IpVec.size()==16);
int n=0; //统计特征点数量
//assert(Ip>=T);
//预筛选:1 5 9 13四个点至少有三个同时满足条件可进行下一步
int a1=IpVec[0]>Ip+T || IpVec[0]<Ip-T;
int a2=IpVec[4]>Ip+T || IpVec[4]<Ip-T;
int a3=IpVec[8]>Ip+T || IpVec[8]<Ip-T;
int a4=IpVec[12]>Ip+T || IpVec[12]<Ip-T;
if(a1+a2+a3+a4<=2){
return false;
}
for(size_t i=0;i<16;i++){
double temp=IpVec[i];
if(temp>Ip+T||temp<Ip-T){
n++;
}
}
if(n>=12){
return true;
}else{
return false;
}
}
/* 功能:对一幅图像进行角点检测
* 输入:arg1:输入检测的图像
* arg2:角点坐标
* arg3:阈值
* 输出:角点检测的结果
* */
void fastDectKey(const cv::Mat srcImg,std::vector<cv::Point>&keyPoint,double T){
assert(!srcImg.empty());
cv::Mat img;
int row=srcImg.rows;
int col=srcImg.cols;
std::vector<double>ipVec(0);
cv::cvtColor(srcImg,img,cv::COLOR_BGR2GRAY);
//assert(img.channels()==1);
for(int i=3;i<row-3;i++){
for(int j=3;j<col-3;j++){
double ipval=img.at<uchar>(i,j);
ipVec.push_back(img.at<uchar>(i-3,j));ipVec.push_back(img.at<uchar>(i-3,j+1)); //1,2
ipVec.push_back(img.at<uchar>(i-2,j+2)); //3
ipVec.push_back(img.at<uchar>(i-1,j+3));ipVec.push_back(img.at<uchar>(i,j+3));ipVec.push_back(img.at<uchar>(i+1,j+3));//4,5,6
ipVec.push_back(img.at<uchar>(i+2,j+2)); //7
ipVec.push_back(img.at<uchar>(i+3,j+1));ipVec.push_back(img.at<uchar>(i+3,j));ipVec.push_back(img.at<uchar>(i+3,j-1));//8,9,10
ipVec.push_back(img.at<uchar>(i+2,j-2)); //11
ipVec.push_back(img.at<uchar>(i+1,j-3));ipVec.push_back(img.at<uchar>(i,j-3));ipVec.push_back(img.at<uchar>(i-1,j-3));//12,13,14
ipVec.push_back(img.at<uchar>(i-2,j-2)); //15
ipVec.push_back(img.at<uchar>(i-3,j-2)); //16
if(ipVec.size()==16){
if(isKeyPoint(ipval,T,ipVec)){
keyPoint.push_back(cv::Point(j,i));
cv::circle(srcImg,cv::Point(j,i),2,cv::Scalar(255,255,0),1,cv::LINE_8);
}
ipVec.clear();
}
}
}
cv::imshow("res-key",srcImg);
}
};
int main() {
std::cout << "Hello, World!" << std::endl;
cv::Mat src=cv::imread("../1.png");
if(src.empty()){
std::cout<<"no image\n";
}
FastKeyPointDec fast;
std::vector<cv::Point>pt;
//cv::flip(src,src,1);
fast.fastDectKey(src,pt,30);
cv::imshow("src",src);
cv::waitKey(0);
return 0;
}
检测效果如下
三、总结
观察FAST-12的检测结果,图像中大多亮度变化明显的角点都有被检测出来,但是单纯的论特征点检测来说,效果不甚理想。其原因大概有以下三点:
(1)FAST算法只是比较图像亮度的大小,所以亮度变化明显的容易被检测出来,而在一些亮度变化缓慢的地方的特征点容易被忽略,因此可以在预处理时增加
一些边缘锐化等一些操作,增加一下图像的对比度,可以较好的提升检测效果。
(2)筛选阈值选取的不够合理;
(3)检测的图像不够理想。
另外,在FAST关键点检测完毕后,还需要使用非极大值抑制法再进行一次筛选,以避免特征点集中的问题。