Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge branch 'develop' #422

Merged
merged 22 commits into from
May 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,5 @@ test_script:
#- sh: ctest -j4

on_success:
- cmd: 7z a OpenMVS_x64.7z "C:\projects\openmvs\bin\bin\x64\%Configuration%\*.exe" "C:\projects\openmvs\bin\bin\x64\%Configuration%\*.dll"
- cmd: 7z a OpenMVS_x64.7z "C:\projects\openmvs\bin\bin\vc15\x64\%Configuration%\*.exe" "C:\projects\openmvs\bin\bin\vc15\x64\%Configuration%\*.dll"
- cmd: appveyor PushArtifact OpenMVS_x64.7z
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ ConfigLibrary()

# List configuration options
SET(OpenMVS_USE_NONFREE ON CACHE BOOL "Build non-free (patented) functionality")
SET(OpenMVS_USE_EXIV2 OFF CACHE BOOL "Link and use EXIV2 library")
SET(OpenMVS_USE_CERES OFF CACHE BOOL "Enable CERES optimization library")
SET(OpenMVS_USE_FAST_FLOAT2INT ON 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)")
Expand Down
35 changes: 28 additions & 7 deletions apps/DensifyPointCloud/DensifyPointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ String strOutputFileName;
String strMeshFileName;
String strDenseConfigFileName;
float fSampleMesh;
int thFilterPointCloud;
int nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
Expand Down Expand Up @@ -85,22 +86,27 @@ bool Initialize(size_t argc, LPCTSTR* argv)

// group of options allowed both on command line and in config file
unsigned nResolutionLevel;
unsigned nMaxResolution;
unsigned nMinResolution;
unsigned nNumViews;
unsigned nMinViewsFuse;
unsigned nOptimize;
unsigned nEstimateColors;
unsigned nEstimateNormals;
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")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the dense point-cloud")
("resolution-level", boost::program_options::value<unsigned>(&nResolutionLevel)->default_value(1), "how many times to scale down the images before point cloud computation")
("min-resolution", boost::program_options::value<unsigned>(&nMinResolution)->default_value(640), "do not scale images lower than this resolution")
("number-views", boost::program_options::value<unsigned>(&nNumViews)->default_value(4), "number of views used for depth-map estimation (0 - all neighbor views available)")
("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")
("estimate-colors", boost::program_options::value<unsigned>(&nEstimateColors)->default_value(1), "estimate the colors for the dense point-cloud")
("estimate-normals", boost::program_options::value<unsigned>(&nEstimateNormals)->default_value(0), "estimate the normals for the dense point-cloud")
("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)")
("resolution-level", boost::program_options::value(&nResolutionLevel)->default_value(1), "how many times to scale down the images before point cloud computation")
("max-resolution", boost::program_options::value(&nMaxResolution)->default_value(3200), "do not scale images higher than this resolution")
("min-resolution", boost::program_options::value(&nMinResolution)->default_value(640), "do not scale images lower than this resolution")
("number-views", boost::program_options::value(&nNumViews)->default_value(5), "number of views used for depth-map estimation (0 - all neighbor views available)")
("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")
("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)")
("estimate-colors", boost::program_options::value(&nEstimateColors)->default_value(2), "estimate the colors for the dense point-cloud")
("estimate-normals", boost::program_options::value(&nEstimateNormals)->default_value(0), "estimate the normals for the dense point-cloud")
("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)")
("filter-point-cloud", boost::program_options::value(&OPT::thFilterPointCloud)->default_value(0), "filter dense point-cloud based on visibility (0 - disabled)")
;

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

// init dense options
if (!Util::isFullPath(OPT::strDenseConfigFileName))
OPT::strDenseConfigFileName = MAKE_PATH(OPT::strDenseConfigFileName);
OPTDENSE::init();
const bool bValidConfig(OPTDENSE::oConfig.Load(OPT::strDenseConfigFileName));
OPTDENSE::update();
OPTDENSE::nResolutionLevel = nResolutionLevel;
OPTDENSE::nMaxResolution = nMaxResolution;
OPTDENSE::nMinResolution = nMinResolution;
OPTDENSE::nNumViews = nNumViews;
OPTDENSE::nMinViewsFuse = nMinViewsFuse;
OPTDENSE::nOptimize = nOptimize;
OPTDENSE::nEstimateColors = nEstimateColors;
OPTDENSE::nEstimateNormals = nEstimateNormals;
if (!bValidConfig)
Expand All @@ -184,6 +194,8 @@ bool Initialize(size_t argc, LPCTSTR* argv)
// start memory dumper
MiniDumper::Create(APPNAME, WORKING_FOLDER);
#endif

Util::Init();
return true;
}

Expand Down Expand Up @@ -233,6 +245,15 @@ int main(int argc, LPCTSTR* argv)
VERBOSE("error: empty initial point-cloud");
return EXIT_FAILURE;
}
if (OPT::thFilterPointCloud < 0) {
// filter point-cloud based on camera-point visibility intersections
scene.PointCloudFilter(OPT::thFilterPointCloud);
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 ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS) {
TD_TIMER_START();
if (!scene.DenseReconstruction())
Expand Down
210 changes: 128 additions & 82 deletions apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ 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<unsigned>(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value<int>(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value<unsigned>(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 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
("verbosity,v", boost::program_options::value<int>(&g_nVerbosityLevel)->default_value(
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
Expand All @@ -105,7 +105,7 @@ bool Initialize(size_t argc, LPCTSTR* argv)
("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")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the MVS project")
("image-folder", boost::program_options::value<std::string>(&OPT::strImageFolder)->default_value(COLMAP_IMAGES_FOLDER), "folder to the undistorted images")
("normalize,f", boost::program_options::value<bool>(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to MVS format")
("normalize,f", boost::program_options::value(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to MVS format")
;

boost::program_options::options_description cmdline_options;
Expand Down Expand Up @@ -185,6 +185,8 @@ bool Initialize(size_t argc, LPCTSTR* argv)
// start memory dumper
MiniDumper::Create(APPNAME, WORKING_FOLDER);
#endif

Util::Init();
return true;
}

Expand Down Expand Up @@ -318,7 +320,6 @@ struct Image {
return true;
}
bool Write(std::ostream& out) const {
ASSERT(!projs.empty());
out << ID+1 << _T(" ")
<< q.w() << _T(" ") << q.x() << _T(" ") << q.y() << _T(" ") << q.z() << _T(" ")
<< t(0) << _T(" ") << t(1) << _T(" ") << t(2) << _T(" ")
Expand Down Expand Up @@ -434,7 +435,7 @@ bool ImportScene(const String& strFolder, Interface& scene)
camera.C = Interface::Pos3d(0,0,0);
if (OPT::bNormalizeIntrinsics) {
// normalize camera intrinsics
const double fScale(1.0/Camera::GetNormalizationScale(colmapCamera.width, colmapCamera.height));
const REAL fScale(REAL(1)/Camera::GetNormalizationScale(colmapCamera.width, colmapCamera.height));
camera.K(0,0) *= fScale;
camera.K(1,1) *= fScale;
camera.K(0,2) *= fScale;
Expand Down Expand Up @@ -555,6 +556,7 @@ bool ExportScene(const String& strFolder, const Interface& scene)

// write camera list
CLISTDEF0IDX(KMatrix,uint32_t) Ks;
CLISTDEF0IDX(COLMAP::Camera,uint32_t) cams;
{
const String filenameCameras(strFolder+COLMAP_CAMERAS);
LOG_OUT() << "Writing cameras: " << filenameCameras << std::endl;
Expand Down Expand Up @@ -614,12 +616,17 @@ bool ExportScene(const String& strFolder, const Interface& scene)
K(1,1) = cam.params[1];
K(0,2) = cam.params[2];
K(1,2) = cam.params[3];
cams.emplace_back(cam);
}
}

// create images list
COLMAP::Images images;
CameraArr cameras;
float maxNumPointsSparse(0);
const float avgViewsPerPoint(3.f);
const uint32_t avgResolutionSmallView(640*480), avgResolutionLargeView(6000*4000);
const uint32_t avgPointsPerSmallView(3000), avgPointsPerLargeView(12000);
{
images.resize(scene.images.size());
cameras.resize(scene.images.size());
Expand All @@ -641,41 +648,125 @@ bool ExportScene(const String& strFolder, const Interface& scene)
camera.R = pose.R;
camera.C = pose.C;
camera.ComposeP();
const COLMAP::Camera& cam = cams[image.platformID];
const uint32_t resolutionView(cam.width*cam.height);
const float linearFactor(float(avgResolutionLargeView-resolutionView)/(avgResolutionLargeView-avgResolutionSmallView));
maxNumPointsSparse += (avgPointsPerSmallView+(avgPointsPerLargeView-avgPointsPerSmallView)*linearFactor)/avgViewsPerPoint;
}
}

// write points list
{
const String filenamePoints(strFolder+COLMAP_POINTS);
LOG_OUT() << "Writing points: " << filenamePoints << std::endl;
std::ofstream file(filenamePoints);
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", filenamePoints.c_str());
return false;
// auto-select dense or sparse mode based on number of points
const bool bSparsePointCloud(scene.vertices.size() < (size_t)maxNumPointsSparse);
if (bSparsePointCloud) {
// write points list
{
const String filenamePoints(strFolder+COLMAP_POINTS);
LOG_OUT() << "Writing points: " << filenamePoints << std::endl;
std::ofstream file(filenamePoints);
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", filenamePoints.c_str());
return false;
}
file << _T("# 3D point list with one line of data per point:") << std::endl;
file << _T("# POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)") << std::endl;
for (uint32_t ID=0; ID<(uint32_t)scene.vertices.size(); ++ID) {
const Interface::Vertex& vertex = scene.vertices[ID];
COLMAP::Point point;
point.ID = ID;
point.p = vertex.X;
for (const Interface::Vertex::View& view: vertex.views) {
COLMAP::Image& img = images[view.imageID];
COLMAP::Point::Track track;
track.idImage = view.imageID;
track.idProj = (uint32_t)img.projs.size();
point.tracks.push_back(track);
COLMAP::Image::Proj proj;
proj.idPoint = ID;
const Point3 X(vertex.X);
ProjectVertex_3x4_3_2(cameras[view.imageID].P.val, X.ptr(), proj.p.data());
img.projs.push_back(proj);
}
point.c = scene.verticesColor.empty() ? Interface::Col3(255,255,255) : scene.verticesColor[ID].c;
point.e = 0;
if (!point.Write(file))
return false;
}
}
file << _T("# 3D point list with one line of data per point:") << std::endl;
file << _T("# POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)") << std::endl;
for (uint32_t ID=0; ID<(uint32_t)scene.vertices.size(); ++ID) {
const Interface::Vertex& vertex = scene.vertices[ID];
COLMAP::Point point;
point.ID = ID;
point.p = vertex.X;
for (const Interface::Vertex::View& view: vertex.views) {
COLMAP::Image& img = images[view.imageID];
COLMAP::Point::Track track;
track.idImage = view.imageID;
track.idProj = (uint32_t)img.projs.size();
point.tracks.push_back(track);
COLMAP::Image::Proj proj;
proj.idPoint = ID;
const Point3 X(vertex.X);
ProjectVertex_3x4_3_2(cameras[view.imageID].P.val, X.ptr(), proj.p.data());
img.projs.push_back(proj);

Util::ensureFolder(strFolder+COLMAP_STEREO_FOLDER);

// write fusion list
{
const String filenameFusion(strFolder+COLMAP_FUSION);
LOG_OUT() << "Writing fusion configuration: " << filenameFusion << std::endl;
std::ofstream file(filenameFusion);
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", filenameFusion.c_str());
return false;
}
point.c = scene.verticesColor.empty() ? Interface::Col3(255,255,255) : scene.verticesColor[ID].c;
point.e = 0;
if (!point.Write(file))
for (const COLMAP::Image& img: images) {
if (img.projs.empty())
continue;
file << img.name << std::endl;
if (file.fail())
return false;
}
}

// write patch-match list
{
const String filenameFusion(strFolder+COLMAP_PATCHMATCH);
LOG_OUT() << "Writing patch-match configuration: " << filenameFusion << std::endl;
std::ofstream file(filenameFusion);
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", filenameFusion.c_str());
return false;
}
for (const COLMAP::Image& img: images) {
if (img.projs.empty())
continue;
file << img.name << std::endl;
if (file.fail())
return false;
file << _T("__auto__, 20") << std::endl;
if (file.fail())
return false;
}
}

Util::ensureFolder(strFolder+COLMAP_STEREO_CONSISTENCYGRAPHS_FOLDER);
Util::ensureFolder(strFolder+COLMAP_STEREO_DEPTHMAPS_FOLDER);
Util::ensureFolder(strFolder+COLMAP_STEREO_NORMALMAPS_FOLDER);
} else {
// export dense point-cloud
const String filenameDensePoints(strFolder+COLMAP_DENSE_POINTS);
const String filenameDenseVisPoints(strFolder+COLMAP_DENSE_POINTS_VISIBILITY);
LOG_OUT() << "Writing points: " << filenameDensePoints << " and " << filenameDenseVisPoints << std::endl;
File file(filenameDenseVisPoints, File::WRITE, File::CREATE | File::TRUNCATE);
if (!file.isOpen()) {
VERBOSE("error: unable to write file '%s'", filenameDenseVisPoints.c_str());
return false;
}
const uint64_t numPoints(scene.vertices.size());
file.write(&numPoints, sizeof(uint64_t));
PointCloud pointcloud;
for (size_t i=0; i<numPoints; ++i) {
const Interface::Vertex& vertex = scene.vertices[i];
pointcloud.points.emplace_back(vertex.X);
if (!scene.verticesNormal.empty())
pointcloud.normals.emplace_back(scene.verticesNormal[i].n);
if (!scene.verticesColor.empty())
pointcloud.colors.emplace_back(scene.verticesColor[i].c);
const uint32_t numViews((uint32_t)vertex.views.size());
file.write(&numViews, sizeof(uint32_t));
for (uint32_t v=0; v<numViews; ++v) {
const Interface::Vertex::View& view = vertex.views[v];
file.write(&view.imageID, sizeof(uint32_t));
}
}
if (!pointcloud.Save(filenameDensePoints, true)) {
VERBOSE("error: unable to write file '%s'", filenameDensePoints.c_str());
return false;
}
}

Expand All @@ -692,55 +783,10 @@ bool ExportScene(const String& strFolder, const Interface& scene)
file << _T("# IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME") << std::endl;
file << _T("# POINTS2D[] as (X, Y, POINT3D_ID)") << std::endl;
for (const COLMAP::Image& img: images) {
if (!img.projs.empty() && !img.Write(file))
if ((bSparsePointCloud && img.projs.empty()) || !img.Write(file))
return false;
}
}

Util::ensureFolder(strFolder+COLMAP_STEREO_FOLDER);

// write fusion list
{
const String filenameFusion(strFolder+COLMAP_FUSION);
LOG_OUT() << "Writing fusion configuration: " << filenameFusion << std::endl;
std::ofstream file(filenameFusion);
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", filenameFusion.c_str());
return false;
}
for (const COLMAP::Image& img: images) {
if (img.projs.empty())
continue;
file << img.name << std::endl;
if (file.fail())
return false;
}
}

// write patch-match list
{
const String filenameFusion(strFolder+COLMAP_PATCHMATCH);
LOG_OUT() << "Writing patch-match configuration: " << filenameFusion << std::endl;
std::ofstream file(filenameFusion);
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", filenameFusion.c_str());
return false;
}
for (const COLMAP::Image& img: images) {
if (img.projs.empty())
continue;
file << img.name << std::endl;
if (file.fail())
return false;
file << _T("__auto__, 20") << std::endl;
if (file.fail())
return false;
}
}

Util::ensureFolder(strFolder+COLMAP_STEREO_CONSISTENCYGRAPHS_FOLDER);
Util::ensureFolder(strFolder+COLMAP_STEREO_DEPTHMAPS_FOLDER);
Util::ensureFolder(strFolder+COLMAP_STEREO_NORMALMAPS_FOLDER);
return true;
}

Expand Down
Loading