main_featureMatching.cpp→aliceVision_featureMatching
- 【90-253】设置输入参数,并对输入进行configure
- 【227-229】只支持LORANSAC和ACRANSAC两种方法
- 【237】随机数生成https://blog.csdn.net/weixin_43919932/article/details/119994136 https://www.jianshu.com/p/6d9a7de995bb
- 【263-267】与之前一样,从之前的cameraInit.sfm读取sfmData
- 【280-301】读取上一步获取的pair
- boost::trim: http://www.manongjc.com/detail/31-meslxdfshasfaig.html
- std::stringstream::str: https://cplusplus.com/reference/sstream/stringstream/str/
- 【304-308】将所有出现在pair里面的id存到filter里面
- 【313-328】载入之前提取的feature,描述子到regoinPerview
- std::unique: https://en.cppreference.com/w/cpp/algorithm/unique
- boost::timer boost::progress_display https://www.cnblogs.com/visayafan/archive/2012/04/30/2476616.html
- std::atomic: https://blog.csdn.net/zyhse/article/details/105470205
- 【213】loadRegions
-
【53-54】
std::unique_ptrfeature::Regions regionsPtr;
imageDescriber.allocate(regionsPtr);
这里面allocate是reset,把regionPtr,重新指向新对象,imageDescriber代表的类FeatDescRegions
-
std::istream_iteratorhttps://en.cppreference.com/w/cpp/iterator/istream_iterator
-
- 【218】unique_ptr.release http://t.zoukankan.com/zengtx-p-11911853.html
- 【218】addRegions:regionsPerView里面存放的类型是MapRegionsPerView = std::map<IndexT, MapRegionsPerDesc> = std::map<IndexT, std::map<feature::EImageDescriberType, std::unique_ptrfeature::Regions>>第一个index表示的是viewId,第二个表示的是描述子的类型
- 【335-352】看这些pair的位姿是否有定义
- 【354-361】有位姿情况下的匹配方式
- match函数
- 对于每个pair,先获取对应的位姿,相机内参(如果不是pinhole就不行)
- 【118-119】计算相机投影矩阵乘以位姿
- 【121】计算基本矩阵https://blog.csdn.net/weixin_45485946/article/details/125493895 https://blog.csdn.net/u011089570/article/details/79040948
- 【122】获得每个pair共有的描述子类型
- map里面的counthttps://www.nhooo.com/note/qa02hr.html
- 【124】allImagePairMatches是一个map,索引是描述子类型,成员是std::vectormatching::IndMatch,matching::IndMatch三个成员,其中两个表示这两个id所表示的描述子是相近的,第三个成员函数表示有多近(可以看后续代码离着这个成员)。每个view都有一个allImagePairMatches,表示这个view,每个描述子类型下,图中每个描述子和其最相似的描述子编号。
- 【142】左侧相机在右侧相机图像中的位置
- 【145】guidedMatchingFundamentalFast,参考Rajvi Shah, Vanshika Shrivastava, and P J Narayanan Geometry-aware Feature Matching for Structure from Motion Applications.
- 其最简单的实现就是对比pair里面每个描述子,可以参考函数guidedMatching
- 通过极线约束,可以加快上面方法的速度。
- 【390-392】创立一个buckets,用来存放极线与图像四个边框交点,buckets是个vector,从图像原点开始,顺时针方向增长(可以看函数pix_to_bucket)
- 【399】算出极线(基本矩阵乘以左图的点,得到应该是直线参数ab
- c)
- 【402】找到极线与图像四个边框的交点:line_to_endPoints
- 【32,49】??判断极线是否在右侧图像上??为什么极线不能平行坐标轴(a=0或者b=0?)
- 先假设这里可以判断出极线就在右侧图像上
- ???剩下的看的都不是很懂,比如极线的搜索范围的设定。应该就是找到每个左侧描述子对应的最像的右侧描述子
- match函数
- 无位姿情况下的匹配过程
- (ANN_L2)
- 【49-54】把pair重新放到一个map里面,原文说目的是Sort pairs according the first index to minimize the MatcherT build operations
- 【71】创建matcher
- 构造函数里面有一个createRegionsMatcher,会根据matcherType和特征是scalar还是binary为成员_regionsMatcher创建不同的值
- 以ANN_L2为例,分配了RegionsMatcher<ArrayMatcher_kdtreeFlann>
- 里面有个成员ArrayMatcher_kdtreeFlann matcher_,在构造函数时候会调用一个函数Build
- 具体可以看flann官方文档和翻译版本:https://blog.csdn.net/weixin_45687825/article/details/110881552
- 一个_datasetM参数用来存放数据,输入数据就用矩阵0行0列的地址就可以,一个_index用来构建kd树和索引
- 【96】根据上面的_regionsMatcher类型,进行match,细节在RegionsMatcher.cpp这个文件里面
- 这里的match输入的query是另一个region的所有描述子,对每一个描述子都要查找离他最近的NNN_=2的邻居
- 【140】同上,获得查找的矩阵0行0列的地址
- 【147】SearchNeighbours,到ArrayMatcher_kdtreeFlann.hpp这个文件
- 【110】看之前的flann的kdtree初始化了没
- 【113-119】构建距离和索引矩阵,应为一个regions里面每个描述子个数都要查,所以共有描述子个数行,NNN_列
- 【126】开始查询
- 【130-139】将查找结果保存
- 【158-164】查找的第一个结果和第二个结果距离要差的足够大才可以
- 【166-175】符合结果的pair保存
- 【90-115】重复上面的步骤,只不过把左右view切换,看得到结果是否一样
- 【120-125】保存这个pair,这个描述子类型下,结果相同描述子匹配matches
- (FAST_CASCADE_HASHING_L2)
- 不同的nearestMatchingMethod,匹配方法不同,(基类IImageCollectionMatcher),后文以ANN_L2作为例子
- 先看mapPutativesMatches的类型:PairwiseMatches=std::map<Pair, MatchesPerDescType> = std::map<Pair, std::map<feature::EImageDescriberType, std::vectormatching::IndMatch> 以单个pair做索引,成员是(一个索引是描述子类型,成员是该pair下的所有描述子对)的一个map
- 【228】调出第一个view下的,对应描述子类型的regions(基类,实际是包含该view下所有特征和描述子的FeatDescRegions类)
- 【233】typeid:https://www.geeksforgeeks.org/typeid-operator-in-c-with-examples/
- (剩下的先跳过)
- (ANN_L2)
- 【383-388】检查匹配结果是否是空的
- 【390-403】如果geometricFilterType是HOMOGRAPHY_GROWING的话,需要对前面得到的匹配的分数(第一近的距离除以第二近的距离,越小越好)进行排序
- 【462-545】根据不同的geometricFilterType进行filter,下文FUNDAMENTAL_MATRIX为例
- omp parallel for schedule https://blog.csdn.net/drzhouweiming/article/details/1844762
- dynamic_cast https://blog.csdn.net/weixin_44212574/article/details/89043854
- uniform_int_distribution https://blog.csdn.net/Andyooper/article/details/89314678
- 【471】robustModelEstimation函数,里面的参数GeometricFilterMatrix_F_AC是一个拟函数(Functor)表示使用的方法是Fundamental matrix+ACransac方法
- 【70】geometricEstimation函数,以GeometricFilterMatrix_F_AC.hpp为例
- 【100】同之前一样,获得左右图相同类型的描述子
- 【107】fillMatricesWithUndistortFeaturesMatches函数获得左右图特征点其原始undistort的点,注意这边把所有描述子类型的点全放在一起了
- 【112】判断是否是鱼眼类型的相机 dynamic_cast https://blog.csdn.net/weixin_44212574/article/details/89043854
- 【130】geometricEstimation_Mat_ACRANSAC 模板Fundamental7PSolver表示用7点求基本矩阵 Mat3Model表示基本矩阵
- 【269】RelativePoseKernel
- 里面FundamentalEpipolarDistanceError的error是Sampson error,参考书Multiple View Geometry in Computer Vision
- 先看4.1,里面讲了单应性矩阵
- 再看4.2.1,里面讲了代数误差,就是误差的模
- 再看4.2.2,讲了几何误差,指的是投影过去的点和匹配点的距离
- 4.2.3,重投影误差,指的是投影过去+投影过来的误差和
- 4.2.5,几何误差的几何解释,引入变量V,和4维空间,V在这个四维空间里面就是两个超平面的相交
- 4.2.6 Sampson error,可以参考https://blog.csdn.net/weixin_39461878/article/details/106457174和https://www.zhihu.com/question/38586401
- 参数UnnormalizerT则表示将匹配点对们进行归一化,参考书的4.4.4
- 初始化过程:RelativePoseKernel.hpp
- 【54-55】归一化过程,可以自己对照程序,拿一个1010图片和10010图片做例子来理解这个归一化
- 【63】_logalpha0,参考ransac里面的误差项前面的归一项
- 里面FundamentalEpipolarDistanceError的error是Sampson error,参考书Multiple View Geometry in Computer Vision
- 【283】ACRANSAC 原理看https://zhuanlan.zhihu.com/p/483852907 + https://blog.csdn.net/u012348774/article/details/79802957 + 原文1.2 Automatic Homographic Registration of a Pair of Images, with A Contrario Elimination of Outliers http://www.ipol.im/pub/art/2012/mmm-oh/article.pdf
- 大致原理
- 在匹配点中抽取若干个点(比如在这里七点法求F,就取7个点)
- 解出模型(F的值)
- 算出剩下点的残差(sampson距离),从小到大排列
- 根据排列依次加入之前的若干点,求NFA,然后保留最小的NFA对应的点
- 重复多次
- 【168】std::itoa https://cplusplus.com/reference/numeric/iota/
- 【173】生成 ( n l ) \binom{n}{l} (ln) l = 0 , 1... n l=0,1...n l=0,1...n 和 ( l k ) \binom{l}{k} (kl) l = 0 , 1... n l=0,1...n l=0,1...n
- 【188-192】随机选择7个点的索引到vec_sample里
- 【195】根据这7个点,解出F
- RelativePoseKernel.hpp 【143-144】根据之前的索引获得对应的点
- 【146】开始解F Fundamental7PSolver.cpp
- 【32-33】这边的A参考 SLAM14里面169页本质矩阵的公式7.13
- 【41】获得A矩阵的零空间 https://mp.weixin.qq.com/s?__biz=MzIyNDM4ODI0OA==&mid=2247494781&idx=1&sn=48a47378757459ae7c4d41d394293346&chksm=e80d198adf7a909c8a81e3e1f64130014d82bd50d514e7684b916d9d0398430f23706262ce9c&mpshare=1&scene=1&srcid=0823e31qHucLS1q63ZkwujcL&sharer_sharetime=1661246325886&sharer_shareid=f78c1a74ecb0a6bc52771898c7f29a5d#rd 矩阵之芯 SVD - 从奇异值分解看四个基本子空间 + 书281页
- 【62-93】找去零空间中的F,利用F是奇异矩阵的特性(slam14 P168页讲了E是,那么F也是),使得F的行列式是0,就可以得到F。这边行列式为0,转成求解一元三次方程
- 【202】求Sampson error
- 【204-213】如果设置了阈值,那就先筛选并判断阈值内点够吗
- 【225-233】上面讲的ACRANSAC第4步
- 【235-252】如果找到就复制出inlier
- 【258】找不到合适的model
- 【262】对应找了model和循环已经结束的情况
- 【264-268】找不到,继续找
- 【272-277】找到了,在找到的模型中选取点再来一次
- 【289】将F unnormalize,参考上文
- 返回,得到内点
- 大致原理
- 【269】RelativePoseKernel
- 【160】copyInlierMatches函数,由于【107】把所有描述子类型点放进去了,因此需要根据index对应的类型
- 【163】计算(在sift例子下)0.14*内点数是否大于7(如果是求F)
- guidedmatching:之前得到了F,对于一个pair,两个图片下的特征点,利用F,得到一个error(guidedMatching.hpp 【260】),如果小于阈值,计算这两个点之间的距离,和之前一样(第一近的距离除以第二近的距离,越小越好),小于阈值这加入matches
- 【70】geometricEstimation函数,以GeometricFilterMatrix_F_AC.hpp为例
- 如此,就得到最终的matches了
- 【556-606】gridFiltering,如果设置了最大特征点数目numMatchesToKeep 则需要选取的特征点足够分散。将图片划分成一个网格,类似于亚像素,然后依次,每个网格提取一个(每个网格可能有多个特征点,这样就可以分散特征点了)
- 【577】将特征点按特征点的scale从大到小排序
- 【582】matchesGridFiltering:(默认的是3*3网格,以此为例)
- 【77-80】算出每一个网格的长宽
- 【82】左右图各保留3*3的网格空间,因此乘以2
- 【84-87】假设有90个特征点,则每个网格可以储存5个。因为有左右两图,有9个网格,所以一共存储529=90个
- 【91-103】根据x y 得到grid的坐标,【99-100】代码应该有错,最大值可以设置gridSize*gridSize-1
- 【105-112】先把match储存在前面,如果有重复则放后面
- 【129-139】精华部分,注意里面的循环依次扫描网格,最外层的是每个网格里面匹配点的编号,也就是前面说的:然后依次,每个网格提取一个(每个网格可能有多个特征点,这样就可以分散特征点了)
- 【609】保存match
- 主要函数saveTxt,先保存pair的编号,再保存pair有几种类型的描述子,之后是这个类型名称和这个类型下描述子个数,最后就是这些匹配点的编号