Skip to content

Commit

Permalink
transform: add scene coordinates normalization
Browse files Browse the repository at this point in the history
  • Loading branch information
cdcseacave committed Jan 26, 2025
1 parent c48731f commit 7df46a6
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 42 deletions.
15 changes: 13 additions & 2 deletions apps/DensifyPointCloud/DensifyPointCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ bool bCrop2ROI;
int nEstimateROI;
int nTowerMode;
int nFusionMode;
unsigned nNormalizeCoordinates;
float fEstimateScale;
int thFilterPointCloud;
int nExportNumViews;
Expand Down Expand Up @@ -168,6 +169,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv)
("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(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)")
("normalize-coordinates", boost::program_options::value(&OPT::nNormalizeCoordinates)->default_value(0), "normalize scene coordinates and output the inverse transform to file (0 - disabled, 1 - center, 2 - center & scale)")
;

// hidden options, allowed both on command line and
Expand Down Expand Up @@ -345,8 +347,10 @@ int main(int argc, LPCTSTR* argv)
VERBOSE("error: cannot load ROI file");
return EXIT_FAILURE;
}
scene.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
return EXIT_SUCCESS;
if (!OPT::bCrop2ROI) {
scene.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
return EXIT_SUCCESS;
}
}
if (!scene.IsBounded())
scene.EstimateROI(OPT::nEstimateROI, 1.1f);
Expand Down Expand Up @@ -422,8 +426,15 @@ int main(int argc, LPCTSTR* argv)
scene.pointcloud.SaveWithScale(baseFileName+_T("_scale.ply"), scene.images, OPT::fEstimateScale);
return EXIT_SUCCESS;
}
if (OPT::nNormalizeCoordinates > 0) {
// normalize scene coordinates
const Matrix4x4 normalizeTransform = scene.ComputeNormalizationTransform(OPT::nNormalizeCoordinates == 2).inv();
scene.Transform(*reinterpret_cast<const Matrix3x4*>(normalizeTransform.val));
VERBOSE("Scene coordinates normalized");
}
PointCloud sparsePointCloud;
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS || sceneType == Scene::SCENE_INTERFACE) {
// estimate depth-maps and densify the point-cloud
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 1 && !scene.pointcloud.IsEmpty())
scene.pointcloud.PrintStatistics(scene.images.data(), &scene.obb);
Expand Down
86 changes: 55 additions & 31 deletions apps/TransformScene/TransformScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,28 @@ using namespace MVS;
namespace {

namespace OPT {
String strInputFileName;
String strPointCloudFileName;
String strMeshFileName;
String strOutputFileName;
String strAlignFileName;
String strTransformFileName;
String strTransferTextureFileName;
String strIndicesFileName;
bool bComputeVolume;
float fEpsNoisePosition;
float fEpsNoiseRotation;
float fPlaneThreshold;
float fSampleMesh;
unsigned nMaxResolution;
unsigned nUpAxis;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strExportType;
String strConfigFileName;
boost::program_options::variables_map vm;
String strInputFileName;
String strPointCloudFileName;
String strMeshFileName;
String strOutputFileName;
String strAlignFileName;
String strTransformFileName;
String strTransferTextureFileName;
String strIndicesFileName;
bool bComputeVolume;
float fEpsNoisePosition;
float fEpsNoiseRotation;
float fPlaneThreshold;
float fSampleMesh;
unsigned nMaxResolution;
unsigned nUpAxis;
unsigned nNormalizeCoordinates;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strExportType;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT

class Application {
Expand Down Expand Up @@ -124,7 +125,8 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv)
("plane-threshold", boost::program_options::value(&OPT::fPlaneThreshold)->default_value(0.f), "threshold used to estimate the ground plane (<0 - disabled, 0 - auto, >0 - desired threshold)")
("sample-mesh", boost::program_options::value(&OPT::fSampleMesh)->default_value(-300000.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)")
("max-resolution", boost::program_options::value(&OPT::nMaxResolution)->default_value(0), "make sure image resolution are not not larger than this (0 - disabled)")
("up-axis", boost::program_options::value(&OPT::nUpAxis)->default_value(2), "scene axis considered to point upwards (0 - x, 1 - y, 2 - z)")
("up-axis", boost::program_options::value(&OPT::nUpAxis)->default_value(2), "scene axis considered to point upwards when computing the volume (0 - x, 1 - y, 2 - z)")
("normalize-coordinates", boost::program_options::value(&OPT::nNormalizeCoordinates)->default_value(0), "normalize scene coordinates and output the inverse transform to file (0 - disabled, 1 - center, 2 - center & scale, 3 - invert internal transform)")
;

boost::program_options::options_description cmdline_options;
Expand Down Expand Up @@ -168,7 +170,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv)
Util::ensureValidPath(OPT::strIndicesFileName);
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower());
const bool bInvalidCommand(OPT::strInputFileName.empty() ||
(OPT::strAlignFileName.empty() && OPT::strTransformFileName.empty() && OPT::strTransferTextureFileName.empty() && !OPT::bComputeVolume));
(OPT::strAlignFileName.empty() && OPT::strTransformFileName.empty() && OPT::strTransferTextureFileName.empty() && !OPT::bComputeVolume && OPT::nNormalizeCoordinates == 0));
if (OPT::vm.count("help") || bInvalidCommand) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
Expand All @@ -192,7 +194,7 @@ bool Application::Initialize(size_t argc, LPCTSTR* argv)
Util::ensureValidPath(OPT::strPointCloudFileName);
Util::ensureValidPath(OPT::strMeshFileName);
Util::ensureValidPath(OPT::strOutputFileName);
if (OPT::strMeshFileName.empty() && (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS && strInputFileNameExt == MVS_EXT)
if (OPT::strMeshFileName.empty() && (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS && strInputFileNameExt == MVS_EXT && OPT::nNormalizeCoordinates == 0)
OPT::strMeshFileName = Util::getFileFullName(OPT::strInputFileName) + _T(".ply");
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFileName(OPT::strInputFileName) + _T("_transformed") MVS_EXT;
Expand Down Expand Up @@ -259,7 +261,28 @@ int main(int argc, LPCTSTR* argv)
VERBOSE("Scene aligned to the given reference scene (%s)", TD_TIMER_GET_FMT().c_str());
}

if (!OPT::strTransformFileName.empty()) {
if (OPT::nNormalizeCoordinates > 0) {
// normalize scene coordinates and output transform to file
Matrix4x4 transform;
if (OPT::nNormalizeCoordinates == 3)
transform = scene.transform;
else
transform = scene.ComputeNormalizationTransform(OPT::nNormalizeCoordinates == 2);
const Matrix4x4 normalizeTransform = transform.inv();
scene.Transform(*reinterpret_cast<const Matrix3x4*>(normalizeTransform.val));
if (!OPT::strTransformFileName.empty()) {
std::ofstream file(MAKE_PATH_SAFE(OPT::strTransformFileName));
file << static_cast<const Matrix4x4::CEMatMap>(transform) << std::endl;
if (file.fail()) {
VERBOSE("error: cannot save transformation matrix");
return EXIT_FAILURE;
}
VERBOSE("Scene transformation matrix saved to '%s'", Util::getFileNameExt(OPT::strTransformFileName).c_str());
}
VERBOSE("Scene coordinates normalized (%s)", TD_TIMER_GET_FMT().c_str());
}

if (!OPT::strTransformFileName.empty() && OPT::nNormalizeCoordinates == 0) {
// transform this scene by the given transform matrix
std::ifstream file(MAKE_PATH_SAFE(OPT::strTransformFileName));
std::string value;
Expand Down Expand Up @@ -326,14 +349,15 @@ int main(int argc, LPCTSTR* argv)
}

// write transformed scene
if (!scene.pointcloud.IsEmpty() && !scene.IsValid()) {
scene.pointcloud.Save(baseFileName + (scene.mesh.IsEmpty() ? _T(".ply") : _T("_pointcloud.ply")), (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS);
}
if (!scene.mesh.IsEmpty() && (!scene.IsValid() || (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS)) {
scene.mesh.Save(baseFileName + (scene.pointcloud.IsEmpty() && scene.IsValid() ? _T("") : _T("_mesh")) + OPT::strExportType);
scene.mesh.Release();
}
if (scene.IsValid())
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType);
if (!scene.IsValid() || (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS) {
if (!scene.pointcloud.IsEmpty())
scene.pointcloud.Save(baseFileName + _T(".ply"), (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS);
if (!scene.mesh.IsEmpty())
scene.mesh.Save(baseFileName + OPT::strExportType);
}
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/
4 changes: 2 additions & 2 deletions libs/Common/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1343,7 +1343,7 @@ class TPoint2 : public cv::Point_<TYPE>
// Access point as Eigen equivalent
inline operator EVec () const { return CEVecMap((const TYPE*)this); }
// Access point as Eigen::Map equivalent
inline operator const CEVecMap () const { return CEVecMap((const TYPE*)this); }
inline operator CEVecMap () const { return CEVecMap((const TYPE*)this); }
inline operator EVecMap () { return EVecMap((TYPE*)this); }
#endif

Expand Down Expand Up @@ -1438,7 +1438,7 @@ class TPoint3 : public cv::Point3_<TYPE>
// Access point as Eigen equivalent
inline operator EVec () const { return CEVecMap((const TYPE*)this); }
// Access point as Eigen::Map equivalent
inline operator const EVecMap () const { return CEVecMap((const TYPE*)this); }
inline operator CEVecMap () const { return CEVecMap((const TYPE*)this); }
inline operator EVecMap () { return EVecMap((TYPE*)this); }
#endif

Expand Down
6 changes: 4 additions & 2 deletions libs/MVS/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1214,7 +1214,8 @@ bool Mesh::Load(const String& fileName)
ret = LoadPLY(fileName);
if (!ret)
return false;
DEBUG_EXTRA("Mesh loaded: %u vertices, %u faces (%s)", vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str());
DEBUG_EXTRA("Mesh '%s' loaded: %u vertices, %u faces (%s)",
Util::getFileNameExt(fileName).c_str(), vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str());
return true;
}
// import the mesh as a PLY file
Expand Down Expand Up @@ -1471,7 +1472,8 @@ bool Mesh::Save(const String& fileName, const cList<String>& comments, bool bBin
ret = SavePLY(ext != _T(".ply") ? String(fileName+_T(".ply")) : fileName, comments, bBinary);
if (!ret)
return false;
DEBUG_EXTRA("Mesh saved: %u vertices, %u faces (%s)", vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str());
DEBUG_EXTRA("Mesh '%s' saved: %u vertices, %u faces (%s)",
Util::getFileNameExt(fileName).c_str(), vertices.size(), faces.size(), TD_TIMER_GET_FMT().c_str());
return true;
}
// export the mesh as a PLY file
Expand Down
62 changes: 58 additions & 4 deletions libs/MVS/Scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,13 @@ bool Scene::LoadInterface(const String & fileName)
// import region of interest
obb.Set(Matrix3x3f(obj.obb.rot), Point3f(obj.obb.ptMin), Point3f(obj.obb.ptMax));

DEBUG_EXTRA("Scene loaded from interface format (%s):\n"
// import transform
transform = obj.transform;

DEBUG_EXTRA("Scene loaded in interface format from '%s' (%s):\n"
"\t%u images (%u calibrated) with a total of %.2f MPixels (%.2f MPixels/image)\n"
"\t%u points, %u vertices, %u faces",
TD_TIMER_GET_FMT().c_str(),
Util::getFileNameExt(fileName).c_str(), TD_TIMER_GET_FMT().c_str(),
images.size(), nCalibratedImages, (double)nTotalPixels/(1024.0*1024.0), (double)nTotalPixels/(1024.0*1024.0*nCalibratedImages),
pointcloud.points.size(), mesh.vertices.size(), mesh.faces.size());
return true;
Expand Down Expand Up @@ -301,14 +304,17 @@ bool Scene::SaveInterface(const String & fileName, int version) const
obj.obb.ptMin = Point3f((obb.m_pos-obb.m_ext).eval());
obj.obb.ptMax = Point3f((obb.m_pos+obb.m_ext).eval());

// export transform
obj.transform = transform;

// serialize out the current state
if (!ARCHIVE::SerializeSave(obj, fileName, version>=0?uint32_t(version):MVSI_PROJECT_VER))
return false;

DEBUG_EXTRA("Scene saved to interface format (%s):\n"
DEBUG_EXTRA("Scene saved in interface format to '%s' (%s):\n"
"\t%u images (%u calibrated)\n"
"\t%u points, %u vertices, %u faces",
TD_TIMER_GET_FMT().c_str(),
Util::getFileNameExt(fileName).c_str(), TD_TIMER_GET_FMT().c_str(),
images.size(), nCalibratedImages,
pointcloud.points.size(), mesh.vertices.size(), mesh.faces.size());
return true;
Expand Down Expand Up @@ -1491,6 +1497,50 @@ bool Scene::ScaleImages(unsigned nMaxResolution, REAL scale, const String& folde
return true;
} // ScaleImages

// compute translation and scale (optional) such that the scene coordinates center at 0 and
// most scene geomatry is in the unit cube ([-0.5,0.5]^3);
// return the transformation matrix that restores the scene to its original coordinates
Matrix4x4 Scene::ComputeNormalizationTransform(bool bScale) const
{
ASSERT(!pointcloud.IsEmpty() || !mesh.IsEmpty());
// compute the center of the scene geometry (point-cloud or mesh)
Point3 center = Point3::ZERO;
if (!mesh.IsEmpty()) {
for (const Mesh::Vertex& X: mesh.vertices)
center += Cast<REAL>(X);
center /= static_cast<REAL>(mesh.vertices.size());
} else {
for (const PointCloud::Point& X: pointcloud.points)
center += Cast<REAL>(X);
center /= static_cast<REAL>(pointcloud.points.size());
}
// compute the scale of the scene geometry (point-cloud or mesh)
REAL scale = 1;
if (bScale) {
REAL avgDist = 0;
if (!mesh.IsEmpty()) {
for (const Mesh::Vertex& X: mesh.vertices)
avgDist += norm(Cast<REAL>(X)-center);
avgDist /= static_cast<REAL>(mesh.vertices.size());
} else {
for (const PointCloud::Point& X: pointcloud.points)
avgDist += norm(Cast<REAL>(X)-center);
avgDist /= static_cast<REAL>(pointcloud.points.size());
}
scale = REAL(2) * avgDist;
}
// compute the transformation matrix
Matrix4x4 transform = Matrix4x4::ZERO;
transform(0,0) = scale;
transform(1,1) = scale;
transform(2,2) = scale;
transform(0,3) = center.x;
transform(1,3) = center.y;
transform(2,3) = center.z;
transform(3,3) = 1;
return transform;
} // ComputeNormalizationTransform

// apply similarity transform
void Scene::Transform(const Matrix3x3& rotation, const Point3& translation, REAL scale)
{
Expand Down Expand Up @@ -1522,6 +1572,10 @@ void Scene::Transform(const Matrix3x3& rotation, const Point3& translation, REAL
obb.Transform(Cast<float>(rotationScale));
obb.Translate(Cast<float>(translation));
}
transform = Matrix4x4::IDENTITY;
Matrix4x4::EMatMap mapTransform(transform);
mapTransform.topLeftCorner<3,3>() = static_cast<Matrix3x3::CEMatMap>(rotationScale);
mapTransform.topRightCorner<3,1>() = static_cast<Point3::CEVecMap>(translation);
}
void Scene::Transform(const Matrix3x4& transform)
{
Expand Down
5 changes: 4 additions & 1 deletion libs/MVS/Scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class MVS_API Scene
ImageArr images; // images, each referencing a platform's camera pose
PointCloud pointcloud; // point-cloud (sparse or dense), each containing the point position and the views seeing it
Mesh mesh; // mesh, represented as vertices and triangles, constructed from the input point-cloud
OBB3f obb; // optional region-of-interest; oriented bounding box containing the entire scene
OBB3f obb; // region-of-interest represented as oriented bounding box containing the entire scene (optional)
Matrix4x4 transform; // transformation used to convert from absolute to relative coordinate system (optional)

unsigned nCalibratedImages; // number of valid images

Expand Down Expand Up @@ -114,6 +115,7 @@ class MVS_API Scene
bool Center(const Point3* pCenter = NULL);
bool Scale(const REAL* pScale = NULL);
bool ScaleImages(unsigned nMaxResolution = 0, REAL scale = 0, const String& folderName = String());
Matrix4x4 ComputeNormalizationTransform(bool bScale = false) const;
void Transform(const Matrix3x3& rotation, const Point3& translation, REAL scale);
void Transform(const Matrix3x4& transform);
bool AlignTo(const Scene&);
Expand Down Expand Up @@ -167,6 +169,7 @@ class MVS_API Scene
ar & pointcloud;
ar & mesh;
ar & obb;
ar & transform;
}
#endif
};
Expand Down

0 comments on commit 7df46a6

Please sign in to comment.