Skip to content

Commit

Permalink
v2.3.0 (#1090)
Browse files Browse the repository at this point in the history
- store all stages in Interface MVS format by default
- store dense point-cloud as PLY, including all point-views information, etc
- add max-texture-size and multi-texture support to mesh texturing stage
- speedup/improve fix non-manifold mesh algorithm
- unify library/application initialization/destroying
- add python support for reading DMAP and Interface MVS file formats
- account for different pixel center conventions when importing/exporting COLMAP which uses pixel center at (0.5,0.5) instead of (0, 0) as OpenMVS
- add interface to MVSNet, Nerfstudio and RTMV scene formats
- improve interface to Polycam scene format
- speedup PLY load/save
- speedup/improve ray-triangle intersection
- add support for UV coordinates agnostic about orientation
- add support for storing neighbor views information in Interface MVS format
- add support for C++20
- various other fixes
  • Loading branch information
cdcseacave authored Jan 7, 2024
1 parent 42b8816 commit ce03889
Show file tree
Hide file tree
Showing 68 changed files with 4,083 additions and 3,324 deletions.
40 changes: 23 additions & 17 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ SET(OpenMVS_BUILD_TOOLS ON CACHE BOOL "Build example applications")
SET(OpenMVS_USE_OPENMP ON CACHE BOOL "Enable OpenMP library")
SET(OpenMVS_USE_OPENGL ON CACHE BOOL "Enable OpenGL library")
SET(OpenMVS_USE_BREAKPAD ON CACHE BOOL "Enable BreakPad library")
SET(OpenMVS_USE_PYTHON OFF CACHE BOOL "Enable Python library bindings")
SET(OpenMVS_USE_PYTHON ON CACHE BOOL "Enable Python library bindings")
SET(OpenMVS_USE_CERES OFF CACHE BOOL "Enable CERES optimization library")
SET(OpenMVS_USE_CUDA ON CACHE BOOL "Enable CUDA library")
SET(OpenMVS_USE_FAST_FLOAT2INT ON CACHE BOOL "Use an optimized code to convert real numbers to int")
SET(OpenMVS_USE_FAST_FLOAT2INT OFF CACHE BOOL "Use an optimized code to convert real numbers to int")
SET(OpenMVS_USE_FAST_INVSQRT OFF CACHE BOOL "Use an optimized code to compute the inverse square root (slower in fact on modern compilers)")
SET(OpenMVS_USE_FAST_CBRT ON CACHE BOOL "Use an optimized code to compute the cubic root")
SET(OpenMVS_USE_FAST_CBRT OFF CACHE BOOL "Use an optimized code to compute the cubic root")
SET(OpenMVS_USE_SSE ON CACHE BOOL "Enable SSE optimizations")
SET(OpenMVS_MAX_CUDA_COMPATIBILITY OFF CACHE BOOL "Build for maximum CUDA device compatibility")
SET(OpenMVS_ENABLE_TESTS ON CACHE BOOL "Enable test code")
Expand Down Expand Up @@ -52,10 +52,15 @@ ENDIF()
PROJECT(OpenMVS)

SET(OpenMVS_MAJOR_VERSION 2)
SET(OpenMVS_MINOR_VERSION 2)
SET(OpenMVS_MINOR_VERSION 3)
SET(OpenMVS_PATCH_VERSION 0)
SET(OpenMVS_VERSION ${OpenMVS_MAJOR_VERSION}.${OpenMVS_MINOR_VERSION}.${OpenMVS_PATCH_VERSION})

# Disable SSE on unsuported platforms
IF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm|ARM|aarch64|AARCH64)")
SET(OpenMVS_USE_SSE OFF)
ENDIF()

# Define helper functions and macros.
INCLUDE(build/Utils.cmake)

Expand Down Expand Up @@ -109,16 +114,15 @@ if(OpenMVS_USE_CUDA)
ENABLE_LANGUAGE(CUDA)
# CUDA-11.x can not be compiled using C++14 standard on Windows
string(REGEX MATCH "^[0-9]+" CUDA_MAJOR ${CMAKE_CUDA_COMPILER_VERSION})
if(${CUDA_MAJOR} GREATER 10 AND CMAKE_CXX_COMPILER MATCHES "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "19.29")
message("Working around windows build failure with visual studio. Visual studio 16.10 introduced a compiler bug with compilng CUDA code with C++14. Set the cuda standard to 17 as a workaround.")
set(CMAKE_CUDA_STANDARD 17)
if(${CUDA_MAJOR} GREATER 10)
SET(CMAKE_CUDA_STANDARD 17)
endif()
EXECUTE_PROCESS(COMMAND "${CMAKE_CUDA_COMPILER}" --list-gpu-arch
OUTPUT_VARIABLE LIST_GPU_ARCH
ERROR_QUIET)
if(NOT LIST_GPU_ARCH AND OpenMVS_MAX_CUDA_COMPATIBILITY)
message(WARNING "Cannot compile for max CUDA compatibility, nvcc does not support --list-gpu-arch")
set(OpenMVS_MAX_CUDA_COMPATIBILITY NO)
SET(OpenMVS_MAX_CUDA_COMPATIBILITY OFF)
endif()
if(NOT OpenMVS_MAX_CUDA_COMPATIBILITY)
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
Expand Down Expand Up @@ -172,19 +176,21 @@ if(OpenMVS_USE_BREAKPAD)
endif()
endif()

SET(Boost_EXTRA_COMPONENTS "")
if(OpenMVS_USE_PYTHON)
FIND_PACKAGE(PythonLibs 3.0 REQUIRED)
if(PythonLibs_FOUND)
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
LIST(APPEND OpenMVS_EXTRA_INCLUDES ${PYTHON_INCLUDE_DIRS})
LIST(APPEND OpenMVS_EXTRA_LIBS ${PYTHON_LIBRARIES})
MESSAGE(STATUS "Python ${PYTHON_VERSION} found (include: ${PYTHON_INCLUDE_DIRS})")
FIND_PACKAGE(Python3 COMPONENTS Interpreter Development REQUIRED)
if(Python3_FOUND)
INCLUDE_DIRECTORIES(${Python3_INCLUDE_DIRS})
LIST(APPEND OpenMVS_EXTRA_INCLUDES ${Python3_INCLUDE_DIRS})
LIST(APPEND OpenMVS_EXTRA_LIBS ${Python3_LIBRARIES})
LIST(APPEND Boost_EXTRA_COMPONENTS python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR})
MESSAGE(STATUS "Python ${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} found (include: ${Python3_INCLUDE_DIRS})")
else()
MESSAGE("-- Can't find Python. Continuing without it.")
endif()
endif()

FIND_PACKAGE(Boost REQUIRED COMPONENTS iostreams program_options system serialization OPTIONAL_COMPONENTS python3)
FIND_PACKAGE(Boost REQUIRED COMPONENTS iostreams program_options system serialization OPTIONAL_COMPONENTS ${Boost_EXTRA_COMPONENTS})
if(Boost_FOUND)
LIST(APPEND OpenMVS_EXTRA_INCLUDES ${Boost_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
Expand All @@ -195,12 +201,12 @@ if(Boost_FOUND)
LIST(APPEND Boost_LIBRARIES zstd)
endif()
SET(_USE_BOOST TRUE)
if(Boost_python3_FOUND)
if(OpenMVS_USE_PYTHON AND Boost_${Boost_EXTRA_COMPONENTS}_FOUND)
SET(_USE_BOOST_PYTHON TRUE)
endif()
endif()

FIND_PACKAGE(Eigen3 REQUIRED)
FIND_PACKAGE(Eigen3 3.4 REQUIRED)
if(EIGEN3_FOUND)
LIST(APPEND OpenMVS_EXTRA_INCLUDES ${EIGEN3_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${EIGEN3_INCLUDE_DIR})
Expand Down
71 changes: 34 additions & 37 deletions apps/DensifyPointCloud/DensifyPointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace {

namespace OPT {
String strInputFileName;
String strPointCloudFileName;
String strOutputFileName;
String strViewNeighborsFileName;
String strOutputViewNeighborsFileName;
Expand All @@ -73,8 +74,17 @@ String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT

class Application {
public:
Application() {}
~Application() { Finalize(); }

bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application

// initialize and parse the command line parameters
bool Initialize(size_t argc, LPCTSTR* argv)
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
Expand All @@ -86,7 +96,7 @@ bool Initialize(size_t argc, LPCTSTR* argv)
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
Expand Down Expand Up @@ -127,6 +137,7 @@ bool Initialize(size_t argc, LPCTSTR* argv)
boost::program_options::options_description config("Densify options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list")
("pointcloud-file,p", boost::program_options::value<std::string>(&OPT::strPointCloudFileName), "sparse point-cloud with views file name to densify (overwrite existing point-cloud)")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the dense point-cloud (optional)")
("view-neighbors-file", boost::program_options::value<std::string>(&OPT::strViewNeighborsFileName), "input filename containing the list of views and their neighbors (optional)")
("output-view-neighbors-file", boost::program_options::value<std::string>(&OPT::strOutputViewNeighborsFileName), "output filename containing the generated list of views and their neighbors")
Expand All @@ -153,7 +164,7 @@ bool Initialize(size_t argc, LPCTSTR* argv)
("estimate-roi", boost::program_options::value(&OPT::nEstimateROI)->default_value(2), "estimate and set region-of-interest (0 - disabled, 1 - enabled, 2 - adaptive)")
("crop-to-roi", boost::program_options::value(&OPT::bCrop2ROI)->default_value(true), "crop scene using the region-of-interest")
("remove-dmaps", boost::program_options::value(&bRemoveDmaps)->default_value(false), "remove depth-maps after fusion")
("tower-mode", boost::program_options::value(&OPT::nTowerMode)->default_value(3), "add a cylinder of points in the center of ROI; scene assume to be Z-up oriented (0 - disabled, 1 - replace, 2 - append, 3 - select neighbors, <0 - force tower mode)")
("tower-mode", boost::program_options::value(&OPT::nTowerMode)->default_value(4), "add a cylinder of points in the center of ROI; scene assume to be Z-up oriented (0 - disabled, 1 - replace, 2 - append, 3 - select neighbors, 4 - select neighbors & append, <0 - force tower mode)")
;

// hidden options, allowed both on command line and
Expand Down Expand Up @@ -211,6 +222,7 @@ bool Initialize(size_t argc, LPCTSTR* argv)
return false;

// initialize optional options
Util::ensureValidPath(OPT::strPointCloudFileName);
Util::ensureValidPath(OPT::strOutputFileName);
Util::ensureValidPath(OPT::strViewNeighborsFileName);
Util::ensureValidPath(OPT::strOutputViewNeighborsFileName);
Expand Down Expand Up @@ -242,29 +254,14 @@ bool Initialize(size_t argc, LPCTSTR* argv)
if (!bValidConfig && !OPT::strDenseConfigFileName.empty())
OPTDENSE::oConfig.Save(OPT::strDenseConfigFileName);

// initialize global options
Process::setCurrentProcessPriority((Process::Priority)OPT::nProcessPriority);
#ifdef _USE_OPENMP
if (OPT::nMaxThreads != 0)
omp_set_num_threads(OPT::nMaxThreads);
#endif

#ifdef _USE_BREAKPAD
// start memory dumper
MiniDumper::Create(APPNAME, WORKING_FOLDER);
#endif

Util::Init();
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}

// finalize application instance
void Finalize()
void Application::Finalize()
{
#if TD_VERBOSE != TD_VERBOSE_OFF
// print memory statistics
Util::LogMemoryInfo();
#endif
MVS::Finalize();

CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
Expand All @@ -280,7 +277,8 @@ int main(int argc, LPCTSTR* argv)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif

if (!Initialize(argc, argv))
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;

Scene scene(OPT::nMaxThreads);
Expand All @@ -296,12 +294,16 @@ int main(int argc, LPCTSTR* argv)
scene.mesh.SamplePoints(ROUND2INT<unsigned>(-OPT::fSampleMesh), pointcloud);
VERBOSE("Sample mesh completed: %u points (%s)", pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str());
pointcloud.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".ply"));
Finalize();
return EXIT_SUCCESS;
}
// load and estimate a dense point-cloud
if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName)))
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName)));
if (sceneType == Scene::SCENE_NA)
return EXIT_FAILURE;
if (!OPT::strPointCloudFileName.empty() && !scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointCloudFileName))) {
VERBOSE("error: cannot load point-cloud file");
return EXIT_FAILURE;
}
if (!OPT::strMaskPath.empty()) {
Util::ensureValidFolderPath(OPT::strMaskPath);
for (Image& image : scene.images) {
Expand All @@ -322,7 +324,6 @@ int main(int argc, LPCTSTR* argv)
return EXIT_FAILURE;
fs >> scene.obb;
scene.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
Finalize();
return EXIT_SUCCESS;
}
if (!scene.IsBounded())
Expand All @@ -332,7 +333,6 @@ int main(int argc, LPCTSTR* argv)
if (!fs)
return EXIT_FAILURE;
fs << scene.obb;
Finalize();
return EXIT_SUCCESS;
}
if (OPT::nTowerMode!=0)
Expand All @@ -355,15 +355,13 @@ int main(int argc, LPCTSTR* argv)
if (!scene.ExportMeshToDepthMaps(MAKE_PATH_SAFE(OPT::strExportDepthMapsName)))
return EXIT_FAILURE;
VERBOSE("Mesh projection completed: %u depth-maps (%s)", scene.images.size(), TD_TIMER_GET_FMT().c_str());
Finalize();
return EXIT_SUCCESS;
}
if (OPT::fMaxSubsceneArea > 0) {
// split the scene in sub-scenes by maximum sampling area
Scene::ImagesChunkArr chunks;
scene.Split(chunks, OPT::fMaxSubsceneArea);
scene.ExportChunks(chunks, GET_PATH_FULL(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType);
Finalize();
return EXIT_SUCCESS;
}
if (OPT::thFilterPointCloud < 0) {
Expand All @@ -372,7 +370,6 @@ int main(int argc, LPCTSTR* argv)
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T("_filtered"));
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
scene.pointcloud.Save(baseFileName+_T(".ply"));
Finalize();
return EXIT_SUCCESS;
}
if (OPT::nExportNumViews && scene.pointcloud.IsValid()) {
Expand All @@ -388,7 +385,6 @@ int main(int argc, LPCTSTR* argv)
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
scene.pointcloud.Save(baseFileName+_T(".ply"));
}
Finalize();
return EXIT_SUCCESS;
}
if (OPT::fEstimateScale > 0 && !scene.pointcloud.IsEmpty() && !scene.images.empty()) {
Expand All @@ -402,35 +398,36 @@ int main(int argc, LPCTSTR* argv)
}
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName)));
scene.pointcloud.SaveWithScale(baseFileName+_T("_scale.ply"), scene.images, OPT::fEstimateScale);
Finalize();
return EXIT_SUCCESS;
}
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS) {
PointCloud sparsePointCloud;
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS || sceneType == Scene::SCENE_INTERFACE) {
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 1 && !scene.pointcloud.IsEmpty())
scene.pointcloud.PrintStatistics(scene.images.data(), &scene.obb);
#endif
if ((ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS)
sparsePointCloud = scene.pointcloud;
TD_TIMER_START();
if (!scene.DenseReconstruction(OPT::nFusionMode, OPT::bCrop2ROI, OPT::fBorderROI)) {
if (ABS(OPT::nFusionMode) != 1)
return EXIT_FAILURE;
VERBOSE("Depth-maps estimated (%s)", TD_TIMER_GET_FMT().c_str());
Finalize();
return EXIT_SUCCESS;
}
VERBOSE("Densifying point-cloud completed: %u points (%s)", scene.pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str());
}

// save the final point-cloud
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName)));
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
scene.pointcloud.Save(baseFileName+_T(".ply"));
scene.pointcloud.Save(baseFileName+_T(".ply"), (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS);
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2)
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), baseFileName+_T(".ply"));
#endif

Finalize();
if ((ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS)
scene.pointcloud.Swap(sparsePointCloud);
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/
Loading

0 comments on commit ce03889

Please sign in to comment.