openMVG源碼學習(三)main_ComputeMatches
之前的連結:
openMVG源碼學習(二)main_ComputeFeatures
openMVG源碼學習(一)main_SfMInit_ImageListing.
代碼前的準備
删除cmd相關
更改代碼書寫習慣。。。
前言少序,直接開始。
輸入輸出說明
//名稱确定
std::string sSfM_Data_Filename;
//輸出檔案夾
std::string sMatchesDirectory = "";
//s幾何模型
std::string sGeometricModel = "f";
//enum EGeometricModel
//{
// FUNDAMENTAL_MATRIX = 0,基本矩陣
// ESSENTIAL_MATRIX = 1,本質矩陣
// HOMOGRAPHY_MATRIX = 2,單應矩陣
// ESSENTIAL_MATRIX_ANGULAR = 3,基本矩陣角
// ESSENTIAL_MATRIX_ORTHO = 4,基本正交矩陣
// ESSENTIAL_MATRIX_UPRIGHT = 5基本矩陣直立
//};
//f距離比(後面解釋)
float fDistRatio = 0.8f;
//配對模型
int iMatchingVideoMode = -1;
//enum EPairMode
//{
// PAIR_EXHAUSTIVE = 0,全部配對
// PAIR_CONTIGUOUS = 1,相鄰配對
// PAIR_FROM_FILE = 2從檔案選擇配對方法
//};
//從檔案的比對檔案的路徑
std::string sPredefinedPairList = "";
//比對器
std::string sNearestMatchingMethod = "AUTO";
//是否重新全部配對,不使用之前已經計算出的成果
bool bForce = false;
//函數Robust_model_estimation中講解
bool bGuided_matching = false;
//funtor GeometricFilter_FMatrix_AC中講解
int imax_iteration = 2048;
//0->預設區域提供程式(在記憶體中加載和存儲所有區域)
//other->緩存區域提供程式(按需加載和存儲區域)
unsigned int ui_max_cache_size = 0;
資料檢查與指派
比較簡單,字面意思,不做解釋
if (sPredefinedPairList.length())
...
if (sMatchesDirectory.empty() || !stlplus::is_folder(sMatchesDirectory))
...
EGeometricModel eGeometricModelToCompute = FUNDAMENTAL_MATRIX;
std::string sGeometricMatchesFilename = "";
switch (sGeometricModel[0])
...
正式流程
a.計算好的描述子比對
加載SfM_Data
SfM_Data sfm_data;
if (!Load(sfm_data, sSfM_Data_Filename, ESfM_Data(VIEWS|INTRINSICS)))
{
std::cerr << std::endl
<< "The input SfM_Data file \""<< sSfM_Data_Filename << "\" cannot be read." << std::endl;
return EXIT_FAILURE;
}
比對的描述子初始化
using namespace openMVG::features;
const std::string sImage_describer = stlplus::create_filespec(sMatchesDirectory, "image_describer", "json");
std::unique_ptr<Regions> regions_type = Init_region_type_from_file(sImage_describer);
if (!regions_type)
{
std::cerr << "Invalid: "
<< sImage_describer << " regions type file." << std::endl;
return EXIT_FAILURE;
}
開始進行比對,比對方式有三種,第一部分有說明
//加載相應的視圖區域
std::shared_ptr<Regions_Provider> regions_provider;
//Regions_Provider抽象區域提供程式允許加載和傳回與視圖相關的區域
if (ui_max_cache_size == 0)
//預設區域提供程式(在記憶體中加載和存儲所有區域)
regions_provider = std::make_shared<Regions_Provider>();
else
//緩存區域提供程式(按需加載和存儲區域)
regions_provider = std::make_shared<Regions_Provider_Cache>(ui_max_cache_size);
讀取區域類型
C_Progress_display progress;
if (!regions_provider->load(sfm_data, sMatchesDirectory, regions_type, &progress))
{
std::cerr << std::endl << "Invalid regions." << std::endl;
return EXIT_FAILURE;
}
讀取圖檔的基本資訊名稱以及寬高
//從SfM資料視圖資料生成一些别名:
//-将視圖列為檔案名和圖像大小的矢量
std::vector<std::string> vec_fileNames;
std::vector<std::pair<size_t, size_t>> vec_imagesSize;
{
//reserve預留白間
vec_fileNames.reserve(sfm_data.GetViews().size());
vec_imagesSize.reserve(sfm_data.GetViews().size());
for (Views::const_iterator iter = sfm_data.GetViews().begin();
iter != sfm_data.GetViews().end();
++iter)
{
const View * v = iter->second.get();
vec_fileNames.push_back(stlplus::create_filespec(sfm_data.s_root_path,
v->s_Img_path));
vec_imagesSize.push_back( std::make_pair( v->ui_width, v->ui_height) );
}
}
判斷比對類型,下圖為PairWiseMatches資料結構
提供了三種圖檔比對方式:
全部、相鄰、從檔案;
以及7種描述子比對方式:
AUTO、BRUTEFORCEL2、BRUTEFORCEHAMMING、HNSWL2、
ANNL2、CASCADEHASHINGL2、FASTCASCADEHASHINGL2
這段代碼是,讀取之前處理好的,以及選擇圖檔比對方式,以及描述子比對方式,還沒有開始計算。
PairWiseMatches map_PutativesMatches;
std::cout << std::endl << " - PUTATIVE MATCHES - " << std::endl;
//如果比對項已經存在,請重新加載它們,使用bForce開啟是否重新加載
if (!bForce
&& (stlplus::file_exists(sMatchesDirectory + "/matches.putative.txt")
|| stlplus::file_exists(sMatchesDirectory + "/matches.putative.bin"))
)
{
if (!(Load(map_PutativesMatches, sMatchesDirectory + "/matches.putative.bin") ||
Load(map_PutativesMatches, sMatchesDirectory + "/matches.putative.txt")) )
{
std::cerr << "Cannot load input matches file";
return EXIT_FAILURE;
}
std::cout << "\t PREVIOUS RESULTS LOADED;"
<< " #pair: " << map_PutativesMatches.size() << std::endl;
}
else // Compute the putative matches
{
std::cout << "Use: ";
switch (ePairmode)
{
case PAIR_EXHAUSTIVE: std::cout << "exhaustive pairwise matching" << std::endl; break;
case PAIR_CONTIGUOUS: std::cout << "sequence pairwise matching" << std::endl; break;
case PAIR_FROM_FILE: std::cout << "user defined pairwise matching" << std::endl; break;
}
//Allocate the right Matcher according the Matching requested method
//根據比對請求的方法配置設定正确的比對器
//Matcher一個圖像采集比對器的實作計算一組圖檔之間的假定比對
std::unique_ptr<Matcher> collectionMatcher;
if (sNearestMatchingMethod == "AUTO")
{
//BRUTE_FORCE_L2,蠻力L2,
//ANN_L2,安L2,
//CASCADE_HASHING_L2,級聯哈希L2,
//HNSW_L2,
//BRUTE_FORCE_HAMMING暴力漢明
if (regions_type->IsScalar())//标量
{
std::cout << "Using FAST_CASCADE_HASHING_L2 matcher" << std::endl;
//reset智能指針指向的新對象
//從SfM資料視圖資料建構一些别名:将視圖列為檔案名和圖像大小的向量
//
//Cascade_Hashing_Matcher_Regions一個圖像采集比對器的實作計算一組圖檔之間的
//假定比對通過使用兩個最近鄰居的距離比的門檻值丢棄虛假的對應。
//使用級聯哈希比對級聯哈希表計算一次并用于所有區域。
collectionMatcher.reset(new Cascade_Hashing_Matcher_Regions(fDistRatio));
}
else
if (regions_type->IsBinary())//二進制的
{
std::cout << "Using BRUTE_FORCE_HAMMING matcher" << std::endl;
//Matcher_Regions
//一個圖像采集比對器的實作計算一組圖檔之間的假定比對通過使用兩個最近鄰居的距離比的門檻值丢棄虛假的對應。
collectionMatcher.reset(new Matcher_Regions(fDistRatio, BRUTE_FORCE_HAMMING));
}
}
else
if (sNearestMatchingMethod == "BRUTEFORCEL2")
{
std::cout << "Using BRUTE_FORCE_L2 matcher" << std::endl;
collectionMatcher.reset(new Matcher_Regions(fDistRatio, BRUTE_FORCE_L2));
}
else
if (sNearestMatchingMethod == "BRUTEFORCEHAMMING")
{
std::cout << "Using BRUTE_FORCE_HAMMING matcher" << std::endl;
collectionMatcher.reset(new Matcher_Regions(fDistRatio, BRUTE_FORCE_HAMMING));
}
else
if (sNearestMatchingMethod == "HNSWL2")
{
std::cout << "Using HNSWL2 matcher" << std::endl;
collectionMatcher.reset(new Matcher_Regions(fDistRatio, HNSW_L2));
}
else
if (sNearestMatchingMethod == "ANNL2")
{
std::cout << "Using ANN_L2 matcher" << std::endl;
collectionMatcher.reset(new Matcher_Regions(fDistRatio, ANN_L2));
}
else
if (sNearestMatchingMethod == "CASCADEHASHINGL2")
{
std::cout << "Using CASCADE_HASHING_L2 matcher" << std::endl;
collectionMatcher.reset(new Matcher_Regions(fDistRatio, CASCADE_HASHING_L2));
}
else
if (sNearestMatchingMethod == "FASTCASCADEHASHINGL2")
{
std::cout << "Using FAST_CASCADE_HASHING_L2 matcher" << std::endl;
collectionMatcher.reset(new Cascade_Hashing_Matcher_Regions(fDistRatio));
}
if (!collectionMatcher)
{
std::cerr << "Invalid Nearest Neighbor method: " << sNearestMatchingMethod << std::endl;
return EXIT_FAILURE;
}
這裡才開始正式計算,以及進行儲存,以及比對表示圖,資料裡面那個三角一樣的圖,越密集則BA越困難。
// Perform the matching
system::Timer timer;
{
// From matching mode compute the pair list that have to be matched:
Pair_Set pairs;
switch (ePairmode)
{
case PAIR_EXHAUSTIVE: pairs = exhaustivePairs(sfm_data.GetViews().size()); break;
case PAIR_CONTIGUOUS: pairs = contiguousWithOverlap(sfm_data.GetViews().size(), iMatchingVideoMode); break;
case PAIR_FROM_FILE:
if (!loadPairs(sfm_data.GetViews().size(), sPredefinedPairList, pairs))
{
return EXIT_FAILURE;
}
break;
}
// Photometric matching of putative pairs
//假設對的照片比對
collectionMatcher->Match(regions_provider, pairs, map_PutativesMatches, &progress);
//---------------------------------------
//-- Export putative matches導出假定比對
//---------------------------------------
if (!Save(map_PutativesMatches, std::string(sMatchesDirectory + "/matches.putative.bin")))
{
std::cerr
<< "Cannot save computed matches in: "
<< std::string(sMatchesDirectory + "/matches.putative.bin");
return EXIT_FAILURE;
}
}
std::cout << "Task (Regions Matching) done in (s): " << timer.elapsed() << std::endl;
}
//-- export putative matches Adjacency matrix
//導出假定比對鄰接矩陣
PairWiseMatchingToAdjacencyMatrixSVG(vec_fileNames.size(),
map_PutativesMatches,
stlplus::create_filespec(sMatchesDirectory, "PutativeAdjacencyMatrix", "svg"));
//-- export view pair graph once putative graph matches have been computed
//一旦計算出假定的圖比對,就導出視圖對圖
{
std::set<IndexT> set_ViewIds;
std::transform(sfm_data.GetViews().begin(), sfm_data.GetViews().end(),
std::inserter(set_ViewIds, set_ViewIds.begin()), stl::RetrieveKey());
graph::indexedGraph putativeGraph(set_ViewIds, getPairs(map_PutativesMatches));
graph::exportToGraphvizData(
stlplus::create_filespec(sMatchesDirectory, "putative_matches"),
putativeGraph);
}
b.計算裡程計
有了圖像之間描述子的比對,按測繪的角度來說就可以後方交會了,這裡便是計算裡程計的流程,并且提供了濾波去除了部分粗差。
這裡ImageCollectionGeometricFilter類承擔了計算裡程計的任務,提供了6種方法進行計算:
HOMOGRAPHY_MATRIX、FUNDAMENTAL_MATRIX、ESSENTIAL_MATRIX、
ESSENTIAL_MATRIX_ANGULAR、ESSENTIAL_MATRIX_ORTHO、ESSENTIAL_MATRIX_UPRIGHT
各種方式的優點缺點見論文哦
//ImageCollectionGeometricFilter
//隻允許保持幾何一緻的比對
//它丢棄了不能産生有效魯棒模型估計的對
std::unique_ptr<ImageCollectionGeometricFilter> filter_ptr(
new ImageCollectionGeometricFilter(&sfm_data, regions_provider));
if (filter_ptr)
{
system::Timer timer;
const double d_distance_ratio = 0.6;
PairWiseMatches map_GeometricMatches;
switch (eGeometricModelToCompute)
{
case HOMOGRAPHY_MATRIX:
{
const bool bGeometric_only_guided_matching = true;
filter_ptr->Robust_model_estimation(
GeometricFilter_HMatrix_AC(4.0, imax_iteration),
map_PutativesMatches, bGuided_matching,
bGeometric_only_guided_matching ? -1.0 : d_distance_ratio, &progress);
map_GeometricMatches = filter_ptr->Get_geometric_matches();
}
break;
case FUNDAMENTAL_MATRIX:
{
filter_ptr->Robust_model_estimation(
GeometricFilter_FMatrix_AC(4.0, imax_iteration),
map_PutativesMatches, bGuided_matching, d_distance_ratio, &progress);
map_GeometricMatches = filter_ptr->Get_geometric_matches();
}
break;
case ESSENTIAL_MATRIX:
{
filter_ptr->Robust_model_estimation(
GeometricFilter_EMatrix_AC(4.0, imax_iteration),
map_PutativesMatches, bGuided_matching, d_distance_ratio, &progress);
map_GeometricMatches = filter_ptr->Get_geometric_matches();
//-- Perform an additional check to remove pairs with poor overlap
//執行附加檢查以移除重疊不良的對
std::vector<PairWiseMatches::key_type> vec_toRemove;
for (const auto & pairwisematches_it : map_GeometricMatches)
{
const size_t putativePhotometricCount = map_PutativesMatches.find(pairwisematches_it.first)->second.size();
const size_t putativeGeometricCount = pairwisematches_it.second.size();
const float ratio = putativeGeometricCount / static_cast<float>(putativePhotometricCount);
if (putativeGeometricCount < 50 || ratio < .3f)
{
// the pair will be removed
vec_toRemove.push_back(pairwisematches_it.first);
}
}
//-- remove discarded pairs
for (const auto & pair_to_remove_it : vec_toRemove)
{
map_GeometricMatches.erase(pair_to_remove_it);
}
}
break;
case ESSENTIAL_MATRIX_ANGULAR:
{
filter_ptr->Robust_model_estimation(
GeometricFilter_ESphericalMatrix_AC_Angular<false>(4.0, imax_iteration),
map_PutativesMatches, bGuided_matching, d_distance_ratio, &progress);
map_GeometricMatches = filter_ptr->Get_geometric_matches();
}
break;
case ESSENTIAL_MATRIX_ORTHO:
{
filter_ptr->Robust_model_estimation(
GeometricFilter_EOMatrix_RA(2.0, imax_iteration),
map_PutativesMatches, bGuided_matching, d_distance_ratio, &progress);
map_GeometricMatches = filter_ptr->Get_geometric_matches();
}
break;
case :
{
filter_ptr->Robust_model_estimation(
GeometricFilter_ESphericalMatrix_AC_Angular<true>(4.0, imax_iteration),
map_PutativesMatches, bGuided_matching, d_distance_ratio, &progress);
map_GeometricMatches = filter_ptr->Get_geometric_matches();
}
break;
}
下面便是對裡程點檔案的導出儲存
//---------------------------------------
//-- Export geometric filtered matches
//導出幾何過濾比對
//---------------------------------------
if (!Save(map_GeometricMatches,
std::string(sMatchesDirectory + "/" + sGeometricMatchesFilename)))
{
std::cerr
<< "Cannot save computed matches in: "
<< std::string(sMatchesDirectory + "/" + sGeometricMatchesFilename);
return EXIT_FAILURE;
}
std::cout << "Task done in (s): " << timer.elapsed() << std::endl;
// -- export Geometric View Graph statistics
//導出幾何視圖圖形統計資訊
graph::getGraphStatistics(sfm_data.GetViews().size(), getPairs(map_GeometricMatches));
//-- export Adjacency matrix
//導出鄰接矩陣
std::cout << "\n Export Adjacency Matrix of the pairwise's geometric matches"
<< std::endl;
PairWiseMatchingToAdjacencyMatrixSVG(vec_fileNames.size(),
map_GeometricMatches,
stlplus::create_filespec(sMatchesDirectory, "GeometricAdjacencyMatrix", "svg"));
//-- export view pair graph once geometric filter have been done
// 完成幾何過濾後導出視圖對圖
{
std::set<IndexT> set_ViewIds;
std::transform(sfm_data.GetViews().begin(), sfm_data.GetViews().end(),
std::inserter(set_ViewIds, set_ViewIds.begin()), stl::RetrieveKey());
graph::indexedGraph putativeGraph(set_ViewIds, getPairs(map_GeometricMatches));
graph::exportToGraphvizData(
stlplus::create_filespec(sMatchesDirectory, "geometric_matches"),
putativeGraph);
}
重要函數說明
函數Robust_model_estimation
template<typename GeometryFunctor>
void ImageCollectionGeometricFilter::Robust_model_estimation
(
const GeometryFunctor & functor,
//functor文中使用的是GeometricFilter_HMatrix_AC等等(後面講解)
const PairWiseMatches & putative_matches,
//PairWiseMatches繼承于std::map<Pair, IndMatches>
//IndMatches是std::vector<matching::IndMatch>
//matching::IndMatch是結構以儲存成對索引的引用,存在排序運算符以删除IndMatch序列的重複項。
//也就是放入比對點的資料結構
const bool b_guided_matching,
const double d_distance_ratio,
C_Progress * my_progress_bar
//進度條
)
{
if (!my_progress_bar)
my_progress_bar = &C_Progress::dummy();
my_progress_bar->restart( putative_matches.size(), "\n- Geometric filtering -\n" );
#ifdef OPENMVG_USE_OPENMP
#pragma omp parallel for schedule(dynamic)
#endif
for (int i = 0; i < (int)putative_matches.size(); ++i)
{
if (my_progress_bar->hasBeenCanceled())
continue;
auto iter = putative_matches.begin();
advance(iter,i);
Pair current_pair = iter->first;
const std::vector<IndMatch> & vec_PutativeMatches = iter->second;
//-- Apply the geometric filter (robust model estimation)
{
IndMatches putative_inliers;
GeometryFunctor geometricFilter = functor; // use a copy since we are in a multi-thread context
if (geometricFilter.Robust_estimation(
sfm_data_,
regions_provider_,
iter->first,
vec_PutativeMatches,
putative_inliers))
{
if (b_guided_matching)
{
IndMatches guided_geometric_inliers;
geometricFilter.Geometry_guided_matching(
sfm_data_,
regions_provider_,
iter->first,
d_distance_ratio,
guided_geometric_inliers);
//std::cout
// << "#before/#after: " << putative_inliers.size()
// << "/" << guided_geometric_inliers.size() << std::endl;
std::swap(putative_inliers, guided_geometric_inliers);
}
#ifdef OPENMVG_USE_OPENMP
#pragma omp critical
#endif
{
_map_GeometricMatches.insert( {current_pair, std::move(putative_inliers)});
}
}
}
++(*my_progress_bar);
}
}