Harris角点:
假定一个特征点,观察特征点周围小窗口(如3X3)内各个方向的强度平均变化,求得平均强度变化最大值的方向,检查该方向的垂直方向的强度变化是否很大(满足设定的阈值)。满足该条件,即为harris角点。
数学表示:
I(x,y)表示点(x,y)的强度值。对于偏移向量(u,v),偏移向量可以看成某个方向。它的平均强度变化为:
求和覆盖预定义窗口内的相邻像素点。该式用泰勒级数展开,用矩阵形式表示:
很明显,中间是一个协方差矩阵,表示所有方向上强度的变化率。这个矩阵的两个特征值给出了最大平均强度变化和垂直方向上的平均强度变化。如果,两个特征值都较高,高于给定的阈值。此点即为角点。
为了避免计算特征值(耗时),使用以下式子代替:
Det矩阵的行列式,Trace矩阵的迹。经证明,两个特征值都较高时,该式子的值较高。参数k一般设置为0.05~0.5。
Harris角点检测,可以通过极大值抑制改进,即保证角点的强度是局部极大值,移除非局部极大值的角点。
代码:
OpenCv提供了接口计算harris角点。
cv::cornerHarris(image, //检测图像,灰度图
conerStrength, // harris角点图
neighbourhood, // 预定义相邻像素点
aperture, // sobel滤波器孔径大小
k // 参数k
);
例子:检测harris角点,非极大值抑制。以圆圈显示角点位置。
Harris.h
#include
#include
#include
class HarrisDetector
{
private:
//角点强度图
cv::Mat conerStrength;
//阈值后的角点强度图
cv::Mat conerTh;
//局部极大值图像
cv::Mat locMax;
//harris参数
int neighbourhood;//领域尺寸
int aperture;//sobel(内部)滤波孔径尺寸
double k;//参数
//最大强度值
double maxStrength;
//阈值
double threshold;
public:
//初始化
HarrisDetector()
: neighbourhood(3)
, aperture(3)
, k(0.1)
, maxStrength(0.0)
, threshold(0.01)
{
}
//harris角点检测
void detect(const cv::Mat &image)
{
//检测harris的值,返回图像每个像素为harris值,类型为32位浮点数
cv::cornerHarris(image, conerStrength, neighbourhood, aperture,
k);
//非极大值抑制
double minStrength;//未用参数
cv::minMaxLoc(conerStrength, &minStrength, &maxStrength);
//膨胀,中心点替换为相邻区域的最大值(如果该点原来是局部极大值
//,该点的值不变)
cv::Mat dilated;
cv::dilate(conerStrength, dilated, cv::Mat());
//获得局部极大值图像(图像中非零区域标记了为局部极大值的点)
cv::compare(conerStrength, dilated, locMax,cv::CMP_EQ);
}
//得到局部极大值图像
cv::Mat getCornerMap(double qualityLevel)
{
cv::Mat conerMap;
//阈值
threshold = qualityLevel*maxStrength;
//阈值化,harris值大于阈值,为白色
cv::threshold(conerStrength, conerTh, threshold, 255, CV_THRESH_BINARY);
//转换图像类型
conerTh.convertTo(conerMap, CV_8U);
//与操作(非极大值抑制),得到角点图像
cv::bitwise_and(conerMap, locMax, conerMap);
return conerMap;
}
void getCorners(std::vector<cv::Point>&points, double quiality)
{
cv::Mat conerMap = getCornerMap(quiality);
getCorners(points, conerMap);
}
//得到角点的坐标
void getCorners(std::vector<cv::Point>&points,
const cv::Mat& conerMap)
{
for (int y = 0; y < conerMap.rows; y++) {
const uchar* rowptr = conerMap.ptr<uchar>(y);
for (int x = 0; x < conerMap.cols; x++) {
if (rowptr[x]) {
points.push_back(cv::Point(x, y));
}
}
}
}
//角点以圆圈显示
void drawOnImage(cv::Mat &image, std::vector<cv::Point>&points,
cv::Scalar color = cv::Scalar(255, 255, 255), int radius = 3,
int tickness = 2)
{
std::vector<cv::Point>::const_iterator it = points.begin();
while (it != points.end()) {
cv::circle(image, *it, radius, color, tickness);
it++;
}
}
};
源.cpp
#include
#include
#include
#include
#include "Harris.h"
int main()
{
cv::Mat image = cv::imread("D:/images/111.jpg",0);
//计算程序耗时
double duration = 0.0;
duration = static_cast<double>(cv::getTickCount());
//检测角点
HarrisDetector h;
h.detect(image);
std::vector<cv::Point>pts;
h.getCorners(pts, 0.01);
//以圆圈显示角点
h.drawOnImage(image, pts);
//计算程序耗时
duration= static_cast<double>(cv::getTickCount())-duration;
duration /= cv::getTickFrequency();
std::cout << "时间:" << duration << "秒" << std::endl;
cv::imshow("Harris角点检测", image);
cv::waitKey(0);
}
FAST特征算法,相比harris,能够快速的检测特征点。
FAST特征算法中将特征点定义为:
假定候选特征点,该点与其周围一圈像素差异较大(大于阈值),且差异较大的像素围成一个周长大于3/4圆的圆弧。该点即为特征点。通常将圆圈半径设置为3(像素)
这种定义方法,相比harris角点计算邻域像素梯度,大大减少了计算量。在考虑程序运行速度时,可以采纳。
考虑上图,中心的上、下、左、右,四个点。如果中心点时特征点,那么这四个点中,至少有三个点必须和中心点差值较大。通过该方法,可以快速去掉不是特征点的检测点,加快算法检测。
代码:
std::vector<cv::KeyPoint> pts;
//声明FSAT特征检测类对象,阈值设置40
cv::FastFeatureDetector fast(40);
//调用成员函数,将特征点的位置保存给pts
fast.detect(image, pts);
示例:
#include
#include
#include
#include
#include
int main()
{
cv::Mat image = cv::imread("D:/images/111.jpg");
//计算时间
double duration = 0;
duration = static_cast<double>(cv::getTickCount());
std::vector<cv::KeyPoint> pts;
//定义FAST特征检测类对象,阈值为40
cv::FastFeatureDetector fast(40);
//调用成员函数
fast.detect(image, pts);
/调用OpenCV提供的特征点绘制函数,以圆圈显示特征点位置
cv::drawKeypoints(image, pts, image, cv::Scalar(255, 255, 255),
cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
//计算时间
duration = static_cast<double>(cv::getTickCount()) - duration;
duration /= cv::getTickFrequency();
//显示结果
cv::imshow("Fast特征", image);
std::cout << "时间:" << duration <<"秒" <<std::endl;
cv::waitKey(0);
}
总结:
虽然有一定的偏差,但两种算法的大体消耗时间有很大的差别,在本例中:FAST比harris实际运行几乎快了10倍以上。