什么是特征点?大体上说就是图像中变化比较明显的点,比如角点或者边界,在一面白色的墙上是提取不到任何特征点的。特征点有点类似于矩阵的特征值(当然只是一个比喻),用少量的bit描述大部分的图像信息,不管在神经网络还是SLAM领域,特征点都有非常多的用途。
我们平时提到特征点一般包括特征提取子和特征描述子两部分,提取子就是先采用算法检测出来一定的特征,比如FAST特征子和SIFT特征自,然后我们要再对于特征进行描述,比如BRIEF描述子和SIFT描述子,描述子可以用来进行匹配,原理就是两个描述子的距离比较近意味着这两个特征子很相似。
下面介绍常用的三种特征子以及opencv的使用例程。
Surf采用 Henssian 矩阵获取图像局部最值还是十分稳定的,但是在求主方向阶段太过于依赖局部区域像素的梯度方向,有可能使得找到的主方向不准确,后面的特征向量提取以及匹配都严重依赖于主方向,即使不大偏差角度也可以造成后面特征匹配的放大误差,从而匹配不成功。
Surf是一种只利用到灰度性质的算法,忽略了色彩信息。
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
// 读入图像
cv::Mat image = cv::imread("../demo1.jpeg");
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
// 特征点的向量
std::vector keypoints;
// 构造SURF特征检测器
cv::SurfFeatureDetector surf;
// 检测SURF特征
surf.detect(image, keypoints);
cv::Mat featureImage;
cv::drawKeypoints(image, // 原始图像
keypoints, // 特征点的向量
featureImage, // 生成图像
cv::Scalar(255,255,255), // 特征点的颜色
cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); // 标志位
cv::namedWindow("SURF Features");
cv::imshow("SURF Features",featureImage);
cv::waitKey(0);
return 0;
}
SIFT的全称是Scale Invariant Feature Transform,尺度不变特征变换,由加拿大教授David G.Lowe提出的,拥有版权专利,只提供可执行程序。
SIFT特征对旋转、尺度缩放、亮度变化等保持不变性,是一种非常稳定的局部特征。所以是目前比较常用的一种特征检测方法。也是只利用了灰度图。
特点:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
// 读入图像, 第二个参数0表示灰度图
Mat image = imread("../demo1.jpeg");
Mat featureImage;
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
// 特征点的向量
std::vector keypoints;
// 构造SIFT特征检测器
cv::SiftFeatureDetector sift;
// 检测SIFT特征值
sift.detect(image, keypoints);
drawKeypoints(image, // 原始图像
keypoints, // 特征点的向量
featureImage, // 生成图像
cv::Scalar(255,255,255), // 特征点的颜色
cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); // 标志位
cv::namedWindow("SIFT Features");
cv::imshow("SIFT Features", featureImage);
cv::waitKey(0);
return 0;
}
ORB是一种基于BRIEF特征描述子的改进,采用了具有方向性的Oriented FAST作为特征点检测算子,并且使用了改进版本的steer Brief描述子。2010年提出,目前广泛应用在SLAM等实时系统中。
关于尺度不变性:ORB没有试图解决尺度不变性,但是这样只求速度的特征描述子,一般都是应用在实时的视频处理中的,这样的话就可以通过跟踪还有一些启发式的策略来解决尺度不变性的问题。
关于计算速度:ORB是sift的100倍,是surf的10倍。
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat img_1 = imread("../demo1.jpeg");
Mat img_2 = imread("../demo2.jpeg");
if (!img_1.data || !img_2.data)
{
cout << "error reading images " << endl;
return -1;
}
// 声明特征子
ORB orb;
vector keyPoints_1, keyPoints_2;
Mat descriptors_1, descriptors_2;
// 开始检测
orb(img_1, Mat(), keyPoints_1, descriptors_1);
orb(img_2, Mat(), keyPoints_2, descriptors_2);
// 检测完成后进行匹配
BruteForceMatcher matcher;
vector matches;
matcher.match(descriptors_1, descriptors_2, matches);
// 显示匹配的结果
Mat img_matches;
drawMatches(img_1,
keyPoints_1,
img_2,
keyPoints_2,
matches,
img_matches,
Scalar::all(-1),
Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::namedWindow("ORB Features"); // 可以不写
imshow("ORB Features", img_matches);
cvWaitKey(0);
return 0;
}
上述三个程序都是在linux环境下写的,opencv的版本是2.4.9 。附上CMakeLists.txt方便大家编译:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(PROJECT_NAME demo)
PROJECT(${PROJECT_NAME})
FIND_PACKAGE(OpenCV REQUIRED)
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})
MESSAGE(STATUS "Project: ${PROJECT_NAME}")
MESSAGE(STATUS "OpenCV library status:")
MESSAGE(STATUS " version: ${OpenCV_VERSION}")
MESSAGE(STATUS " libraries: ${OpenCV_LIBS}")
MESSAGE(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
# 增加 sift 程序编译
ADD_EXECUTABLE(sift sift.cpp)
TARGET_LINK_LIBRARIES(sift ${OpenCV_LIBS})
# 增加 surf 程序编译
ADD_EXECUTABLE(surf surf.cpp)
TARGET_LINK_LIBRARIES(surf ${OpenCV_LIBS})
# 增加 orb 程序编译
ADD_EXECUTABLE(orb orb.cpp)
TARGET_LINK_LIBRARIES(orb ${OpenCV_LIBS})