Skip to content

Commit 5079a78

Browse files
committed
viewer: add workflow support
1 parent 1f68718 commit 5079a78

File tree

11 files changed

+685
-30
lines changed

11 files changed

+685
-30
lines changed

apps/DensifyPointCloud/DensifyPointCloud.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv)
140140
unsigned nFuseFilter;
141141
unsigned nOptimize;
142142
int nIgnoreMaskLabel;
143+
float fDepthReprojectionErrorThreshold;
143144
bool bRemoveDmaps;
144145
boost::program_options::options_description config("Densify options");
145146
config.add_options()
@@ -166,6 +167,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv)
166167
("sample-mesh", boost::program_options::value(&OPT::fSampleMesh)->default_value(0.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)")
167168
("fusion-mode", boost::program_options::value(&OPT::nFusionMode)->default_value(0), "depth-maps fusion mode (-2 - fuse disparity-maps, -1 - export disparity-maps only, 0 - depth-maps & fusion, 1 - export depth-maps only)")
168169
("fusion-filter", boost::program_options::value(&nFuseFilter)->default_value(2), "filter used to fuse the depth-maps (0 - merge, 1 - fuse, 2 - dense-fuse)")
170+
("fusion-reprojection-threshold,d", boost::program_options::value(&fDepthReprojectionErrorThreshold)->default_value(1.2f), "dense-fuse maximum distance between measured and depth projected pixel")
169171
("postprocess-dmaps", boost::program_options::value(&nOptimize)->default_value(0), "flags used to filter the depth-maps after estimation (0 - disabled, 1 - remove-speckles, 2 - fill-gaps, 4 - adjust-confidence)")
170172
("filter-point-cloud", boost::program_options::value(&OPT::thFilterPointCloud)->default_value(0), "filter dense point-cloud based on visibility (0 - disabled)")
171173
("export-number-views", boost::program_options::value(&OPT::nExportNumViews)->default_value(0), "export points with >= number of views (0 - disabled, <0 - save MVS project too)")
@@ -266,6 +268,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv)
266268
OPTDENSE::nFuseFilter = nFuseFilter;
267269
OPTDENSE::nOptimize = nOptimize;
268270
OPTDENSE::nIgnoreMaskLabel = nIgnoreMaskLabel;
271+
OPTDENSE::fDepthReprojectionErrorThreshold = fDepthReprojectionErrorThreshold;
269272
OPTDENSE::bRemoveDmaps = bRemoveDmaps;
270273
if (!bValidConfig && !OPT::strDenseConfigFileName.empty())
271274
OPTDENSE::oConfig.Save(OPT::strDenseConfigFileName);

apps/Viewer/Renderer.cpp

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,19 @@ bool Renderer::Initialize() {
6464
// Set default lighting
6565
SetLighting(Eigen::Vector3f(0.f, 0.f, 1.f), 1.f, Eigen::Vector3f(1.f, 1.f, 1.f));
6666

67+
// Enable point size and line width control
68+
GL_CHECK(glEnable(GL_PROGRAM_POINT_SIZE));
69+
6770
// Enable depth testing
6871
GL_CHECK(glEnable(GL_DEPTH_TEST));
6972
GL_CHECK(glDepthFunc(GL_LESS));
7073

71-
// Enable point size and line width control
72-
GL_CHECK(glEnable(GL_PROGRAM_POINT_SIZE));
73-
7474
// Enable blending for transparency
7575
GL_CHECK(glEnable(GL_BLEND));
7676
GL_CHECK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
7777

78+
GL_CHECK(glDisable(GL_CULL_FACE));
79+
GL_CHECK(glFrontFace(GL_CCW));
7880
return true;
7981
}
8082
catch (const std::exception& e) {
@@ -195,6 +197,8 @@ void Renderer::CreateShaders() {
195197
#include "shaders/selection.vert"
196198
,
197199
#include "shaders/selection.frag"
200+
,
201+
#include "shaders/selection.geom"
198202
);
199203

200204
// 2D overlay shader for SelectionController
@@ -1129,10 +1133,6 @@ void Renderer::RenderImageOverlays(const Window& window) {
11291133
if (imageOverlayIndexCount == 0)
11301134
return;
11311135

1132-
// Save current GL state
1133-
GLboolean depthTestEnabled;
1134-
GL_CHECK(glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled));
1135-
11361136
// Set up for 3D rendering with special handling for transparency
11371137
GL_CHECK(glDisable(GL_DEPTH_TEST)); // Temporarily disable depth testing to ensure visibility
11381138
GL_CHECK(glEnable(GL_BLEND));
@@ -1169,8 +1169,7 @@ void Renderer::RenderImageOverlays(const Window& window) {
11691169
imageOverlayVAO->Unbind();
11701170

11711171
// Restore previous depth test state
1172-
if (depthTestEnabled)
1173-
GL_CHECK(glEnable(GL_DEPTH_TEST));
1172+
GL_CHECK(glEnable(GL_DEPTH_TEST));
11741173
}
11751174

11761175
void Renderer::RenderSelection(const Window& window) {
@@ -1199,7 +1198,12 @@ void Renderer::RenderSelection(const Window& window) {
11991198
return;
12001199

12011200
// Render selection lines
1201+
GL_CHECK(glDisable(GL_DEPTH_TEST));
1202+
12021203
selectionShader->Use();
1204+
GLint viewport[4] = { 0, 0, 1, 1 };
1205+
GL_CHECK(glGetIntegerv(GL_VIEWPORT, viewport));
1206+
selectionShader->SetVector2("viewportSize", Eigen::Vector2f((float)viewport[2], (float)viewport[3]));
12031207
selectionVAO->Bind();
12041208

12051209
// Use different colors for different selection types
@@ -1225,6 +1229,8 @@ void Renderer::RenderSelection(const Window& window) {
12251229
}
12261230

12271231
selectionVAO->Unbind();
1232+
1233+
GL_CHECK(glEnable(GL_DEPTH_TEST));
12281234
}
12291235

12301236
void Renderer::RenderBounds() {
@@ -1250,7 +1256,6 @@ void Renderer::RenderCoordinateAxes(const Camera& camera) {
12501256
GLint oldViewport[4];
12511257
GLboolean depthTestEnabled;
12521258
GL_CHECK(glGetIntegerv(GL_VIEWPORT, oldViewport));
1253-
GL_CHECK(glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled));
12541259

12551260
// Set up a small viewport in the bottom right corner
12561261
const int axesSize = 100; // Size of the axes widget
@@ -1294,8 +1299,7 @@ void Renderer::RenderCoordinateAxes(const Camera& camera) {
12941299

12951300
// Restore original viewport and depth test state
12961301
GL_CHECK(glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]));
1297-
if (depthTestEnabled)
1298-
GL_CHECK(glEnable(GL_DEPTH_TEST));
1302+
GL_CHECK(glEnable(GL_DEPTH_TEST));
12991303
}
13001304

13011305
void Renderer::RenderArcballGizmos(const Camera& camera, const class ArcballControls& controls) {
@@ -1408,12 +1412,7 @@ void Renderer::RenderSelectionOverlay(const Window& window) {
14081412
if (!selectionOverlayShader || !selectionOverlayVAO || !selectionOverlayVBO)
14091413
return;
14101414
// Disable depth testing for 2D overlay
1411-
GLboolean depthTestEnabled = glIsEnabled(GL_DEPTH_TEST);
1412-
if (depthTestEnabled)
1413-
GL_CHECK(glDisable(GL_DEPTH_TEST));
1414-
1415-
// Set up 2D rendering state
1416-
GL_CHECK(glDisable(GL_CULL_FACE));
1415+
GL_CHECK(glDisable(GL_DEPTH_TEST));
14171416

14181417
selectionOverlayShader->Use();
14191418
selectionOverlayShader->SetVector3("overlayColor", Eigen::Vector3f(1.f, 1.f, 0.f)); // Yellow
@@ -1464,8 +1463,7 @@ void Renderer::RenderSelectionOverlay(const Window& window) {
14641463
selectionOverlayVAO->Unbind();
14651464

14661465
// Restore OpenGL state
1467-
if (depthTestEnabled)
1468-
GL_CHECK(glEnable(GL_DEPTH_TEST));
1466+
GL_CHECK(glEnable(GL_DEPTH_TEST));
14691467
}
14701468

14711469
void Renderer::RenderSelectedGeometry(const Window& window) {
@@ -1475,7 +1473,6 @@ void Renderer::RenderSelectedGeometry(const Window& window) {
14751473
return;
14761474

14771475
// Enable blending for highlighting effect
1478-
glIsEnabled(GL_DEPTH_TEST);
14791476
GL_CHECK(glEnable(GL_BLEND));
14801477
GL_CHECK(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
14811478

apps/Viewer/Scene.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,140 @@ bool Scene::Export(const String& _fileName, const String& exportType, bool bView
361361
return bPoints || bMesh;
362362
}
363363

364+
bool Scene::RunDensifyWorkflow(const DensifyWorkflowOptions& options) {
365+
if (!IsOpen()) {
366+
DEBUG("error: no scene loaded");
367+
return false;
368+
}
369+
if (scene.images.empty()) {
370+
DEBUG("error: scene has no images to densify");
371+
return false;
372+
}
373+
374+
MVS::OPTDENSE::init();
375+
MVS::OPTDENSE::update();
376+
MVS::OPTDENSE::nResolutionLevel = options.resolutionLevel;
377+
MVS::OPTDENSE::nMaxResolution = options.maxResolution;
378+
MVS::OPTDENSE::nMinResolution = options.minResolution;
379+
MVS::OPTDENSE::nSubResolutionLevels = options.subResolutionLevels;
380+
MVS::OPTDENSE::nNumViews = options.numViews;
381+
MVS::OPTDENSE::nMinViews = MAXF(1u, options.minViews);
382+
MVS::OPTDENSE::nMinViewsTrustPoint = MAXF(1u, options.minViewsTrust);
383+
MVS::OPTDENSE::nMinViewsFuse = MAXF(1u, options.minViewsFuse);
384+
MVS::OPTDENSE::nEstimationIters = MAXF(1u, options.estimationIters);
385+
MVS::OPTDENSE::nEstimationGeometricIters = options.geometricIters;
386+
MVS::OPTDENSE::nFuseFilter = CLAMP(options.fuseFilter, 0u, (unsigned)MVS::OPTDENSE::FUSE_DENSEFILTER);
387+
MVS::OPTDENSE::fDepthReprojectionErrorThreshold = options.fDepthReprojectionErrorThreshold;
388+
MVS::OPTDENSE::nEstimateColors = options.estimateColors ? 2u : 0u;
389+
MVS::OPTDENSE::nEstimateNormals = options.estimateNormals ? 2u : 0u;
390+
MVS::OPTDENSE::bRemoveDmaps = options.removeDepthMaps;
391+
MVS::OPTDENSE::nOptimize = options.postprocess ? (unsigned)MVS::OPTDENSE::OPTIMIZE : 0u;
392+
393+
if (!scene.DenseReconstruction(options.fusionMode, options.cropToROI, options.borderROI, options.sampleMeshNeighbors)) {
394+
DEBUG("error: dense reconstruction failed");
395+
return false;
396+
}
397+
398+
UpdateGeometryAfterModification();
399+
return true;
400+
}
401+
402+
bool Scene::RunReconstructMeshWorkflow(const ReconstructMeshWorkflowOptions& options) {
403+
if (!IsOpen()) {
404+
DEBUG("error: no scene loaded");
405+
return false;
406+
}
407+
if (!scene.pointcloud.IsValid()) {
408+
DEBUG("error: point-cloud is empty; run densify before reconstructing the mesh");
409+
return false;
410+
}
411+
412+
MVS::Scene& mvsScene = scene;
413+
414+
if (options.constantWeight)
415+
mvsScene.pointcloud.pointWeights.Release();
416+
417+
if (!mvsScene.ReconstructMesh(options.minPointDistance, options.useFreeSpaceSupport, options.useOnlyROI,
418+
4, options.thicknessFactor, options.qualityFactor)) {
419+
DEBUG("error: mesh reconstruction failed");
420+
return false;
421+
}
422+
423+
if (options.cropToROI && mvsScene.IsBounded()) {
424+
const size_t numVertices = mvsScene.mesh.vertices.size();
425+
const size_t numFaces = mvsScene.mesh.faces.size();
426+
mvsScene.mesh.RemoveFacesOutside(mvsScene.obb);
427+
VERBOSE("Mesh trimmed to ROI: %u vertices and %u faces removed",
428+
(unsigned)(numVertices - mvsScene.mesh.vertices.size()),
429+
(unsigned)(numFaces - mvsScene.mesh.faces.size()));
430+
}
431+
432+
float decimate = options.decimateMesh;
433+
if (options.targetFaceNum && !mvsScene.mesh.faces.empty())
434+
decimate = static_cast<float>(options.targetFaceNum) / mvsScene.mesh.faces.size();
435+
decimate = CLAMP(decimate, 0.f, 1.f);
436+
if (decimate <= 0.f)
437+
decimate = 1.f;
438+
439+
mvsScene.mesh.Clean(1.f, options.removeSpurious, options.removeSpikes, options.closeHoles, options.smoothSteps, options.edgeLength, false);
440+
mvsScene.mesh.Clean(decimate, 0.f, options.removeSpikes, options.closeHoles, 0u, 0.f, false);
441+
mvsScene.mesh.Clean(1.f, 0.f, false, 0u, 0u, 0.f, true);
442+
443+
UpdateGeometryAfterModification();
444+
return true;
445+
}
446+
447+
bool Scene::RunRefineMeshWorkflow(const RefineMeshWorkflowOptions& options) {
448+
if (!IsOpen()) {
449+
DEBUG("error: no scene loaded");
450+
return false;
451+
}
452+
if (scene.mesh.IsEmpty()) {
453+
DEBUG("error: mesh is empty; reconstruct a mesh before refining");
454+
return false;
455+
}
456+
457+
if (!scene.RefineMesh(options.resolutionLevel, options.minResolution, options.maxViews,
458+
options.decimateMesh, options.closeHoles, options.ensureEdgeSize, options.maxFaceArea,
459+
options.scales, options.scaleStep, options.alternatePair, options.regularityWeight,
460+
options.rigidityElasticityRatio, options.gradientStep, options.planarVertexRatio,
461+
options.reduceMemory)) {
462+
DEBUG("error: mesh refinement failed");
463+
return false;
464+
}
465+
466+
UpdateGeometryAfterModification();
467+
return true;
468+
}
469+
470+
bool Scene::RunTextureMeshWorkflow(const TextureMeshWorkflowOptions& options) {
471+
if (!IsOpen()) {
472+
DEBUG("error: no scene loaded");
473+
return false;
474+
}
475+
if (scene.mesh.IsEmpty()) {
476+
DEBUG("error: mesh is empty; reconstruct or load a mesh before texturing");
477+
return false;
478+
}
479+
480+
float decimate = CLAMP(options.decimateMesh, 0.f, 1.f);
481+
if (decimate <= 0.f)
482+
decimate = 1.f;
483+
scene.mesh.Clean(decimate, 0.f, false, options.closeHoles, 0u, 0.f, false);
484+
scene.mesh.Clean(1.f, 0.f, false, 0u, 0u, 0.f, true);
485+
486+
if (!scene.TextureMesh(options.resolutionLevel, options.minResolution, options.minCommonCameras,
487+
options.outlierThreshold, options.ratioDataSmoothness, options.globalSeamLeveling,
488+
options.localSeamLeveling, options.textureSizeMultiple, options.rectPackingHeuristic,
489+
Pixel8U(options.emptyColor), options.sharpnessWeight, options.ignoreMaskLabel, options.maxTextureSize)) {
490+
DEBUG("error: mesh texturing failed");
491+
return false;
492+
}
493+
494+
UpdateGeometryAfterModification();
495+
return true;
496+
}
497+
364498
MVS::IIndex Scene::ImageIdxMVS2Viewer(MVS::IIndex idx) const {
365499
// Convert MVS image index to viewer index
366500
// The list of images in the viewer is a subset of the MVS images,

0 commit comments

Comments
 (0)