diff --git a/apps/DensifyPointCloud/DensifyPointCloud.cpp b/apps/DensifyPointCloud/DensifyPointCloud.cpp index 7637e60bb..84b241d6f 100644 --- a/apps/DensifyPointCloud/DensifyPointCloud.cpp +++ b/apps/DensifyPointCloud/DensifyPointCloud.cpp @@ -54,6 +54,7 @@ String strOutputViewNeighborsFileName; String strMeshFileName; String strExportROIFileName; String strImportROIFileName; +String strCropROIFileName; String strDenseConfigFileName; String strExportDepthMapsName; String strMaskPath; @@ -176,6 +177,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv) ("mesh-file", boost::program_options::value(&OPT::strMeshFileName), "mesh file name used for image pair overlap estimation") ("export-roi-file", boost::program_options::value(&OPT::strExportROIFileName), "ROI file name to be exported form the scene") ("import-roi-file", boost::program_options::value(&OPT::strImportROIFileName), "ROI file name to be imported into the scene") + ("crop-roi-file", boost::program_options::value(&OPT::strCropROIFileName), "ROI file name to crop the scene keeping only the points inside ROI and the cameras seeing them") ("dense-config-file", boost::program_options::value(&OPT::strDenseConfigFileName), "optional configuration file for the densifier (overwritten by the command line options)") ("export-depth-maps-name", boost::program_options::value(&OPT::strExportDepthMapsName), "render given mesh and save the depth-map for every image to this file name base (empty - disabled)") ; @@ -231,6 +233,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv) Util::ensureValidPath(OPT::strMeshFileName); Util::ensureValidPath(OPT::strExportROIFileName); Util::ensureValidPath(OPT::strImportROIFileName); + Util::ensureValidPath(OPT::strCropROIFileName); if (OPT::strOutputFileName.empty()) OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + _T("_dense.mvs"); @@ -321,6 +324,15 @@ int main(int argc, LPCTSTR* argv) } } } + if (!OPT::strCropROIFileName.empty()) { + std::ifstream fs(MAKE_PATH_SAFE(OPT::strCropROIFileName)); + if (!fs) + return EXIT_FAILURE; + fs >> scene.obb; + scene.CropToROI(scene.obb); + scene.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType); + return EXIT_SUCCESS; + } if (!OPT::strImportROIFileName.empty()) { std::ifstream fs(MAKE_PATH_SAFE(OPT::strImportROIFileName)); if (!fs) diff --git a/libs/Common/AABB.h b/libs/Common/AABB.h index 84099691e..9d550c150 100644 --- a/libs/Common/AABB.h +++ b/libs/Common/AABB.h @@ -96,8 +96,8 @@ class TAABB inline TYPE operator [] (BYTE i) const { ASSERT(i> (std::istream& st, TAABB& obb) { diff --git a/libs/Common/Common.h b/libs/Common/Common.h index 0decd1ace..ddcbf0ced 100644 --- a/libs/Common/Common.h +++ b/libs/Common/Common.h @@ -80,7 +80,7 @@ namespace SEACAVE { extern int g_nVerbosityLevel; } #ifdef PRINT_ASSERT_MSG #undef PRINT_ASSERT_MSG -#define PRINT_ASSERT_MSG(exp, ...) {std::cout << SEACAVE::PrintMessageToString("ASSERTION FAILED: (" #exp ") ", __VA_ARGS__) << std::endl;} +#define PRINT_ASSERT_MSG(exp, ...) {std::cout << SEACAVE::PrintMessageToString("ASSERTION FAILED: (" #exp ") ", ##__VA_ARGS__) << std::endl;} #endif diff --git a/libs/Common/Config.h b/libs/Common/Config.h index 69363eae9..d4758331a 100644 --- a/libs/Common/Config.h +++ b/libs/Common/Config.h @@ -227,16 +227,16 @@ #include #ifdef _INC_CRTDBG #define SIMPLE_ASSERT(exp) {if (!(exp) && 1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, #exp)) _CrtDbgBreak();} -#define ASSERT(exp, ...) {static bool bIgnore(false); if (!bIgnore && !(exp)) {PRINT_ASSERT_MSG(exp, __VA_ARGS__); if (!(bIgnore = !(1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, #exp)))) _CrtDbgBreak();}} +#define ASSERT(exp, ...) {static bool bIgnore(false); if (!bIgnore && !(exp)) {PRINT_ASSERT_MSG(exp, ##__VA_ARGS__); if (!(bIgnore = !(1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, #exp)))) _CrtDbgBreak();}} #else #define SIMPLE_ASSERT(exp) {if (!(exp)) __debugbreak();} -#define ASSERT(exp, ...) {if (!(exp)) {PRINT_ASSERT_MSG(exp, __VA_ARGS__); __debugbreak();}} +#define ASSERT(exp, ...) {if (!(exp)) {PRINT_ASSERT_MSG(exp, ##__VA_ARGS__); __debugbreak();}} #endif // _INC_CRTDBG #define TRACE(...) {TCHAR buffer[2048]; _sntprintf(buffer, 2048, __VA_ARGS__); OutputDebugString(buffer);} #else // _MSC_VER #include #define SIMPLE_ASSERT(exp) {if (!(exp)) assert(exp);} -#define ASSERT(exp, ...) {if (!(exp)) {PRINT_ASSERT_MSG(exp, __VA_ARGS__); assert(exp);}} +#define ASSERT(exp, ...) {if (!(exp)) {PRINT_ASSERT_MSG(exp, ##__VA_ARGS__); assert(exp);}} #define TRACE(...) #endif // _MSC_VER @@ -248,10 +248,10 @@ #else #ifdef _MSC_VER #define SIMPLE_ASSERT(exp) {if (!(exp)) __debugbreak();} -#define ASSERT(exp, ...) {if (!(exp)) {PRINT_ASSERT_MSG(exp, __VA_ARGS__); __debugbreak();}} +#define ASSERT(exp, ...) {if (!(exp)) {PRINT_ASSERT_MSG(exp, ##__VA_ARGS__); __debugbreak();}} #else // _MSC_VER #define SIMPLE_ASSERT(exp) {if (!(exp)) __builtin_trap();} -#define ASSERT(exp, ...) {if (!(exp)) {PRINT_ASSERT_MSG(exp, __VA_ARGS__); __builtin_trap();}} +#define ASSERT(exp, ...) {if (!(exp)) {PRINT_ASSERT_MSG(exp, ##__VA_ARGS__); __builtin_trap();}} #endif // _MSC_VER #endif #define TRACE(...) diff --git a/libs/Common/List.h b/libs/Common/List.h index bf45e47a2..b1a765971 100644 --- a/libs/Common/List.h +++ b/libs/Common/List.h @@ -135,6 +135,15 @@ class cList _ArrayConstruct(_vector, size); } + // construct a list from the contents of the range [first, last) + template + cList(InputIt first, InputIt last, bool /*dummy*/) : _size(std::distance(first, last)), _vectorSize(_size), _vector((TYPE*)operator new[](static_cast(_size) * sizeof(TYPE))) + { + while (first != last) + Insert(*first++); + } + + // copy constructor: creates a deep-copy of the given list cList(const cList& rList) : _size(rList._size), _vectorSize(rList._vectorSize), _vector(NULL) { diff --git a/libs/Common/OBB.h b/libs/Common/OBB.h index adf321e5a..c4f09939f 100644 --- a/libs/Common/OBB.h +++ b/libs/Common/OBB.h @@ -94,9 +94,9 @@ class TOBB inline TYPE operator [] (BYTE i) const { ASSERT(i> (std::istream& st, TOBB& obb) { diff --git a/libs/Common/Types.inl b/libs/Common/Types.inl index c0ca1e8fe..0433a7ce4 100644 --- a/libs/Common/Types.inl +++ b/libs/Common/Types.inl @@ -68,6 +68,12 @@ struct hash> { return seed; } }; +template <> +struct hash { + size_t operator()(const SEACAVE::PairIdx& v) const { + return std::hash()(v.idx); + } +}; // adds the given key-value pair in the map, overwriting the current value if the key exists template diff --git a/libs/Common/Util.h b/libs/Common/Util.h index 0f6af868b..e5ca28190 100644 --- a/libs/Common/Util.h +++ b/libs/Common/Util.h @@ -598,34 +598,34 @@ class GENERAL_API Util uint32_t rez = (uint32_t)(sTime / ((int64_t)24*3600*1000)); if (rez) { ++nrNumbers; - len += _sntprintf(buf+len, 128, "%ud", rez); + len += _sntprintf(buf, 128, "%ud", rez); } if (nAproximate > 3 && nrNumbers > 0) return buf; rez = (uint32_t)((sTime%((int64_t)24*3600*1000)) / (3600*1000)); if (rez) { ++nrNumbers; - len += _sntprintf(buf+len, 128, "%uh", rez); + len += _sntprintf(buf+len, 128-len, "%uh", rez); } if (nAproximate > 2 && nrNumbers > 0) return buf; rez = (uint32_t)((sTime%((int64_t)3600*1000)) / (60*1000)); if (rez) { ++nrNumbers; - len += _sntprintf(buf+len, 128, "%um", rez); + len += _sntprintf(buf+len, 128-len, "%um", rez); } if (nAproximate > 1 && nrNumbers > 0) return buf; rez = (uint32_t)((sTime%((int64_t)60*1000)) / (1*1000)); if (rez) { ++nrNumbers; - len += _sntprintf(buf+len, 128, "%us", rez); + len += _sntprintf(buf+len, 128-len, "%us", rez); } if (nAproximate > 0 && nrNumbers > 0) return buf; rez = (uint32_t)(sTime%((int64_t)1*1000)); if (rez || !nrNumbers) - len += _sntprintf(buf+len, 128, "%ums", rez); + len += _sntprintf(buf+len, 128-len, "%ums", rez); return String(buf, len); } diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index a81bd46d3..40327f692 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -1358,81 +1358,8 @@ bool Scene::ExportChunks(const ImagesChunkArr& chunks, const String& path, ARCHI { FOREACH(chunkID, chunks) { const ImagesChunk& chunk = chunks[chunkID]; - Scene subset; - subset.nCalibratedImages = (IIndex)chunk.images.size(); - // extract chunk images - typedef std::unordered_map MapIIndex; - MapIIndex mapPlatforms(platforms.size()); - MapIIndex mapImages(images.size()); - FOREACH(idxImage, images) { - if (chunk.images.find(idxImage) == chunk.images.end()) - continue; - const Image& image = images[idxImage]; - if (!image.IsValid()) - continue; - // copy platform - const Platform& platform = platforms[image.platformID]; - MapIIndex::iterator itSubPlatformMVS = mapPlatforms.find(image.platformID); - uint32_t subPlatformID; - if (itSubPlatformMVS == mapPlatforms.end()) { - ASSERT(subset.platforms.size() == mapPlatforms.size()); - subPlatformID = subset.platforms.size(); - mapPlatforms.emplace(image.platformID, subPlatformID); - Platform subPlatform; - subPlatform.name = platform.name; - subPlatform.cameras = platform.cameras; - subset.platforms.emplace_back(std::move(subPlatform)); - } else { - subPlatformID = itSubPlatformMVS->second; - } - Platform& subPlatform = subset.platforms[subPlatformID]; - // copy image - const IIndex idxImageNew((IIndex)mapImages.size()); - mapImages[idxImage] = idxImageNew; - Image subImage(image); - subImage.platformID = subPlatformID; - subImage.poseID = subPlatform.poses.size(); - subImage.ID = idxImage; - subset.images.emplace_back(std::move(subImage)); - // copy pose - subPlatform.poses.emplace_back(platform.poses[image.poseID]); - } - // map image IDs from global to local - for (Image& image: subset.images) { - RFOREACH(i, image.neighbors) { - ViewScore& neighbor = image.neighbors[i]; - const auto itImage(mapImages.find(neighbor.ID)); - if (itImage == mapImages.end()) { - image.neighbors.RemoveAtMove(i); - continue; - } - ASSERT(itImage->second < subset.images.size()); - neighbor.ID = itImage->second; - } - } - // extract point-cloud - FOREACH(idxPoint, pointcloud.points) { - PointCloud::ViewArr subViews; - PointCloud::WeightArr subWeights; - const PointCloud::ViewArr& views = pointcloud.pointViews[idxPoint]; - FOREACH(i, views) { - const IIndex idxImage(views[i]); - const auto itImage(mapImages.find(idxImage)); - if (itImage == mapImages.end()) - continue; - subViews.emplace_back(itImage->second); - if (!pointcloud.pointWeights.empty()) - subWeights.emplace_back(pointcloud.pointWeights[idxPoint][i]); - } - if (subViews.size() < 2) - continue; - subset.pointcloud.points.emplace_back(pointcloud.points[idxPoint]); - subset.pointcloud.pointViews.emplace_back(std::move(subViews)); - if (!pointcloud.pointWeights.empty()) - subset.pointcloud.pointWeights.emplace_back(std::move(subWeights)); - if (!pointcloud.colors.empty()) - subset.pointcloud.colors.emplace_back(pointcloud.colors[idxPoint]); - } + IIndexArr idxImages(chunk.images.begin(), chunk.images.end(), true); + Scene subset = SubScene(idxImages); // set scene ROI subset.obb.Set(OBB3f::MATRIX::Identity(), chunk.aabb.ptMin, chunk.aabb.ptMax); // serialize out the current state @@ -1666,6 +1593,128 @@ void Scene::AddNoiseCameraPoses(float epsPosition, float epsRotation) imageData.UpdateCamera(platforms); } } + +// fetch sub-scene composed of the given image indices +Scene Scene::SubScene(const IIndexArr& idxImages) const +{ + ASSERT(!idxImages.empty()); + Scene subScene(nMaxThreads); + subScene.obb = obb; + subScene.nCalibratedImages = 0; + // export images and poses + std::unordered_map mapImages; + std::unordered_map mapPlatforms; + std::unordered_map mapPlatformCamera; + for (IIndex idxImage: idxImages) { + const Image& image = images[idxImage]; + if (!image.IsValid()) + continue; + const Platform& platform = platforms[image.platformID]; + const Platform::Camera& camera = platform.cameras[image.cameraID]; + const auto platformIt(mapPlatforms.emplace(image.platformID, (uint32_t)mapPlatforms.size())); + const uint32_t platformID(platformIt.first->second); + if (platformIt.second) { + // create new platform + Platform& subPlatform = subScene.platforms.AddEmpty(); + subPlatform.name = platform.name; + } + Platform& subPlatform = subScene.platforms[platformID]; + const auto platformCameraIt(mapPlatformCamera.emplace(PairIdx(image.platformID,image.cameraID), PairIdx(platformID,subPlatform.cameras.size()))); + if (platformCameraIt.second) { + // create new camera + subPlatform.cameras.emplace_back(camera); + } + mapImages.emplace(idxImage, subScene.images.size()); + Image& subImage = subScene.images.emplace_back(image); + if (subImage.ID == NO_ID) + subImage.ID = idxImage; + subImage.platformID = platformCameraIt.first->second.i; + subImage.cameraID = platformCameraIt.first->second.j; + if (!image.IsValid()) + continue; + subImage.poseID = subPlatform.poses.size(); + subPlatform.poses.emplace_back(platform.poses[image.poseID]); + ++subScene.nCalibratedImages; + } + ASSERT(!mapImages.empty()); + if (mapImages.size() < 2 || subScene.nCalibratedImages == nCalibratedImages) + return *this; + // remap image neighbors + for (Image& image: subScene.images) { + ASSERT(image.IsValid()); + RFOREACH(idxN, image.neighbors) { + ViewScore& neighbor = image.neighbors[idxN]; + const auto itImage(mapImages.find(neighbor.ID)); + if (itImage == mapImages.end()) { + image.neighbors.RemoveAtMove(idxN); + continue; + } + ASSERT(itImage->second < subScene.images.size()); + neighbor.ID = itImage->second; + } + } + // export points + FOREACH(idxPoint, pointcloud.points) { + PointCloud::ViewArr subPointViews; + PointCloud::WeightArr subPointWeights; + const PointCloud::ViewArr& views = pointcloud.pointViews[idxPoint]; + FOREACH(idxView, views) { + const PointCloud::View idxImage = views[idxView]; + const auto it(mapImages.find(idxImage)); + if (it == mapImages.end()) + continue; + subPointViews.push_back(it->second); + if (!pointcloud.pointWeights.empty()) + subPointWeights.push_back(pointcloud.pointWeights[idxPoint][idxView]); + } + if (subPointViews.size() < 2) + continue; + subScene.pointcloud.points.emplace_back(pointcloud.points[idxPoint]); + subScene.pointcloud.pointViews.emplace_back(std::move(subPointViews)); + if (!subPointWeights.empty()) + subScene.pointcloud.pointWeights.emplace_back(std::move(subPointWeights)); + if (!pointcloud.normals.empty()) + subScene.pointcloud.normals.emplace_back(pointcloud.normals[idxPoint]); + if (!pointcloud.colors.empty()) + subScene.pointcloud.colors.emplace_back(pointcloud.colors[idxPoint]); + } + subScene.mesh = mesh; + return subScene; +} + +// remove all points outside the given bounding-box and keep only the cameras that see the remaining points +// - minNumPoints: minimum number of points to keep the camera +Scene& Scene::CropToROI(const OBB3f& obb, unsigned minNumPoints) +{ + ASSERT(obb.IsValid()); + // remove geometry outside the ROI + if (!pointcloud.IsEmpty()) + pointcloud.RemovePointsOutside(obb); + if (!mesh.IsEmpty()) + mesh.RemoveFacesOutside(obb); + // remove cameras that do not see any points + if (minNumPoints == 0 || !pointcloud.IsValid()) + return *this; + UnsignedArr visibility(images.size()); + visibility.Memset(0); + for (const PointCloud::ViewArr& views: pointcloud.pointViews) { + for (const PointCloud::View& idxImage: views) { + const Image& imageData = images[idxImage]; + if (!imageData.IsValid()) + continue; + ++visibility[idxImage]; + } + } + IIndexArr idxImages; + FOREACH(idxImage, images) { + const Image& imageData = images[idxImage]; + if (!imageData.IsValid()) + continue; + if (visibility[idxImage] >= minNumPoints) + idxImages.emplace_back(idxImage); + } + return *this = SubScene(idxImages); +} /*----------------------------------------------------------------*/ diff --git a/libs/MVS/Scene.h b/libs/MVS/Scene.h index c3a9198ca..e83d133dd 100644 --- a/libs/MVS/Scene.h +++ b/libs/MVS/Scene.h @@ -118,6 +118,8 @@ class MVS_API Scene bool AlignTo(const Scene&); REAL ComputeLeveledVolume(float planeThreshold=0, float sampleMesh=-100000, unsigned upAxis=2, bool verbose=true); void AddNoiseCameraPoses(float epsPosition, float epsRotation); + Scene SubScene(const IIndexArr& idxImages) const; + Scene& CropToROI(const OBB3f&, unsigned minNumPoints = 3); // Estimate and set region-of-interest bool EstimateROI(int nEstimateROI=0, float scale=1.f);