天天看點

openMVG源碼學習(二)main_ComputeFeaturesopenMVG源碼學習(二)main_ComputeFeatures

openMVG源碼學習(二)main_ComputeFeatures

這個學習筆記将使用openmvg作為第三方庫,官方文檔當中所推薦的恢複運動結構的方法,當中代碼的含義以及我在學習當中所發現的經驗。也歡迎大家來讨論!

先前文章:

連結: openMVG源碼學習(一)main_SfMInit_ImageListing

代碼運作前的準備

删除cmd相關的東西,反正這個在clion當中運作傳回會出錯,删除cmd的相關的東西也不會影響代碼的完整性。删除之後main函數傳參的參數,即int main()

之後,對代碼的結構進行整理,讓自己更加容易去看懂當中的邏輯安排。

删除下面這個頭檔案及其相關的代碼,因為我的mvg沒有找到檔案,且這個是SIFT描述子的頭檔案,反正就用不了,但是其他的描述子可以使用,且運作邏輯都一樣,故删除沒有影響。如果真的想用的話,openCV contrib歡迎使用奧(我至今沒有配置好。。。)有時候版權也是一個累贅是吧哈哈!

#include "nonFree/sift/SIFT_describer_io.hpp"
           

該源碼相對簡單,等同于其他開源庫的描述子的調用邏輯,如果還不知道什麼是描述子的話SIFT論文等等都可以給你一些答案,這裡不多說了。

是以本文在此不再呈現整個代碼邏輯,隻介紹幾個特殊的點。

資料與參數

std::string sSfM_Data_Filename;	 //main_SfMInit_ImageListing生成的檔案的路徑
std::string sOutDir = "";		//輸出檔案夾
bool bUpRight = false;			//AKAZE描述子使用,是否計算方向
std::string sImage_Describer_Method = "SIFT_ANATOMY";		//使用的描述子
bool bForce = false;		//
std::string sFeaturePreset = "";		//描述子品質NORMAL,HIGH,ULTRA
           

預設定

設定好描述子類型與描述子品質後

導出用于:動态未來區域計算和/或加載的所用圖像描述符和區域類型

std::ofstream stream(sImage_describer.c_str());
   if (!stream.is_open())
       return EXIT_FAILURE;

 cereal::JSONOutputArchive archive(stream);
 archive(cereal::make_nvp("image_describer", image_describer));
 auto regionsType = image_describer->Allocate();
 archive(cereal::make_nvp("regions_type", regionsType));
           

開始計算

對于每張圖檔

Views::const_iterator iterViews = sfm_data.views.begin();
        std::advance(iterViews, i);
        const View * view = iterViews->second.get();
           

建立兩個檔案,XXX.feat,XXX.desc

const std::string
                sView_filename = stlplus::create_filespec(sfm_data.s_root_path, view->s_Img_path),
                sFeat = stlplus::create_filespec(sOutDir, stlplus::basename_part(sView_filename), "feat"),
                sDesc = stlplus::create_filespec(sOutDir, stlplus::basename_part(sView_filename), "desc");

        // If features or descriptors file are missing, compute them
        if (!preemptive_exit && (bForce || !stlplus::file_exists(sFeat) || !stlplus::file_exists(sDesc)))
        {
            if (!ReadImage(sView_filename.c_str(), &imageGray))
                continue;
           

檢視是否有遮擋特征遮罩

Image<unsigned char> * mask = nullptr; // The mask is null by default

            const std::string
                    mask_filename_local =
                    stlplus::create_filespec(sfm_data.s_root_path,
                                             stlplus::basename_part(sView_filename) + "_mask", "png"),
                    mask__filename_global =
                    stlplus::create_filespec(sfm_data.s_root_path, "mask", "png");

            Image<unsigned char> imageMask;
            // 嘗試讀取本地掩碼
            if (stlplus::file_exists(mask_filename_local))
            {
                if (!ReadImage(mask_filename_local.c_str(), &imageMask))
                {
                    std::cerr << "Invalid mask: " << mask_filename_local << std::endl
                              << "Stopping feature extraction." << std::endl;
                    preemptive_exit = true;
                    continue;
                }
                // 僅當本地遮罩符合目前圖像大小時使用
                if (imageMask.Width() == imageGray.Width() && imageMask.Height() == imageGray.Height())
                    mask = &imageMask;
            }
            else
            {
                // 嘗試讀取全局掩碼
                if (stlplus::file_exists(mask__filename_global))
                {
                    if (!ReadImage(mask__filename_global.c_str(), &imageMask))
                    {
                        std::cerr << "Invalid mask: " << mask__filename_global << std::endl
                                  << "Stopping feature extraction." << std::endl;
                        preemptive_exit = true;
                        continue;
                    }
                    // 僅當全局遮罩适合目前圖像大小時才使用它
                    if (imageMask.Width() == imageGray.Width() && imageMask.Height() == imageGray.Height())
                        mask = &imageMask;
                }
            }
           

計算特征和描述符并将它們導出到檔案中(核心)

auto regions = image_describer->Describe(imageGray, mask);
            if (regions && !image_describer->Save(regions.get(), sFeat, sDesc)) {
                std::cerr << "Cannot save regions for images: " << sView_filename << std::endl
                          << "Stopping feature extraction." << std::endl;
                preemptive_exit = true;
                continue;
            }
        }
           

openMP

即是否使用多線程?

不過慎用,本人電腦12線程32g記憶體,處理每張3000*4000的圖檔時會調用虛拟記憶體,降低效率,又因為,源碼中進度條存在時,當目前所有線程完成本圖檔的處理之後,才會統一調用,下一組圖像進行計算,記憶體峰值過高,可以在此作優化,比如c++17的多線程可以解決記憶體阻塞的效果,同時需要預計記憶體使用量,防止爆記憶體,得不償失。