【三维重建笔记】02 OpenMVS 流程主要 API 整理

引言

前一节 中初步尝试使用 colmap + MVS 流程重建了人物的 三维模型, 但是还遗留了 执行时间偏长(几个小时), 结果遗留噪声, 部分区域显得有些粗糙等问题。

前一节直接使用了 openMVS 官方 编译好的 exe 以及程序默认参数,来生成结果。本节试着通过编写程序的方式, 来走一遍 openMVS 的流程, 顺便进一步了解 OpenMVS API, 看看能否按照自己的需求,来调节参数,处理上面遗留的一些问题。

故本节主要梳理 OpenMVS 流程的 API 调用入参的含义

最终本次实验中, 生成的模型结果如下

观感上比之前 好了不少

另外, 本次试验中几个主要步骤的耗时如下
总的流程跑下来大致是 38 分钟 左右(设备: RTX2060, Core i7-10875, 实验时没带充电器,笔记本用电池运行,通电应该能更快一些)。

此次,最耗时的主要是 refineMesh 那一步, 后续看看是否还能优化。

0.Scene 类与一些基础操作

OpenMVS 流程的几个主要操作都以 Scene 的成员方法的形式进行了封装。

【1】加载 mvs 文件

复制代码
Scene scene(MAX_THREADS_NUM);
if (scene.Load(INPUT_MVS_FILE) == Scene::SCENE_NA) {
    std::cerr << "Failed to load input scene: " << INPUT_MVS_FILE << std::endl;
    return LOAD_INPUT_SCENE_FAILED;
}

【2】保存 mvs 文件

复制代码
scene.Save(OUTPUT_SCENE_ROI_TO_MVS);

【3】保存 mesh 文件

复制代码
scene.mesh.Save(OUTPUT_ORIGIN_MESH);

1.scene.DenseReconstruction 生成稠密点云

【0】生成稠密点云前,先计算 ROI(Bounding box)范围,然后将 ROI 之外的无用点去除掉,避免其参与计算

复制代码
scene.EstimateROI(1.0f, -1);// 使用第一个缩放参数可以整体调整生成的 ROI boundingBox 的大小。(无法只沿着某个方向缩小, 不过也够用)
scene.CropToROI(scene.obb);

【1】调用函数前需要通过如下方式先进行参数配置

复制代码
OPTDENSE::init();
MVS::OPTDENSE::update();
OPTDENSE::nResolutionLevel = 1;          // How many times to scale down the images before dense reconstruction (0=original, 
                                         // 1=half, 2=quarter, etc.).Higher values process faster but produce less detail.
OPTDENSE::nMaxResolution = 2560;         // Maximum image resolution in pixels. Images larger than this will be downscaled to 
                                         // this resolution.Set to 0 for no limit.
OPTDENSE::nMinResolution = 640;          // Minimum image resolution in pixels.Images can not be downscaled to a resolution 
                                         // smaller than this.
OPTDENSE::nSubResolutionLevels = 1;      // Number of additional lower resolution levels to process for better multi-scale 
                                         // depth estimation.0 means only process at the selected resolution level.
OPTDENSE::nNumViews = 4;                 // Number of neighbor images to use for depth estimation (0 to select valid views).
                                         // More views increase accuracy, but slow down processing.
OPTDENSE::nMinViews = 4;                 // Minimum number of views in which a point must be visible to be considered during 
                                         // neighbor views estimation. Higher values produce more similar neighbor views, 
										 // but may discard some valid points.
OPTDENSE::nMinViewsFuse = 5;             // Minimum number of views required to include a depth point in the final fused point 
                                         // cloud. Higher values produce cleaner results, but may lose coverage.
										 //(融合到最终点云中所需的最小视图数量。数值越高,结果越干净,但可能会降低覆盖范围。)
OPTDENSE::nMinViewsTrustPoint = 3;       // Minimum number of views for a point to be considered for approximating the 
                                         // depth-maps during initialization (<2 - random initialization).
OPTDENSE::nEstimationIters = 5;          // Number of iterations for photometric refinement of each depth estimate.More 
                                         // iterations improve accuracy, but increase computation time.
OPTDENSE::nEstimationGeometricIters = 3; // Number of iterations for geometric consistency filtering (0 disabled). More 
                                         // iterations may produce more accurate results, but increase computation time.
OPTDENSE::nEstimateColors = 2;           // Estimate color for each point in the dense cloud based on the source images. 
                                         // Disable to skip color computation.
OPTDENSE::nEstimateNormals = 2;          // Store estimated normals for each point. Normals are useful for surface 
                                         // reconstruction and visualization.
OPTDENSE::nFuseFilter = 2;               // Fusion quality level:- Merge only(0): Fast, just merge all points; 
                                         // Fuse(1): Standard fusion with outlier removal; 
										 // Dense fuse(2): Slower but produces the densest, highest quality result
OPTDENSE::nOptimize = 4;                 // flags used to filter the depth-maps after estimation (0 - disabled, 
                                         // 1 - remove-speckles, 2 - fill-gaps, 4 - adjust-confidence) 并且这个值是可以叠加的
										 //(按位运算理解  如果是 5(4 + 1), 即应用了移除噪点(1), 又调整了置信度(4))
OPTDENSE::nIgnoreMaskLabel = -1;         // label value to ignore in the image mask, stored in the MVS scene or next to each 
                                         // image with '.mask.png' extension (<0 - disabled)
OPTDENSE::fDepthReprojectionErrorThreshold = 0.5f;  // dense-fuse maximum distance between measured and depth projected pixel 
                                                    //(稠密融合(dense-fuse)中,实测像素与深度投影像素之间的最大允许误差距离)
OPTDENSE::bRemoveDmaps = true;           // Delete intermediate depth maps after fusion to save disk space. Disable to keep 
                                         // depth maps for later inspection or re-fusion.

函数声明

复制代码
bool Scene::DenseReconstruction(int nFusionMode, bool bCrop2ROI, float fBorderROI, float fSampleMeshNeighbors)

调用示例

复制代码
if (!scene.DenseReconstruction(0, true, 0.f, 0.f)) {
    std::cerr << "Dense reconstruction failed." << std::endl;
    return EXIT_FAILURE;
}

生成的稠密点云结果大致如下 (黄线 bounding box 即为 ROI 区域)

2.scene.ReconstructMesh 将稠密点云转为 Mesh

输入参数

复制代码
struct ReconstructMeshOptions {
	float minPointDistance{1.5};      // Increase for smoother, coarser mesh; decrease for finer detail
	bool isUseFreeSpaceSupport{false};// 对于室外 或者 复杂的场景, 可以考虑设置为false; 是否使用相机的射线信息来雕刻空白区域以 
	                                  // 优化表面重建
	bool isIntegrateOnly{false};      // 是否只计算 ROI 中的点
	float thicknessFactor{1.0};       // 更高的值可以减少噪声, 但可能导致更大的 hole 与 移除 点云分布比较薄的区域
	float qualityFactor{1.0};
	float decimate{0.2};              // 用于控制三角形面片数 有效 范围 0 - 1
	float removeSpurious{20.0};       // Higher value remove more isolated piece
	bool removeSpikes{true};          // Automatically detect and remove spike artifacts (sharp, thin protrusions) 
	                                  // from the mesh. Recommended for cleaner results.
	int closeHoleNum{30};             // larger values close bigger holes
	int smoothSteps{5};               // More iterations create smoother surfaces, but may lose detail
	float edgeLength{0.0};            // Controls mesh resolution and uniformity
};

调用示例

复制代码
if (!scene.ReconstructMesh(
	reconstructMeshOptions.minPointDistance,
	reconstructMeshOptions.isUseFreeSpaceSupport,
	reconstructMeshOptions.isIntegrateOnly,
	4,
	reconstructMeshOptions.thicknessFactor,
	reconstructMeshOptions.qualityFactor)) {
	std::cerr << "ReconstructMesh failed." << std::endl;
	return EXIT_FAILURE;
}

调用 ReconstructMesh 之后 使用 clean 方法清理一下生成的mesh

复制代码
// 官方示例的 demo 中, ReconstructMesh  之后是这样清理了三次
scene.mesh.Clean(
	1.f,
	reconstructMeshOptions.removeSpurious,
	reconstructMeshOptions.removeSpikes,
	reconstructMeshOptions.closeHoleNum,
	reconstructMeshOptions.smoothSteps,
	reconstructMeshOptions.edgeLength,
	false);

scene.mesh.Clean(
	reconstructMeshOptions.decimate,
	0.f,
	reconstructMeshOptions.removeSpikes,
	reconstructMeshOptions.closeHoleNum,
	0u,
	0.f,
	false);

scene.mesh.Clean(1.f, 0.f, false, 0u, 0u, 0.f, true);

生成的结果

(感觉 这个 Mesh 结果也够用了, 后面也可以权衡一下,是否要花 大量的时间 进行后一步优化)

3.scene.RefineMesh 优化输入的 Mesh

输入参数

复制代码
struct RefineMeshOptions {
	unsigned nResolutionLevel{0};        // Image resolution scale for refinement (0=original, 1=half, etc.).Higher values are
                            	         // faster but less detailed. Start with lower resolution for coarse refinement.
	unsigned nMinResolution{640};        // Minimum image resolution in pixels.Images can not be downscaled to a resolution 
	                                     // smaller than this.
	unsigned nMaxViews{8};               // Maximum number of view neighbors to use during refinement. More views improve 
	                                     // accuracy, but increase computation time and memory usage.
	float fDecimateMesh{0.3f};           // Simplify the input mesh before refinement (0 = no decimation, 1 = maximum). 
	                                     // Useful for reducing computation on high-poly meshes.
	unsigned nCloseHoles{30};            // Maximum hole size (in edges) to fill before refinement.Closing holes prevents 
	                                     // artifacts at boundaries (0 - disabled)
	unsigned nEnsureEdgeSize{1};         // Subdivide or collapse edges to ensure uniform size (0=no change, 1=moderate, 
	                                     // 2=aggressive).Helps create more uniform mesh topology.
	unsigned nMaxFaceArea{32};           // Maximum face area projected in any pair of images that is not subdivided 
	                                     // (0 - disabled) (在任何一对图像中,一个面(通常是三维模型中的三角片面)所投影的最大面积,
										 // 超过这个面积将不会被进一步细分)
	unsigned nScales{2};                 // Number of multi-scale refinement passes. More scales improve convergence from 
	                                     // coarse to fine detail.(更多的尺度有助于从粗略结构到精细细节的收敛)
	float fScaleStep{0.5};               // Resolution scaling factor between successive refinement scales. Lower values 
	                                     // create more gradual transitions between scales.
	unsigned nAlternatePair{0};          // Which image pairs to use as reference during multi-view refinement:- Both 
	                                     // references: Use all paired views (most accurate)- Alternate: Switch between left/right 
										 // (balanced)- Left/Right only: Use only one reference (faster, less accurate)
	float fRegularityWeight{0.2};        // Weight for mesh regularity term.Higher values produce smoother surfaces, but may lose 
	                                     // detail.Lower values preserve sharp features, but can be noisy.(低的值会更平滑, 但可能
										 // 会丢失一些细节。 高的值会保留一些更锐利的细节, 但可能会有更多的噪声)
	float fRatioRigidityElasticity{0.90};// Balance between mesh rigidity and elasticity:- 0 = fully elastic (flexible 
	                                     // deformation)- 1 = fully rigid (minimal deformation)Affects how much the mesh can 
										 // deform. (网格刚性与弹性的平衡: 0 = 完全弹性, 1 完全刚性)
	float fGradientStep{45.05};          //(文档里该值的理解拆分为两部分, 整数部分负责控制迭代次数, 小数部分赋值微调)
										 // opts.gradientStep = iters + gstep*0.1f;
										 // iters: Number of iterations of gradient descent optimization.
										 // gstep: Step size for gradient descent optimization.Larger values converge faster, 
										 // but may be unstable.Smaller values are more stable, but slower.
	float fThPlanarVertex{0.f};          // Ratio of vertices to treat as planar (constrained to move along their normal).
	                                     // Higher values preserve flat surfaces better, but reduce flexibility. (数值越高,
										 // 越能有效保持平坦表面,但会降低网格的变形灵活性。)
	unsigned nReduceMemory{1};           // Memory reduction strategy:- 0 = no reduction (fastest, most memory)- 
	                                     // 3 = maximum reduction (slowest, least memory)Use higher values for large scenes 
										 // or limited RAM.
};

函数调用

复制代码
if (!scene.RefineMesh(
	refineMeshOptions.nResolutionLevel,
	refineMeshOptions.nMinResolution,
	refineMeshOptions.nMaxViews,
	refineMeshOptions.fDecimateMesh,
	refineMeshOptions.nCloseHoles,
	refineMeshOptions.nEnsureEdgeSize,
	refineMeshOptions.nMaxFaceArea,
	refineMeshOptions.nScales,
	refineMeshOptions.fScaleStep,
	refineMeshOptions.nAlternatePair,
	refineMeshOptions.fRegularityWeight,
	refineMeshOptions.fRatioRigidityElasticity,
	refineMeshOptions.fGradientStep,
	refineMeshOptions.fThPlanarVertex=0.f,
	refineMeshOptions.nReduceMemory=1)) {
	std::cerr << "RefineMesh failed." << std::endl;
	return EXIT_FAILURE;
}

结果如下

Mesh 文件比优化前 大 1000 多 K, 效果优化前更平滑一些。

4.scene.TextureMesh 为 Mesh 生成贴图

输入参数

复制代码
struct TextureMeshOptions {
	unsigned resolutionLevel{0};     // Image resolution scale for texture extraction (0=original, 1=half, etc.).
	                                 // Higher values are faster but produce lower quality textures.
	unsigned minResolution{640};     // Minimum image resolution in pixels.Images can not be downscaled to a 
	                                 // resolution smaller than this.
	unsigned minCommonCameras{0};    // Minimum number of cameras that must see a face for it to be textured.
	                                 // Higher values ensure better texture quality but may leave some faces 
									 // untextured.
	float outlierThreshold{6e-2f};   // Threshold for rejecting outliers during views to face assignment.Higher 
	                                 // values are more permissive.(用于剔除噪点的阈值。 数值越高, 条件越宽松)
	float ratioDataSmoothness{0.6f}; // Balance between data term and smoothness term:0 = prioritize photometric quality 
	                                 // 1 = prioritize seam smoothness(0 优先保证光度质量 1 优先保证接缝的平滑度)
	bool globalSeamLeveling{true};   // Apply global color adjustment to minimize exposure differences between texture 
	                                 // patches.Recommended for better visual consistency across the entire model.
									 // (应用全局色彩调整,以最小化纹理块之间的曝光差异。建议启用此选项,以提升整个模型的视觉一致性。)
	bool localSeamLeveling{true};    // Apply local color blending along texture seams.Smooths transitions between 
	                                 // patches.Works well with global seam leveling for best results.
	unsigned textureSizeMultiple{0}; // Texture dimensions will be multiples of this value (0 - power of two)
	                                 //(纹理尺寸将会是此值的整数倍(0 表示使用 2 的幂次)。)
	unsigned rectPackingHeuristic{3};// Algorithm for packing texture patches into atlas:- 0 = MaxRects BSSF (best)- 
	                                 // 1 = MaxRects BL (fast)- 2 = Skyline BL Higher numbers are faster, but may be less efficient.
	uint32_t emptyColor{0x00000000}; // 用于填充没有 texture 覆盖到的区域的颜色
	float sharpnessWeight{0.5f};     // Sharpness weight to be applied on the texture (0 - disabled, 0.5 - good value).
	int ignoreMaskLabel{-1};         // Label value to ignore in the image mask, stored in the MVS scene or next to each image with 
	                                 // '.mask.png' extension(-1 - auto estimate mask for lens distortion, -2 - disabled)
	int maxTextureSize{8192};        // Maximum texture atlas size in pixels per dimension.Multiple textures are created if needed. 
	                                 // Larger values allow higher resolution textures, but require more memory (0 - no limit)
};

调用示例

复制代码
if (!scene.TextureMesh(
	textureOptions.resolutionLevel,
	textureOptions.minResolution,
	textureOptions.minCommonCameras,
	textureOptions.outlierThreshold,
	textureOptions.ratioDataSmoothness,
	textureOptions.globalSeamLeveling,
	textureOptions.localSeamLeveling,
	textureOptions.textureSizeMultiple,
	textureOptions.rectPackingHeuristic,
	Pixel8U(textureOptions.emptyColor),
	textureOptions.sharpnessWeight,
	textureOptions.ignoreMaskLabel,
	textureOptions.maxTextureSize)) {
	std::cerr << "RefineMesh failed." << std::endl;
	return EXIT_FAILURE;
}

结果如下

小结

本节进一步调用 API 来进行模型重建, 执行耗时与生成结果上相较于之前都有不少提升。

梳理下来,流程中的 API 可控制的入参有点多,但 openMVS 的API封装用起来也是挺方便的。

后续可以再看看 API 的方法是如何实现的, 还能如何拓展。

相关推荐
深蓝海拓9 小时前
PySide6从0开始学习的笔记(四)QMainWindow
笔记·python·学习·pyqt
sheeta19989 小时前
LeetCode 每日一题笔记 日期:2025.12.15 题目:2110.股票平滑下跌阶段的数目
笔记·算法·leetcode
智者知已应修善业16 小时前
【求中位数】2024-1-23
c语言·c++·经验分享·笔记·算法
张人玉16 小时前
百度 AI 图像识别 WinForms 应用代码分析笔记
人工智能·笔记·百度
xqqxqxxq17 小时前
背单词软件技术笔记(V1.0核心版及V2.0随机挖字母)
笔记
YJlio18 小时前
Active Directory 工具学习笔记(10.8):AdInsight——保存与导出(证据留存、共享与二次分析)
数据库·笔记·学习
xqqxqxxq18 小时前
背单词软件技术笔记(V2.0扩展版)
java·笔记·python
yuxb7319 小时前
Kubernetes核心组件详解与实践:controller
笔记·kubernetes
受之以蒙21 小时前
Rust 与 dora-rs:吃透核心概念,手把手打造跨语言的机器人实时数据流应用
人工智能·笔记·rust