最近赶时髦,看了一些智能优化算法如蚁群算法,还有机器学习的一些东西,就想着怎么把这些先进的东西用在图像配准中。头脑风暴了一下,觉得在已经检测到两幅图像的特征点的基础上,就如何对它们进行匹配似乎有优化的空间。
匹配的过程其实是找对应点的过程,对应点指的是在各自图像中相对位置一样的点,如图像进行刚性变换之后,图像点在坐标虽然改变了,但是它们之间的相对位置是不会变的,三角形的直角对应直角点,锐角对应锐角点。但是如何在对应点坐标之间建立关系呢,看代码的时候看到利用 cv::perspectiveTransform(dstPoints, dstPoints, homography.inv())将实验图像的特征点变换到参考图像同一个坐标系中,再进行欧式距离的计算,这个暗含了估计的变换矩阵是正确的,而且这其实是为了衡量变换矩阵估计准确性的标准,并不是为了得到匹配对。这才想起来其实不用太多考虑坐标、坐标系,因为我们检测得到特征点,更准确说是描述子,描述子具有旋转不变性。这个描述子已经把特征点的坐标、邻域的梯度(包括方向、大小)都包含了。得到描述子的过程就包含了将坐标轴旋转为关键点方向。所以直接比较128维描述子(对SIFT来说),就可以得到哪些特征点是对应的。这里又有两种策略,一种是穷举法https://docs.opencv.org/2.4/modules/features2d/doc/common_interfaces_of_descriptor_matchers.html#bfmatcher,一种是快速最近邻搜索包(Fast_Library_for_Approximate_Nearest_Neighbors)https://docs.opencv.org/2.4/modules/features2d/doc/common_interfaces_of_descriptor_matchers.html#flannbasedmatcher。因为描述子一般都是高维向量,所以要优化的话这会是一个值得关注的地方。
看看match具体是怎么实现的。之前博客中提到的图像配准算法比较对matchFeatures进行了重载:
void FeatureAlgorithm::matchFeatures(constDescriptors& train, const Descriptors& query, Matches& matches)const
{
matcher->match(query, train, matches);
}
void FeatureAlgorithm::matchFeatures(constDescriptors& train, const Descriptors& query, int k,std::vector<Matches>& matches) const//matchFeatures的重载,分别利用穷举和knn,参数有int型k就是knn
{
assert(knMatchSupported);
matcher->knnMatch(query, train, matches, k);
}
matcher是OpenCV中的feature2 dmodule中的cv::DescriptorMatcher。在feature2d中对https://docs.opencv.org/2.4/modules/features2d/doc/features2d.htmlmatch也进行了重载,这里matchFeatures用的是match的第一种变形,把train descriptors当做变量输入函数,并且利用穷举法搜索。matchFeatures的第二个重载用的是k近邻点算法,从query集合中的每个描述子中找到k个最好的匹配对,返回k个距离逐渐增大的匹配对(k通常不大于20)。这与通常意义上的匹配是不一样的,这里不是一一对应的,而是一对多。
KNN中是依靠周围K个已知类别点来判断待分类点的类别的,就像是物以类聚,这要求有一个先验知识,即一个数据和标签已知的训练集。k个近邻点中哪一类更多那么待判断点就更有可能属于那一类。
那么,具体在特征点匹配过程中如何使用KNN算法呢?特征点匹配和数据库查、图像检索本质上是同一个问题,都可以归结为一个通过距离函数在高维矢量之间进行相似性检索的问题,如何快速而准确地找到查询点的近邻,不少人提出了很多高维空间索引结构和近似查询的算法。https://blog.csdn.net/v_july_v/article/details/8203674k-d树(本质是一种平衡二叉树)就是对搜索空间进行不重叠划分,实现k近邻算法的一种方法。
结合Rob-Hess的源码和参考3,用建立大顶堆的思想实现数据中最小的k个数据的查找。即用容量为k的最大堆存储最小的k个数,此时,k1<k2<...<kmax(kmax设为大顶堆中最大元素)。遍历一次数列,n,每次遍历一个元素x,与堆顶元素比较,x<kmax,更新堆(用时logk),否则不更新堆。这样下来,总费时O(n*logk)。
那么这里还有一个问题就是在匹配过程中,我们只知道一系列高维数据,但是是对一个query在train中找距离最近的前k个,我们还需要先求query和所有train特征点之间的距离(若利用KD树的搜索策略,应该不会有重复计算)。距离的表示有很多种方法,参考2中也做了一番整理,常见的有欧式距离、曼哈顿距离(城市街区距离)、切比雪夫距离(国际象棋国王,坐标差最大值)、马氏距离(协方差距离,适合分类)、夹角余弦距离(根据向量方向重合度判断相似性,用于文本分类的词袋模型)、杰卡德相似系数(两个集合A和B的交集元素在A,B的并集中所占的比例)。距离的选择,一是要保证准确性,即保证不对应的特征点的距离尽可能大,相对应的特征点距离尽可能小,另一方面要使运算量小。Rob-Hess采用了欧式距离作为比较描述子的标准。对图像1中的每一个特征点利用BBF找到了最近邻的2个对应点,并分别计算出了它们之间的欧式距离。最近邻小于次近邻的0.49时,把最近邻作为对应点。
针对高维数据处理,有一个方法是PCA主成分分析。还有核机制?
Ng所举的例子是利用单幅图像重构了场景的3D模型。如果可以实现3D模型,那么我们就完全可以将图像配准到任何视角,而不是简单的透视变换。
还有一种思路是将特征点聚类,找到图像中的主体,即将主体内容和背景分离开来。将主体的中心点(数据中心化)和边界找出来。
Flann
Reference:
1.https://blog.csdn.net/icvpr/article/details/8491369
2.https://blog.csdn.net/v_july_v/article/details/8203674
3.https://blog.csdn.net/v_JULY_v/article/details/6370650