Opencv 关键点和描述符(二)—— 通用关键点和描述符
Opencv 关键点和描述符(一)—— 关键点及跟踪基础
Opencv 关键点和描述符(三)—— 核心关键点检测方法
关键点就是一小块图像,而描述符是一种数学结构,通常为一组浮点数。而如何更好地将图像信息抽象为描述符将是一个主要的问题。同时,对于描述符,它应该能够针对不同的场景,给出一定的旋转不变性。
关键点和描述符的是三个主要的应用场景就是跟踪,目标识别和立体建模。不过不管是哪种用途,根本的处理逻辑确实类似的:首先找出图像中的关键点,之后选取一种描述符,最后基于关键点的描述符查找匹配 —— detect-describe-match。
光流,跟踪和识别
为了描述关键点,Opencv 关键点的类定义如下
class cv::KeyPoint {
public:
cv::Point2f pt; // coordinates of the keypoint
float size; // diameter of the meaningful keypoint neighborhood
float angle; // computed orientation of the keypoint (-1 if none)
float response; // response for which the keypoints was selected
int octave; // octave (pyramid layer) keypoint was extracted from
int class_id; // object id, can be used to cluster keypoints by object
cv::KeyPoint(
cv::Point2f _pt,
float _size,
float _angle = -1,
float _response = 0,
int _octave = 0,
int _class_id = -1
);
cv::KeyPoint(
float x,
float y,
float _size,
float _angle = -1,
float _response = 0,
int _octave = 0,
int _class_id = -1
);
...
};
参数说明:
- pt:关键点的位置
- size:关键点的范围
- angle:关键点角度
- response:能够给某个关键点更强烈响应的检测器,有时能够被理解为特性实际存在的概率
- octave:标示了关键点被找到的层级,总是希望在相同的层级找到对应的关键点
- class_id:标示关键点来自于哪一个目标
为了查找并计算描述符,Opencv 定义了如下抽象类。
class cv::Feature2D : public cv::Algorithm {
public:
virtual void detect(
cv::InputArray image, // Image on which to detect
vector< cv::KeyPoint >& keypoints, // Array of found keypoints
cv::InputArray mask = cv::noArray()
) const;
virtual void detect(
cv::InputArrayOfArrays images, // Images on which to detect
vector<vector< cv::KeyPoint > >& keypoints, // keypoints for each image
cv::InputArrayOfArrays masks = cv::noArray()
) const;
virtual void compute(
cv::InputArray image, // Image where keypoints are located
std::vector<cv::KeyPoint>& keypoints, // input/output vector of keypoints
cv::OutputArray descriptors); // computed descriptors, M x N matrix,
// where M is the number of keypoints
// and N is the descriptor size
virtual void compute(
cv::InputArrayOfArrays image, // Images where keypoints are located
std::vector<std::vector<cv::KeyPoint> >& keypoints, //I/O vec of keypnts
cv::OutputArrayOfArrays descriptors); // computed descriptors,
// vector of (Mi x N) matrices, where
// Mi is the number of keypoints in
// the i-th image and N is the
// descriptor size
virtual void detectAndCompute(
cv::InputArray image, // Image on which to detect
cv::InputArray mask, // Optional region of interest mask
std::vector<cv::KeyPoint>& keypoints, // found or provided keypoints
cv::OutputArray descriptors, // computed descriptors
bool useProvidedKeypoints = false); // if true,
// the provided keypoints are used,
// otherwise they are detected
virtual int descriptorSize() const; // size of each descriptor in elements
virtual int descriptorType() const; // type of descriptor elements
virtual int defaultNorm() const; // the recommended norm to be used
// for comparing descriptors.
// Usually, it's NORM_HAMMING for
// binary descriptors and NORM_L2
// for all others.
virtual void read(const cv::FileNode&);
virtual void write(cv::FileStorage&) const;
...
};
函数说明:
- detect:用于计算 Keypoint
- compute:用于计算 descriptor
- detectAndCompute:由于不同的关键点检测算法对于同一幅图像常常得到不同的结果,而且在算法计算过程中需要一种特殊的图像表示,其计算量很大,如果分开进行此步骤将重复进行两次,因此如果需要得到描述符,通常建议直接使用 detectAndCompute 函数
- descriptorSize:返回描述符向量的长度
- descriptorType:描述符元素的类型
- defaultNorm:描述符的归一化方法,指定了如何比较两个描述符,比如对于01描述符,可以使用 NORM_HAMMING;而对于 SIFT 和 SURF,则可以使用 NORM_L2 或者 NORM_L1。
一个实际的实现可以只实现其中的某个或几个方法:
- cv::Feature2D::detect():FAST
- cv::Feature2D::compute():FREAK
- cv::Feature2D::detectAndCompute():SIFT,SURF,ORB,BRISK。算法中将隐式调用检测和计算方法
cv::DMatch 类
通常,一个匹配器尝试在一副或一组图中匹配一幅图中的关键点,如果匹配成功,将返回 cv::DMatch 的列表。
class cv::DMatch {
public:
DMatch(); // sets this->distance
// to std::numeric_limits<float>::max()
DMatch(int _queryIdx, int _trainIdx, float _distance);
DMatch(int _queryIdx, int _trainIdx, int _imgIdx, float _distance);
int queryIdx; // query descriptor index
int trainIdx; // train descriptor index
int imgIdx; // train image index
float distance;
bool operator<(const DMatch &m) const; // Comparison operator
// based on 'distance'
}
成员说明:
- queryIdx, trainIdx:指定了每幅图像中关键点与关键点列表中元素的匹配情况。其中,默认 query image 为新图片,而 training image 为旧图片
- imgIdx:指定了匹配的图像
- distance:给出了匹配程度
- operator<():给定基于 distance 的比较方式
cv::DescriptorMatcher 抽象类
通常匹配器被应用在目标识别和跟踪两个场景中。其中目标识别需要我们训练匹配器——给出已知物体最大区分度的描述符,然后根据我们给出的描述符给出字典中哪个描述符与之相匹配。而跟踪则要求在给定两组描述符的条件下,给出它们之间的匹配情况。
cv::DescriptorMatcher 提供了 match(),knnMatch() 和 radiusMatch() 三个函数,其中每个函数都有针对目标检测和跟踪的两个版本,其中识别需要输入一个特性列表和训练好的字典,而跟踪则需输入两个特性列表。
class cv::DescriptorMatcher {
public:
virtual void add(InputArrayOfArrays descriptors); // Add train descriptors
virtual void clear(); // Clear train descriptors
virtual bool empty() const; // true if no descriptors
void train(); // Train matcher
virtual bool isMaskSupported() const = 0; // true if supports masks
const vector<cv::Mat>& getTrainDescriptors() const; // Get train descriptors
// methods to match descriptors from one list vs. "trained" set (recognition)
//
void match(
InputArray queryDescriptors,
vector<cv::DMatch>& matches,
InputArrayOfArrays masks = noArray()
);
void knnMatch(
InputArray queryDescriptors,
vector< vector<cv::DMatch> >& matches,
int k,
InputArrayOfArrays masks = noArray(),
bool compactResult = false
);
void radiusMatch(
InputArray queryDescriptors,
vector< vector<cv::DMatch> >& matches,
float maxDistance,
InputArrayOfArrays masks = noArray(),
bool compactResult = false
);
// methods to match descriptors from two lists (tracking)
//
// Find one best match for each query descriptor
void match(
InputArray queryDescriptors,
InputArray trainDescriptors,
vector<cv::DMatch>& matches,
InputArray mask = noArray()
) const;
// Find k best matches for each query descriptor (in increasing
// order of distances)
void knnMatch(
InputArray queryDescriptors,
InputArray trainDescriptors,
vector< vector<cv::DMatch> >& matches,
int k,
InputArray mask = noArray(),
bool compactResult = false
) const;
// Find best matches for each query descriptor with distance less
// than maxDistance
void radiusMatch(
InputArray queryDescriptors,
InputArray trainDescriptors,
vector< vector<cv::DMatch> >& matches,
float maxDistance,
InputArray mask = noArray(),
bool compactResult = false
) const;
virtual void read(const FileNode&); // Reads matcher from a file node
virtual void write(FileStorage&) const; // Writes matcher to a file storage
virtual cv::Ptr<cv::DescriptorMatcher> clone(
bool emptyTrainData = false
) const = 0;
static cv::Ptr<cv::DescriptorMatcher> create(
const string& descriptorMatcherType
);
...
};
函数说明:
- add:添加描述符,其中每个元素都是一个 Mat,每一行是一个描述符,列数是描述符的维数
- getTrainDescriptors:获得已添加的描述符
- clear,empty:清空和判断匹配器是否添加了描述符
- train:当加载完所有描述符时,通常需要运行 train 函数。它将基于使用的匹配方法生成指定数据结构,以便将来更高效地加速匹配过程。通常如果提供了 train 方法,必须在调用匹配方法之前调用 train 方法。
- match(), knnMatch(), and radiusMatch():用于目标识别的函数,其中 match() 方法只返回最优匹配,而 knnMatch() 将返回前 k 个最优匹配,radiusMatch 则返回所有距离小于指定距离的匹配。
- read,write:存储和加载匹配器,特别对于大型数据库,不用再保存所有图片
- clone,create:其中 emptyTrainData 标示是否使用原始训练数据,而 create 可接受如下方法的字符串
关键点滤波器
关键点滤波器用于从现有的关键点中查找更佳的关键点或者去除相同的关键点。
class cv::KeyPointsFilter {
public:
static void runByImageBorder(
vector< cv::KeyPoint >& keypoints, // in/out list of keypoints
cv::Size imageSize, // Size of original image
int borderSize // Size of border in pixels
);
static void runByKeypointSize(
vector< cv::KeyPoint >& keypoints, // in/out list of keypoints
float minSize, // Smallest keypoint to keep
float maxSize = FLT_MAX // Largest one to keep
);
static void runByPixelsMask(
vector< cv::KeyPoint >& keypoints, // in/out list of keypoints
const cv::Mat& mask // Keep where mask is nonzero
);
static void removeDuplicated(
vector< cv::KeyPoint >& keypoints // in/out list of keypoints
);
static void retainBest(
vector< cv::KeyPoint >& keypoints, // in/out list of keypoints
int npoints // Keep this many
);
}
函数说明:
- runByImageBorder():去除所有小于图像边缘大小的关键点,不过必须事先指定之前使用的 imageSize
- runByKeypointSize():去除所有小于 minSize 或者大于 maxSize 的关键点
- runByPixelsMask():去除所有 mask 中为零的关键点
- removeDuplicated():去除重复的关键点
- retainBest():去除关键点直到数量降为 npoints