Skip to content

Commit eacdde9

Browse files
authored
Merge branch 'develop' (#422)
* dense: improve PatchMatch algorithm * viewer: improve pick-tool * dense: increase patch size * match: extend cylinder/cone functionality * dense: visibility filter * dense: adjust patch size for average scene types * dense: correct normal * common: log number of cores * dense: various tweaks * dense: clean-up * cmake: improve CGAL linking * dense: fix fuse with missing depth-maps * cmake: fix find CGAL * interface: import P camera list * io: use legacy type names if requested * interface: export dense point-cloud to COLMAP * dense: improve refine stage (cherry picked from commit d42ec8d4f04d69a2e41257bf391ccb29b1a147bb) * dense: fine-tune patch-match * common: fix VC2019 warnings * common: fix build with opencv older than 3.4 * io: remove unused code * dense: improve confidence computation
1 parent d19dae8 commit eacdde9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2109
-6290
lines changed

.appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,5 @@ test_script:
108108
#- sh: ctest -j4
109109

110110
on_success:
111-
- cmd: 7z a OpenMVS_x64.7z "C:\projects\openmvs\bin\bin\x64\%Configuration%\*.exe" "C:\projects\openmvs\bin\bin\x64\%Configuration%\*.dll"
111+
- cmd: 7z a OpenMVS_x64.7z "C:\projects\openmvs\bin\bin\vc15\x64\%Configuration%\*.exe" "C:\projects\openmvs\bin\bin\vc15\x64\%Configuration%\*.dll"
112112
- cmd: appveyor PushArtifact OpenMVS_x64.7z

CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ ConfigLibrary()
4141

4242
# List configuration options
4343
SET(OpenMVS_USE_NONFREE ON CACHE BOOL "Build non-free (patented) functionality")
44-
SET(OpenMVS_USE_EXIV2 OFF CACHE BOOL "Link and use EXIV2 library")
4544
SET(OpenMVS_USE_CERES OFF CACHE BOOL "Enable CERES optimization library")
4645
SET(OpenMVS_USE_FAST_FLOAT2INT ON CACHE BOOL "Use an optimized code to convert real numbers to int")
4746
SET(OpenMVS_USE_FAST_INVSQRT OFF CACHE BOOL "Use an optimized code to compute the inverse square root (slower in fact on modern compilers)")

apps/DensifyPointCloud/DensifyPointCloud.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ String strOutputFileName;
4949
String strMeshFileName;
5050
String strDenseConfigFileName;
5151
float fSampleMesh;
52+
int thFilterPointCloud;
5253
int nArchiveType;
5354
int nProcessPriority;
5455
unsigned nMaxThreads;
@@ -85,22 +86,27 @@ bool Initialize(size_t argc, LPCTSTR* argv)
8586

8687
// group of options allowed both on command line and in config file
8788
unsigned nResolutionLevel;
89+
unsigned nMaxResolution;
8890
unsigned nMinResolution;
8991
unsigned nNumViews;
9092
unsigned nMinViewsFuse;
93+
unsigned nOptimize;
9194
unsigned nEstimateColors;
9295
unsigned nEstimateNormals;
9396
boost::program_options::options_description config("Densify options");
9497
config.add_options()
9598
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list")
9699
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the dense point-cloud")
97-
("resolution-level", boost::program_options::value<unsigned>(&nResolutionLevel)->default_value(1), "how many times to scale down the images before point cloud computation")
98-
("min-resolution", boost::program_options::value<unsigned>(&nMinResolution)->default_value(640), "do not scale images lower than this resolution")
99-
("number-views", boost::program_options::value<unsigned>(&nNumViews)->default_value(4), "number of views used for depth-map estimation (0 - all neighbor views available)")
100-
("number-views-fuse", boost::program_options::value<unsigned>(&nMinViewsFuse)->default_value(3), "minimum number of images that agrees with an estimate during fusion in order to consider it inlier")
101-
("estimate-colors", boost::program_options::value<unsigned>(&nEstimateColors)->default_value(1), "estimate the colors for the dense point-cloud")
102-
("estimate-normals", boost::program_options::value<unsigned>(&nEstimateNormals)->default_value(0), "estimate the normals for the dense point-cloud")
103-
("sample-mesh", boost::program_options::value<float>(&OPT::fSampleMesh)->default_value(0.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)")
100+
("resolution-level", boost::program_options::value(&nResolutionLevel)->default_value(1), "how many times to scale down the images before point cloud computation")
101+
("max-resolution", boost::program_options::value(&nMaxResolution)->default_value(3200), "do not scale images higher than this resolution")
102+
("min-resolution", boost::program_options::value(&nMinResolution)->default_value(640), "do not scale images lower than this resolution")
103+
("number-views", boost::program_options::value(&nNumViews)->default_value(5), "number of views used for depth-map estimation (0 - all neighbor views available)")
104+
("number-views-fuse", boost::program_options::value(&nMinViewsFuse)->default_value(3), "minimum number of images that agrees with an estimate during fusion in order to consider it inlier")
105+
("optimize", boost::program_options::value(&nOptimize)->default_value(7), "filter used after depth-map estimation (0 - disabled, 1 - remove speckles, 2 - fill gaps, 4 - cross-adjust)")
106+
("estimate-colors", boost::program_options::value(&nEstimateColors)->default_value(2), "estimate the colors for the dense point-cloud")
107+
("estimate-normals", boost::program_options::value(&nEstimateNormals)->default_value(0), "estimate the normals for the dense point-cloud")
108+
("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)")
109+
("filter-point-cloud", boost::program_options::value(&OPT::thFilterPointCloud)->default_value(0), "filter dense point-cloud based on visibility (0 - disabled)")
104110
;
105111

106112
// hidden options, allowed both on command line and
@@ -161,13 +167,17 @@ bool Initialize(size_t argc, LPCTSTR* argv)
161167
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + _T("_dense.mvs");
162168

163169
// init dense options
170+
if (!Util::isFullPath(OPT::strDenseConfigFileName))
171+
OPT::strDenseConfigFileName = MAKE_PATH(OPT::strDenseConfigFileName);
164172
OPTDENSE::init();
165173
const bool bValidConfig(OPTDENSE::oConfig.Load(OPT::strDenseConfigFileName));
166174
OPTDENSE::update();
167175
OPTDENSE::nResolutionLevel = nResolutionLevel;
176+
OPTDENSE::nMaxResolution = nMaxResolution;
168177
OPTDENSE::nMinResolution = nMinResolution;
169178
OPTDENSE::nNumViews = nNumViews;
170179
OPTDENSE::nMinViewsFuse = nMinViewsFuse;
180+
OPTDENSE::nOptimize = nOptimize;
171181
OPTDENSE::nEstimateColors = nEstimateColors;
172182
OPTDENSE::nEstimateNormals = nEstimateNormals;
173183
if (!bValidConfig)
@@ -184,6 +194,8 @@ bool Initialize(size_t argc, LPCTSTR* argv)
184194
// start memory dumper
185195
MiniDumper::Create(APPNAME, WORKING_FOLDER);
186196
#endif
197+
198+
Util::Init();
187199
return true;
188200
}
189201

@@ -233,6 +245,15 @@ int main(int argc, LPCTSTR* argv)
233245
VERBOSE("error: empty initial point-cloud");
234246
return EXIT_FAILURE;
235247
}
248+
if (OPT::thFilterPointCloud < 0) {
249+
// filter point-cloud based on camera-point visibility intersections
250+
scene.PointCloudFilter(OPT::thFilterPointCloud);
251+
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T("_filtered"));
252+
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
253+
scene.pointcloud.Save(baseFileName+_T(".ply"));
254+
Finalize();
255+
return EXIT_SUCCESS;
256+
}
236257
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS) {
237258
TD_TIMER_START();
238259
if (!scene.DenseReconstruction())

apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp

Lines changed: 128 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,11 @@ bool Initialize(size_t argc, LPCTSTR* argv)
8585
("help,h", "produce this help message")
8686
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
8787
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
88-
("archive-type", boost::program_options::value<unsigned>(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary")
89-
("process-priority", boost::program_options::value<int>(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
90-
("max-threads", boost::program_options::value<unsigned>(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
88+
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary")
89+
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
90+
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
9191
#if TD_VERBOSE != TD_VERBOSE_OFF
92-
("verbosity,v", boost::program_options::value<int>(&g_nVerbosityLevel)->default_value(
92+
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
9393
#if TD_VERBOSE == TD_VERBOSE_DEBUG
9494
3
9595
#else
@@ -105,7 +105,7 @@ bool Initialize(size_t argc, LPCTSTR* argv)
105105
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input COLMAP folder containing cameras, images and points files OR input MVS project file")
106106
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the MVS project")
107107
("image-folder", boost::program_options::value<std::string>(&OPT::strImageFolder)->default_value(COLMAP_IMAGES_FOLDER), "folder to the undistorted images")
108-
("normalize,f", boost::program_options::value<bool>(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to MVS format")
108+
("normalize,f", boost::program_options::value(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to MVS format")
109109
;
110110

111111
boost::program_options::options_description cmdline_options;
@@ -185,6 +185,8 @@ bool Initialize(size_t argc, LPCTSTR* argv)
185185
// start memory dumper
186186
MiniDumper::Create(APPNAME, WORKING_FOLDER);
187187
#endif
188+
189+
Util::Init();
188190
return true;
189191
}
190192

@@ -318,7 +320,6 @@ struct Image {
318320
return true;
319321
}
320322
bool Write(std::ostream& out) const {
321-
ASSERT(!projs.empty());
322323
out << ID+1 << _T(" ")
323324
<< q.w() << _T(" ") << q.x() << _T(" ") << q.y() << _T(" ") << q.z() << _T(" ")
324325
<< t(0) << _T(" ") << t(1) << _T(" ") << t(2) << _T(" ")
@@ -434,7 +435,7 @@ bool ImportScene(const String& strFolder, Interface& scene)
434435
camera.C = Interface::Pos3d(0,0,0);
435436
if (OPT::bNormalizeIntrinsics) {
436437
// normalize camera intrinsics
437-
const double fScale(1.0/Camera::GetNormalizationScale(colmapCamera.width, colmapCamera.height));
438+
const REAL fScale(REAL(1)/Camera::GetNormalizationScale(colmapCamera.width, colmapCamera.height));
438439
camera.K(0,0) *= fScale;
439440
camera.K(1,1) *= fScale;
440441
camera.K(0,2) *= fScale;
@@ -555,6 +556,7 @@ bool ExportScene(const String& strFolder, const Interface& scene)
555556

556557
// write camera list
557558
CLISTDEF0IDX(KMatrix,uint32_t) Ks;
559+
CLISTDEF0IDX(COLMAP::Camera,uint32_t) cams;
558560
{
559561
const String filenameCameras(strFolder+COLMAP_CAMERAS);
560562
LOG_OUT() << "Writing cameras: " << filenameCameras << std::endl;
@@ -614,12 +616,17 @@ bool ExportScene(const String& strFolder, const Interface& scene)
614616
K(1,1) = cam.params[1];
615617
K(0,2) = cam.params[2];
616618
K(1,2) = cam.params[3];
619+
cams.emplace_back(cam);
617620
}
618621
}
619622

620623
// create images list
621624
COLMAP::Images images;
622625
CameraArr cameras;
626+
float maxNumPointsSparse(0);
627+
const float avgViewsPerPoint(3.f);
628+
const uint32_t avgResolutionSmallView(640*480), avgResolutionLargeView(6000*4000);
629+
const uint32_t avgPointsPerSmallView(3000), avgPointsPerLargeView(12000);
623630
{
624631
images.resize(scene.images.size());
625632
cameras.resize(scene.images.size());
@@ -641,41 +648,125 @@ bool ExportScene(const String& strFolder, const Interface& scene)
641648
camera.R = pose.R;
642649
camera.C = pose.C;
643650
camera.ComposeP();
651+
const COLMAP::Camera& cam = cams[image.platformID];
652+
const uint32_t resolutionView(cam.width*cam.height);
653+
const float linearFactor(float(avgResolutionLargeView-resolutionView)/(avgResolutionLargeView-avgResolutionSmallView));
654+
maxNumPointsSparse += (avgPointsPerSmallView+(avgPointsPerLargeView-avgPointsPerSmallView)*linearFactor)/avgViewsPerPoint;
644655
}
645656
}
646657

647-
// write points list
648-
{
649-
const String filenamePoints(strFolder+COLMAP_POINTS);
650-
LOG_OUT() << "Writing points: " << filenamePoints << std::endl;
651-
std::ofstream file(filenamePoints);
652-
if (!file.good()) {
653-
VERBOSE("error: unable to open file '%s'", filenamePoints.c_str());
654-
return false;
658+
// auto-select dense or sparse mode based on number of points
659+
const bool bSparsePointCloud(scene.vertices.size() < (size_t)maxNumPointsSparse);
660+
if (bSparsePointCloud) {
661+
// write points list
662+
{
663+
const String filenamePoints(strFolder+COLMAP_POINTS);
664+
LOG_OUT() << "Writing points: " << filenamePoints << std::endl;
665+
std::ofstream file(filenamePoints);
666+
if (!file.good()) {
667+
VERBOSE("error: unable to open file '%s'", filenamePoints.c_str());
668+
return false;
669+
}
670+
file << _T("# 3D point list with one line of data per point:") << std::endl;
671+
file << _T("# POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)") << std::endl;
672+
for (uint32_t ID=0; ID<(uint32_t)scene.vertices.size(); ++ID) {
673+
const Interface::Vertex& vertex = scene.vertices[ID];
674+
COLMAP::Point point;
675+
point.ID = ID;
676+
point.p = vertex.X;
677+
for (const Interface::Vertex::View& view: vertex.views) {
678+
COLMAP::Image& img = images[view.imageID];
679+
COLMAP::Point::Track track;
680+
track.idImage = view.imageID;
681+
track.idProj = (uint32_t)img.projs.size();
682+
point.tracks.push_back(track);
683+
COLMAP::Image::Proj proj;
684+
proj.idPoint = ID;
685+
const Point3 X(vertex.X);
686+
ProjectVertex_3x4_3_2(cameras[view.imageID].P.val, X.ptr(), proj.p.data());
687+
img.projs.push_back(proj);
688+
}
689+
point.c = scene.verticesColor.empty() ? Interface::Col3(255,255,255) : scene.verticesColor[ID].c;
690+
point.e = 0;
691+
if (!point.Write(file))
692+
return false;
693+
}
655694
}
656-
file << _T("# 3D point list with one line of data per point:") << std::endl;
657-
file << _T("# POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)") << std::endl;
658-
for (uint32_t ID=0; ID<(uint32_t)scene.vertices.size(); ++ID) {
659-
const Interface::Vertex& vertex = scene.vertices[ID];
660-
COLMAP::Point point;
661-
point.ID = ID;
662-
point.p = vertex.X;
663-
for (const Interface::Vertex::View& view: vertex.views) {
664-
COLMAP::Image& img = images[view.imageID];
665-
COLMAP::Point::Track track;
666-
track.idImage = view.imageID;
667-
track.idProj = (uint32_t)img.projs.size();
668-
point.tracks.push_back(track);
669-
COLMAP::Image::Proj proj;
670-
proj.idPoint = ID;
671-
const Point3 X(vertex.X);
672-
ProjectVertex_3x4_3_2(cameras[view.imageID].P.val, X.ptr(), proj.p.data());
673-
img.projs.push_back(proj);
695+
696+
Util::ensureFolder(strFolder+COLMAP_STEREO_FOLDER);
697+
698+
// write fusion list
699+
{
700+
const String filenameFusion(strFolder+COLMAP_FUSION);
701+
LOG_OUT() << "Writing fusion configuration: " << filenameFusion << std::endl;
702+
std::ofstream file(filenameFusion);
703+
if (!file.good()) {
704+
VERBOSE("error: unable to open file '%s'", filenameFusion.c_str());
705+
return false;
674706
}
675-
point.c = scene.verticesColor.empty() ? Interface::Col3(255,255,255) : scene.verticesColor[ID].c;
676-
point.e = 0;
677-
if (!point.Write(file))
707+
for (const COLMAP::Image& img: images) {
708+
if (img.projs.empty())
709+
continue;
710+
file << img.name << std::endl;
711+
if (file.fail())
712+
return false;
713+
}
714+
}
715+
716+
// write patch-match list
717+
{
718+
const String filenameFusion(strFolder+COLMAP_PATCHMATCH);
719+
LOG_OUT() << "Writing patch-match configuration: " << filenameFusion << std::endl;
720+
std::ofstream file(filenameFusion);
721+
if (!file.good()) {
722+
VERBOSE("error: unable to open file '%s'", filenameFusion.c_str());
678723
return false;
724+
}
725+
for (const COLMAP::Image& img: images) {
726+
if (img.projs.empty())
727+
continue;
728+
file << img.name << std::endl;
729+
if (file.fail())
730+
return false;
731+
file << _T("__auto__, 20") << std::endl;
732+
if (file.fail())
733+
return false;
734+
}
735+
}
736+
737+
Util::ensureFolder(strFolder+COLMAP_STEREO_CONSISTENCYGRAPHS_FOLDER);
738+
Util::ensureFolder(strFolder+COLMAP_STEREO_DEPTHMAPS_FOLDER);
739+
Util::ensureFolder(strFolder+COLMAP_STEREO_NORMALMAPS_FOLDER);
740+
} else {
741+
// export dense point-cloud
742+
const String filenameDensePoints(strFolder+COLMAP_DENSE_POINTS);
743+
const String filenameDenseVisPoints(strFolder+COLMAP_DENSE_POINTS_VISIBILITY);
744+
LOG_OUT() << "Writing points: " << filenameDensePoints << " and " << filenameDenseVisPoints << std::endl;
745+
File file(filenameDenseVisPoints, File::WRITE, File::CREATE | File::TRUNCATE);
746+
if (!file.isOpen()) {
747+
VERBOSE("error: unable to write file '%s'", filenameDenseVisPoints.c_str());
748+
return false;
749+
}
750+
const uint64_t numPoints(scene.vertices.size());
751+
file.write(&numPoints, sizeof(uint64_t));
752+
PointCloud pointcloud;
753+
for (size_t i=0; i<numPoints; ++i) {
754+
const Interface::Vertex& vertex = scene.vertices[i];
755+
pointcloud.points.emplace_back(vertex.X);
756+
if (!scene.verticesNormal.empty())
757+
pointcloud.normals.emplace_back(scene.verticesNormal[i].n);
758+
if (!scene.verticesColor.empty())
759+
pointcloud.colors.emplace_back(scene.verticesColor[i].c);
760+
const uint32_t numViews((uint32_t)vertex.views.size());
761+
file.write(&numViews, sizeof(uint32_t));
762+
for (uint32_t v=0; v<numViews; ++v) {
763+
const Interface::Vertex::View& view = vertex.views[v];
764+
file.write(&view.imageID, sizeof(uint32_t));
765+
}
766+
}
767+
if (!pointcloud.Save(filenameDensePoints, true)) {
768+
VERBOSE("error: unable to write file '%s'", filenameDensePoints.c_str());
769+
return false;
679770
}
680771
}
681772

@@ -692,55 +783,10 @@ bool ExportScene(const String& strFolder, const Interface& scene)
692783
file << _T("# IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME") << std::endl;
693784
file << _T("# POINTS2D[] as (X, Y, POINT3D_ID)") << std::endl;
694785
for (const COLMAP::Image& img: images) {
695-
if (!img.projs.empty() && !img.Write(file))
786+
if ((bSparsePointCloud && img.projs.empty()) || !img.Write(file))
696787
return false;
697788
}
698789
}
699-
700-
Util::ensureFolder(strFolder+COLMAP_STEREO_FOLDER);
701-
702-
// write fusion list
703-
{
704-
const String filenameFusion(strFolder+COLMAP_FUSION);
705-
LOG_OUT() << "Writing fusion configuration: " << filenameFusion << std::endl;
706-
std::ofstream file(filenameFusion);
707-
if (!file.good()) {
708-
VERBOSE("error: unable to open file '%s'", filenameFusion.c_str());
709-
return false;
710-
}
711-
for (const COLMAP::Image& img: images) {
712-
if (img.projs.empty())
713-
continue;
714-
file << img.name << std::endl;
715-
if (file.fail())
716-
return false;
717-
}
718-
}
719-
720-
// write patch-match list
721-
{
722-
const String filenameFusion(strFolder+COLMAP_PATCHMATCH);
723-
LOG_OUT() << "Writing patch-match configuration: " << filenameFusion << std::endl;
724-
std::ofstream file(filenameFusion);
725-
if (!file.good()) {
726-
VERBOSE("error: unable to open file '%s'", filenameFusion.c_str());
727-
return false;
728-
}
729-
for (const COLMAP::Image& img: images) {
730-
if (img.projs.empty())
731-
continue;
732-
file << img.name << std::endl;
733-
if (file.fail())
734-
return false;
735-
file << _T("__auto__, 20") << std::endl;
736-
if (file.fail())
737-
return false;
738-
}
739-
}
740-
741-
Util::ensureFolder(strFolder+COLMAP_STEREO_CONSISTENCYGRAPHS_FOLDER);
742-
Util::ensureFolder(strFolder+COLMAP_STEREO_DEPTHMAPS_FOLDER);
743-
Util::ensureFolder(strFolder+COLMAP_STEREO_NORMALMAPS_FOLDER);
744790
return true;
745791
}
746792

0 commit comments

Comments
 (0)