【三维重建】openMVG中的sfmInit_ImageListing.cpp梳理

这个函数主要的作用是读入图像,产生数据

sfmInit_ImageListing.cpp

#include "openMVG/cameras/cameras.hpp"
#include "openMVG/exif/exif_IO_EasyExif.hpp"
#include "openMVG/exif/sensor_width_database/ParseDatabase.hpp"
#include "openMVG/geodesy/geodesy.hpp"
#include "openMVG/image/image_io.hpp"
#include "openMVG/numeric/eigen_alias_definition.hpp"
#include "openMVG/sfm/sfm_data.hpp"
#include "openMVG/sfm/sfm_data_io.hpp"
#include "openMVG/sfm/sfm_data_utils.hpp"
#include "openMVG/sfm/sfm_view.hpp"
#include "openMVG/sfm/sfm_view_priors.hpp"
#include "openMVG/system/loggerprogress.hpp"
#include "openMVG/types.hpp"

#include "third_party/cmdLine/cmdLine.h"
#include "third_party/stlplus3/filesystemSimplified/file_system.hpp"

#include <fstream>
#include <memory>
#include <string>
#include <utility>

using namespace openMVG;
using namespace openMVG::cameras;
using namespace openMVG::exif;
using namespace openMVG::geodesy;
using namespace openMVG::image;
using namespace openMVG::sfm;

/// Check that Kmatrix is a string like "f;0;ppx;0;f;ppy;0;0;1"
/// With f,ppx,ppy as valid numerical value
bool checkIntrinsicStringValidity(const std::string &Kmatrix, double &focal, double &ppx, double &ppy)
{
    std::vector<std::string> vec_str;
    stl::split(Kmatrix, ';', vec_str); // 分割 用;隔开的变量
    if (vec_str.size() != 9)
    {
        OPENMVG_LOG_ERROR << "\n Missing ';' character";
        return false;
    }
    // Check that all K matrix value are valid numbers
    // 判断是否所有的K内参矩阵的元素是否都是有效数字
    for (size_t i = 0; i < vec_str.size(); ++i)
    {
        double readvalue = 0.0;
        std::stringstream ss;
        ss.str(vec_str[i]);
        if (!(ss >> readvalue))
        {
            OPENMVG_LOG_ERROR << "\n Used an invalid not a number character";
            return false;
        }
        if (i == 0)
            focal = readvalue;
        if (i == 2)
            ppx = readvalue;
        if (i == 5)
            ppy = readvalue;
    }
    return true;
}

bool getGPS(
    const std::string &filename,
    const int &GPS_to_XYZ_method,
    Vec3 &pose_center)
{
    std::unique_ptr<Exif_IO> exifReader(new Exif_IO_EasyExif);
    if (exifReader)
    {
        // Try to parse EXIF metada & check existence of EXIF data
        if (exifReader->open(filename) && exifReader->doesHaveExifInfo())
        {
            // Check existence of GPS coordinates
            double latitude, longitude, altitude;
            if (exifReader->GPSLatitude(&latitude) &&
                exifReader->GPSLongitude(&longitude) &&
                exifReader->GPSAltitude(&altitude))
            {
                // Add ECEF or UTM XYZ position to the GPS position array
                switch (GPS_to_XYZ_method)
                {
                case 1:
                    pose_center = lla_to_utm(latitude, longitude, altitude);
                    break;
                case 0:
                default:
                    pose_center = lla_to_ecef(latitude, longitude, altitude);
                    break;
                }
                return true;
            }
        }
    }
    return false;
}

/// Check string of prior weights
std::pair<bool, Vec3> checkPriorWeightsString(
    const std::string &sWeights)
{
    std::pair<bool, Vec3> val(true, Vec3::Zero());
    std::vector<std::string> vec_str;
    stl::split(sWeights, ';', vec_str);
    if (vec_str.size() != 3)
    {
        OPENMVG_LOG_ERROR << "Missing ';' character in prior weights";
        val.first = false;
    }
    // Check that all weight values are valid numbers
    for (size_t i = 0; i < vec_str.size(); ++i)
    {
        double readvalue = 0.0;
        std::stringstream ss;
        ss.str(vec_str[i]);
        if (!(ss >> readvalue))
        {
            OPENMVG_LOG_ERROR << "Used an invalid not a number character in local frame origin";
            val.first = false;
        }
        val.second[i] = readvalue;
    }
    return val;
}

// Create the description of an input image dataset for OpenMVG toolsuite
// 为OpenMVG工具套件创建输入图像数据集的描述
// - Export a SfM_Data file with View & Intrinsic data
// 输出包含view和内参数的SfM_Data文件
int main(int argc, char *argv[])
{

    // Step 数据准备
    std::string sImageDir = "/home/bck20/桌面/myopenMVGtest/image";   // 输入文件夹的路径
    std::string sOutputDir = "/home/bck20/桌面/myopenMVGtest/output"; // 输出文件夹的路径
    /** sKmatrix 相机内参矩阵
     *    fx  0   ppx
     *    0   fy  ppy
     *    0   0   1
     *    相机内参矩阵,字符串读入,由checkIntrinsicStringValidity获取f,ppx,ppy数据
     */
    std::string sKmatrix;           //= "fx;0;ppx;0;fy;ppy;0;0;1";
    std::string sfileDatabase = ""; // 从文件读取数据库文件,读入到vector<Datasheet>当中

    std::string sPriorWeights = "1.0;1.0;1.0";         // 优先权重
    std::pair<bool, Vec3> prior_w_info(false, Vec3()); //优先定义旋转的优先级

    int i_User_camera_model = PINHOLE_CAMERA_RADIAL3; //  径向畸变K1,K2,K3 radial distortion K1,K2,K3

    bool b_Group_camera_model = true; // 如果有需要,可将具有相同特性的相机组合在一起(从而获得更快、更稳定的BA)
    int i_GPS_XYZ_method = 0;
    double focal_pixels = -1.0; // 焦点像素

    // Step 数据读取
    /** 数据读取
     * 1、影像文件
     * 2、输出文件夹
     * 3、相机内参矩阵
     * 4、传感器型号与尺寸
     * 5、坐标基准
     * 6、畸变模型
     */

    // 每个图像的预期属性
    double width = -1, height = -1, focal = -1, ppx = -1, ppy = -1;

    const EINTRINSIC e_User_camera_model = EINTRINSIC(i_User_camera_model); // 得到相机模型的枚举

    // part  一系列文件是否存在的判断
    // 图片输入文件夹判断是否存在
    if (!stlplus::folder_exists(sImageDir))
    {
        OPENMVG_LOG_ERROR << "The input directory doesn't exist";
        return EXIT_FAILURE;
    }

    // sfm_data.json 参数输出文件夹 判断这个变量是否为空
    if (sOutputDir.empty())
    {
        OPENMVG_LOG_ERROR << "Invalid output directory";
        return EXIT_FAILURE;
    }

    // 判断文件夹是否已经存在,如果不存在就是创建一个文件夹
    if (!stlplus::folder_exists(sOutputDir))
    {
        if (!stlplus::folder_create(sOutputDir))
        {
            OPENMVG_LOG_ERROR << "Cannot create output directory";
            return EXIT_FAILURE;
        }
    }

    //
    if (sKmatrix.size() > 0 &&
        !checkIntrinsicStringValidity(sKmatrix, focal, ppx, ppy))
    {
        OPENMVG_LOG_ERROR << "Invalid K matrix input";
        return EXIT_FAILURE;
    }

    if (sKmatrix.size() > 0 && focal_pixels != -1.0)
    {
        OPENMVG_LOG_ERROR << "Cannot combine -f and -k options";
        return EXIT_FAILURE;
    }

    // 从文件读取数据文件,读入到vector<Datasheet>
    // 其中datasheet存储相机型号和传感器尺寸的数据库结构
    // 有两个成员std::string model和double sensorSize
    std::vector<Datasheet> vec_database;
    if (!sfileDatabase.empty())
    {
        if (!parseDatabase(sfileDatabase, vec_database))
        {
            OPENMVG_LOG_ERROR
                << "Invalid input database: " << sfileDatabase
                << ", please specify a valid file.";
            return EXIT_FAILURE;
        }
    }

    // 存储图片的名字
    // 被定义为各张图片路径的vector,并且用名称排序
    std::vector<std::string> vec_image = stlplus::folder_files(sImageDir);
    std::sort(vec_image.begin(), vec_image.end());

    // Step 读入并生成sfm_data.json
    // 使用视图及其对应摄像机配置空场景
    SfM_Data sfm_data;
    sfm_data.s_root_path = sImageDir; // setup main image root_path
    Views &views = sfm_data.views;
    Intrinsics &intrinsics = sfm_data.intrinsics;

    system::LoggerProgress my_progress_bar(vec_image.size(), "- Listing images -");
    std::ostringstream error_report_stream;
    for (std::vector<std::string>::const_iterator iter_image = vec_image.begin();
         iter_image != vec_image.end();
         ++iter_image, ++my_progress_bar)
    {
        // 读取元数据以填充相机参数(w、h、focal、ppx、ppy)字段
        // 初始化内参
        width = height = ppx = ppy = focal = -1.0;

        // 将文件夹名与文件名合并
        // e.g sImageFilename = /home/bck20/桌面/myopenMVGtest/image/100_7102.JPG
        const std::string sImageFilename = stlplus::create_filespec(sImageDir, *iter_image);
        // 获取文件名(即不带文件夹部分但带有扩展名的文件名)
        // e.g sImFilenamePart = 100_7102.JPG
        const std::string sImFilenamePart = stlplus::filename_part(sImageFilename);

        // part 检测是否是图像
        // 测试是否是所支持图像格式
        if (openMVG::image::GetFormat(sImageFilename.c_str()) == openMVG::image::Unknown)
        {
            error_report_stream
                << sImFilenamePart << " : Unknown image file format."
                << "\n";
            continue; // image cannot be opened
        }
        // 判断是否为遮罩图像
        if (sImFilenamePart.find("mask.png") != std::string::npos ||
            sImFilenamePart.find("_mask.png") != std::string::npos)
        {
            error_report_stream
                << sImFilenamePart << " is a mask image"
                << "\n";
            continue;
        }

        ImageHeader imgHeader;
        if (!openMVG::image::ReadImageHeader(sImageFilename.c_str(), &imgHeader))
            continue; // image cannot be read

        width = imgHeader.width;
        height = imgHeader.height;
        ppx = width / 2.0;
        ppy = height / 2.0;

        // part 考虑手动提供焦点的情况
        if (sKmatrix.size() > 0) // 已知用户校准K矩阵
        {
            if (!checkIntrinsicStringValidity(sKmatrix, focal, ppx, ppy))
                focal = -1.0;
        }
        else
        {
            // 用户提供的焦距值
            if (focal_pixels != -1)
            {
                focal = focal_pixels;
            }
        }

        // 如果不是手动提供或者错误提供
        if (focal == -1)
        {
            std::unique_ptr<Exif_IO> exifReader(new Exif_IO_EasyExif);
            // 打开文件进行检查和分析 返回bool
            exifReader->open(sImageFilename);
            // 验证文件是否有元数据并且获取相机的型号不为空
            const bool bHaveValidExifMetadata =
                exifReader->doesHaveExifInfo() && !exifReader->getModel().empty() &&
                !exifReader->getBrand().empty();

            // 错误则报错,对则引用
            if (bHaveValidExifMetadata) // if image contains meta data
            {
                // 处理焦距等于0的情况
                if (exifReader->getFocal() == 0.0f)
                {
                    error_report_stream
                        << stlplus::basename_part(sImageFilename) << ": Focal length is missig."
                        << "\n";
                    focal = -1.0;
                }
            }
            else
            // 在列表文件中创建图像条目
            {
                const std::string sCamModel = exifReader->getBrand() + " " + exifReader->getModel();
                Datasheet datasheet;
                // 下面函数的意思是从数据库中找有没有这个相机的型号
                if (getInfo(sCamModel, vec_database, datasheet))
                {
                    const double ccdw = datasheet.sensorSize_; // 传感器的尺寸
                    focal = std::max(width, height) * exifReader->getFocal() / ccdw;
                }
                else
                {
                    error_report_stream
                        << stlplus::basename_part(sImageFilename)
                        << "\" model \"" << sCamModel << "\"doesn't exit in the database"
                        << "\n"
                        << "Please consider add your camera model and sensor width in the database."
                        << "\n";
                }
            }
        }

        // part 获取相机模型,构建与视图相关的内在参数
        std::shared_ptr<IntrinsicBase> intrinsic;
        if (focal > 0 && ppx > 0 && ppy > 0 && width > 0 && height > 0)
        {
            // 创建所需的相机类型
            switch (e_User_camera_model)
            {
            case PINHOLE_CAMERA:
                intrinsic = std::make_shared<Pinhole_Intrinsic>(width, height, focal, ppx, ppy);
                break;
            case PINHOLE_CAMERA_RADIAL1:
                intrinsic = std::make_shared<Pinhole_Intrinsic_Radial_K1>(width, height, focal, ppx, ppy, 0.0); // setup no distortion as initial guess
                break;
            case PINHOLE_CAMERA_RADIAL3:
                intrinsic = std::make_shared<Pinhole_Intrinsic_Radial_K3>(width, height, focal, ppx, ppy, 0.0, 0.0, 0.0); // setup no distortion as initial guess
                break;
            case PINHOLE_CAMERA_BROWN:
                intrinsic = std::make_shared<Pinhole_Intrinsic_Brown_T2>(width, height, focal, ppx, ppy, 0.0, 0.0, 0.0, 0.0, 0.0); // setup no distortion as initial guess
                break;
            case PINHOLE_CAMERA_FISHEYE:
                intrinsic = std::make_shared<Pinhole_Intrinsic_Fisheye>(width, height, focal, ppx, ppy, 0.0, 0.0, 0.0, 0.0); // setup no distortion as initial guess
                break;
            case CAMERA_SPHERICAL:
                intrinsic = std::make_shared<Intrinsic_Spherical>(width, height);
                break;
            default:
                OPENMVG_LOG_ERROR << "Error: unknown camera model: " << (int)e_User_camera_model;
                return EXIT_FAILURE;
            }
        }
        // part 构建图片对应的视图,若有gps权重,就需要定位为优先旋转
        Vec3 pose_center;
        if (getGPS(sImageFilename, i_GPS_XYZ_method, pose_center) /*&& b_Use_pose_prior*/)
        { // views的子类,可以选择是否优先旋转,或者优先调整位置
            ViewPriors v(*iter_image, views.size(), views.size(), views.size(), width, height);

            // 添加与图像相关的内部文件(如果有)
            // (使用无效的内在字段值导出视图)
            if (!intrinsic)
            {
                v.id_intrinsic = UndefinedIndexT;
            }
            else
            {
                // 添加已经定义的内参值给sfm_container
                intrinsics[v.id_intrinsic] = intrinsic;
            }

            v.b_use_pose_center_ = true;
            v.pose_center_ = pose_center;
            // 先前的权重
            if (prior_w_info.first == true)
            {
                v.center_weight_ = prior_w_info.second;
            }

            // 将视图添加到sfm容器
            views[v.id_view] = std::make_shared<ViewPriors>(v);
        }
        // part 没有gps信息时
        else
        {
            View v(*iter_image, views.size(), views.size(), views.size(), width, height);

            // 添加与图像相关的内参数(如果有的话)
            if (!intrinsic)
            {
                // 使用无效的内在字段值导出视图
                v.id_intrinsic = UndefinedIndexT;
            }
            else
            {
                intrinsics[v.id_intrinsic] = intrinsic;
            }

            // 将视图添加到sfm容器
            views[v.id_view] = std::make_shared<View>(v);
        }
    }

    // part 报错与保存
    // 显示保存的警告和错误消息(如果有)
    if (!error_report_stream.str().empty())
    {
        OPENMVG_LOG_WARNING
            << "Warning & Error messages:\n"
            << error_report_stream.str();
    }

    // 如果需要,可以共享共同属性的组相机(导致更快更稳定的BA)
    if (b_Group_camera_model)
    {
        GroupSharedIntrinsics(sfm_data);
    }

    // 保存SFM数据和内参
    if (!Save(sfm_data,
              stlplus::create_filespec(sOutputDir, "sfm_data.json").c_str(),
              ESfM_Data(VIEWS | INTRINSICS)))
    {
        return EXIT_FAILURE;
    }

    // 显示报错信息
    OPENMVG_LOG_INFO
        << "SfMInit_ImageListing report:\n"
        << "listed #File(s): " << vec_image.size() << "\n"
        << "usable #File(s) listed in sfm_data: " << sfm_data.GetViews().size() << "\n"
        << "usable #Intrinsic(s) listed in sfm_data: " << sfm_data.GetIntrinsics().size();

    return EXIT_SUCCESS;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.0)
project(mvgtest1)
set(CMAKE_BUILD_TYPE "debug")
set(CMAKE_CXX_STANDARD 17)

add_executable(sfMInit_ImageListing sfMInit_ImageListing.cpp)   

#OpenMVG,OpenCV,Ceres

find_package(OpenCV REQUIRED)
find_package(OpenMVG REQUIRED)

find_package(Ceres REQUIRED PATHS "${CERES_PATH}/Thirdparty/ceres-solver")
include_directories(${CERES_INCLUDE_DIRS})
set(LIBS ${Ceres_LIBS_DIR}/libceres.a umfpack cxsparse glog gflags gomp
        ccolamd btf klu cholmod lapack blas camd amd pthread)
include_directories(${OPENMVG_INCLUDE_DIRS})

target_link_libraries(sfMInit_ImageListing
        PRIVATE
        OpenMVG::openMVG_sfm
        OpenMVG::openMVG_matching
        OpenMVG::openMVG_camera
        OpenMVG::openMVG_exif
        OpenMVG::openMVG_features
        OpenMVG::openMVG_geodesy
        OpenMVG::openMVG_geometry
        OpenMVG::openMVG_graph
        OpenMVG::openMVG_image
        OpenMVG::openMVG_linearProgramming
        OpenMVG::openMVG_matching
        OpenMVG::openMVG_matching_image_collection
        OpenMVG::openMVG_multiview
        OpenMVG::openMVG_numeric
        OpenMVG::openMVG_robust_estimation
        OpenMVG::openMVG_sfm
        OpenMVG::openMVG_system
        ${OpenCV_LIBS}
)

FIND_PACKAGE( OpenMP REQUIRED)
if(OPENMP_FOUND)
    message("OPENMP FOUND")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
posted @ 2022-09-09 13:37  乞力马扎罗山的雪  阅读(349)  评论(0编辑  收藏  举报