Skip to content

Commit

Permalink
mesh: add crop to ROI
Browse files Browse the repository at this point in the history
  • Loading branch information
cdcseacave committed Oct 17, 2024
1 parent 6a4517b commit bc6d2f9
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 78 deletions.
12 changes: 12 additions & 0 deletions apps/DensifyPointCloud/DensifyPointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ String strOutputViewNeighborsFileName;
String strMeshFileName;
String strExportROIFileName;
String strImportROIFileName;
String strCropROIFileName;
String strDenseConfigFileName;
String strExportDepthMapsName;
String strMaskPath;
Expand Down Expand Up @@ -176,6 +177,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv)
("mesh-file", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name used for image pair overlap estimation")
("export-roi-file", boost::program_options::value<std::string>(&OPT::strExportROIFileName), "ROI file name to be exported form the scene")
("import-roi-file", boost::program_options::value<std::string>(&OPT::strImportROIFileName), "ROI file name to be imported into the scene")
("crop-roi-file", boost::program_options::value<std::string>(&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<std::string>(&OPT::strDenseConfigFileName), "optional configuration file for the densifier (overwritten by the command line options)")
("export-depth-maps-name", boost::program_options::value<std::string>(&OPT::strExportDepthMapsName), "render given mesh and save the depth-map for every image to this file name base (empty - disabled)")
;
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions libs/Common/List.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ class cList
_ArrayConstruct(_vector, size);
}

// construct a list from the contents of the range [first, last)
template <class InputIt>
cList(InputIt first, InputIt last, bool /*dummy*/) : _size(std::distance(first, last)), _vectorSize(_size), _vector((TYPE*)operator new[](static_cast<size_t>(_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)
{
Expand Down
6 changes: 3 additions & 3 deletions libs/Common/OBB.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ class TOBB
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return m_rot.data()[i]; }

friend std::ostream& operator << (std::ostream& st, const TOBB& obb) {
st << obb.m_rot; st << std::endl;
st << obb.m_pos; st << std::endl;
st << obb.m_ext; st << std::endl;
st << obb.m_rot << std::endl;
st << obb.m_pos.transpose() << std::endl;
st << obb.m_ext.transpose() << std::endl;
return st;
}
friend std::istream& operator >> (std::istream& st, TOBB& obb) {
Expand Down
6 changes: 6 additions & 0 deletions libs/Common/Types.inl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ struct hash<cv::Point3_<T>> {
return seed;
}
};
template <>
struct hash<SEACAVE::PairIdx> {
size_t operator()(const SEACAVE::PairIdx& v) const {
return std::hash<SEACAVE::PairIdx::PairIndex>()(v.idx);
}
};

// adds the given key-value pair in the map, overwriting the current value if the key exists
template <typename Key, typename T>
Expand Down
199 changes: 124 additions & 75 deletions libs/MVS/Scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IIndex,IIndex> 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
Expand Down Expand Up @@ -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<IIndex,IIndex> mapImages;
std::unordered_map<uint32_t,uint32_t> mapPlatforms;
std::unordered_map<PairIdx,PairIdx> 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);
}
/*----------------------------------------------------------------*/


Expand Down
2 changes: 2 additions & 0 deletions libs/MVS/Scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit bc6d2f9

Please sign in to comment.