OpenCV学习笔记(二)——特征提取与描述
OpenCV学习笔记(二)——特征提取与描述
1. 特征点简介
什么是特征点?简单来说就是图像中变化显著的特殊位置,在平滑或均匀颜色的区域通常难以提取到足够的特征点。这些特殊位置类似于矩阵中的关键数值(当然这只是个比喻),通过少量bit编码即可大致描述图像的主要信息,在计算机视觉领域无论是深度学习还是视觉定位(SLAM),都需要大量依赖于这些关键位置的存在与应用。
在实际应用中我们通常将这些关键位置划分为两类:一个是用于检测阶段的关键位置提取器(如FAST、SIFT算法)另一个则是用于对这些关键位置进行编码描述的关键函数(如BRIEF、SIFT编码函数)。这些编码函数的作用就是通过对关键位置进行匹配分析从而实现对目标物体或场景的理解与识别工作原理就在于两个编码函数之间计算出的距离越小就意味着这两个关键位置具有较高的相似性
下面我们将重点介绍几种常用的特征求取方法及其在OpenCV中的实现过程
2. SURF(Speed Up Robust Feature)
Surf算法在使用Hessian矩阵获取图像局部极值方面表现出非常稳定的性能,在确定主方向的过程中过分依赖局部区域像素梯度的方向可能会导致主方向识别不够准确;由于后续的特征向量提取和匹配过程高度依赖于主方向这一特性,在即使存在一定程度的角度偏差的情况下也可能造成特征匹配结果被放大误差所影响而导致最终匹配失败;该算法仅基于灰度信息进行操作而未考虑色彩信息
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/nonfree/nonfree.hpp>
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<cv::KeyPoint> 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;
}
3. SIFT(Scale-invariant feature transform)
SIFT的英文名称是Scale Invariant Feature Transform(SIFT),它也被称为尺度不变的特征描述符(Scale-Invariant Feature Transform),由加拿大学者David G.Lowe于1994年首次提出并获得专利授权。该方法对于图像旋转、缩放以及亮度变化均表现出高度鲁棒性,并被广泛应用于各种计算机视觉任务中。因此成为当前应用最为广泛的特征检测方案之一。这也使得它仅依赖于灰度图像数据来进行分析和识别。
- 图像的局部特征在旋转、尺度缩放以及亮度变化等变换下保持不变;而在视角变化、仿射变换以及噪声等方面也展现出一定的稳定性。
- 其独特性和丰富的信息含量使其能够适应海量特征库并通过高效的方式实现快速且精确的信息匹配。
- 即使数量极少的情况下也能产生大量的SIFT特征。
- 经过优化后的新算法具备实时性的能力。
- 其扩展性强使得它能够方便地与其他多种特征向量协同工作。
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/nonfree/nonfree.hpp>
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<cv::KeyPoint> 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;
}
4. ORB(Oriented Brief)
一种优化型的方向感知FAST算法与增强版steer-BRIEF描述器相结合的方式被提出于2010年以后,并广泛应用于如SLAM等实时系统中。
关于尺度不变性问题:并未旨在解决这一特性的问题,在这类快速算法中通常主要用于实时视频处理领域,并可能结合跟踪技术和若干启发式策略来辅助实现。
在计算速度方面:该算法相较于SIFT的速度提升了一百倍,在性能上则较之SURF提升了十倍。
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/legacy/legacy.hpp>
#include <iostream>
#include <vector>
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<KeyPoint> 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<HammingLUT> matcher;
vector<DMatch> 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;
}
5. 程序的编译
包括几个项目均基于Linux开发环境运行,并且使用的是OpenCV 2.x系列中的具体版本号为2.x.x框架构建系统配置文件。请提供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})
