-
-
Notifications
You must be signed in to change notification settings - Fork 829
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
Align by DGPS positions in images in SfmTransform - test with DJI drone images for example #1523
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for your contribution!
I left some comments and we really should clarify this axis inversion thing
set(ALICEVISION_HAVE_GEOGRAPHIC 0) | ||
|
||
if(NOT ALICEVISION_USE_GEOGRAPHIC STREQUAL "OFF") | ||
find_library(GeographicLib_LIBRARY NAMES Geographic GeographicLib DOC "GeographicLib library") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find_library(GeographicLib_LIBRARY NAMES Geographic GeographicLib DOC "GeographicLib library") | |
find_package(GeographicLib CONFIG) |
then the target GeographicLib::GeographicLib_SHARED
can be used to express the dependency in the remaining cmake parts
@@ -194,6 +193,7 @@ alicevision_add_software(aliceVision_sfmTransform | |||
aliceVision_sfmData | |||
aliceVision_sfmDataIO | |||
Boost::program_options | |||
${GeographicLib_LIBRARY} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GeographicLib_LIBRARY
can be set to GeographicLib::GeographicLib_SHARED
if found or to empty string.
using ceres::Solve; | ||
using ceres::Solver; | ||
|
||
typedef Eigen::Matrix<double,3,4> Matrix34d; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typedef Eigen::Matrix<double,3,4> Matrix34d; | |
using Matrix34d = Eigen::Matrix<double,3,4>; |
GeographicLib::UTMUPS::Forward(lat, lon, zone, northp, x, y, gamma, k); | ||
mean += Eigen::Vector3d(x, y, alt); | ||
|
||
coords.push_back(Eigen::Vector3d(x, y, alt)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
coords.push_back(Eigen::Vector3d(x, y, alt)); | |
coords.emplace_back(x, y, alt); |
{ | ||
FILE* localcoordfile = fopen("localcenter.json", "wt"); | ||
fprintf(localcoordfile,"{ \"Coords\": {\n"); | ||
fprintf(localcoordfile,"\"Center\": [ %.16f, %.16f, %.16f ],\n", mean(0), mean(1), mean(2)); | ||
fprintf(localcoordfile,"\"Zone\": %d,\n",zone); | ||
fprintf(localcoordfile,"\"North\": %s,\n",northp ? "true" : "false"); | ||
fprintf(localcoordfile,"\"EPSGCode\": %d\n",northp ? 32600+zone : 32700+zone); | ||
fprintf(localcoordfile,"}\n}"); | ||
fclose(localcoordfile); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be better to write a function that does that using boost.json
t = Tnew; | ||
S = scale; | ||
|
||
delete[] pose_for_alignment; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are some other stuff to delete
delete[] Rn;
delete loss_function;
{ | ||
bool debug = false; | ||
|
||
std::vector<Eigen::Vector3d> coords; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems to be filled but never used in any other part of the code, maybe remove it?
<< "No pose for view " << v.second->getViewId() << std::endl); | ||
} | ||
} | ||
mean /= numValidPoses; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should check whether numValidPoses > 0
before otherwise return with a message/error as there are no valid poses
std::vector<Eigen::Vector3d> coords; | ||
|
||
Eigen::Vector3d mean = Eigen::Vector3d::Zero(); | ||
int numValidPoses = 0; | ||
const sfmData::Poses poses = sfmData.getPoses(); | ||
int zone; bool northp; | ||
for (auto v : sfmData.getViews()) { | ||
|
||
IndexT poseId = v.second->getPoseId(); | ||
if(poses.find(poseId) != poses.end()) | ||
{ | ||
double lat; double lon; double alt; | ||
v.second->getGpsPositionWGS84FromMetadata(lat, lon, alt); | ||
// zone and northp should be returned!!! | ||
double x, y, gamma, k; | ||
GeographicLib::UTMUPS::Forward(lat, lon, zone, northp, x, y, gamma, k); | ||
mean += Eigen::Vector3d(x, y, alt); | ||
|
||
coords.push_back(Eigen::Vector3d(x, y, alt)); | ||
|
||
numValidPoses++; | ||
} | ||
else | ||
{ | ||
ALICEVISION_LOG_INFO(std::setprecision(17) | ||
<< "No pose for view " << v.second->getViewId() << std::endl); | ||
} | ||
} | ||
mean /= numValidPoses; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would refactor this whole section into a separate function that computes the mean position and the number of valid poses, it would make the code more readable
|
||
#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_GEOGRAPHIC) | ||
if (alignmentMethod == EAlignmentMethod::FROM_GPS2UTM) | ||
{ | ||
ALICEVISION_LOG_INFO("Applying additional inversion to Y and Z to make textured mesh end up aligned correctly!"); | ||
Eigen::Matrix3d invYZ = Eigen::Matrix3d::Identity(); | ||
invYZ(1,1) = -1; invYZ(2,2) = -1; | ||
Eigen::Vector3d zeroT = Eigen::Vector3d::Zero(); | ||
|
||
sfm::applyTransform(sfmData, 1.0, invYZ, zeroT); | ||
} | ||
#endif | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why there is such inversion. This smells a lot like the infamous problem of conversion between the opengl reference frame and the computer vision reference frame.
@servantftechnicolor Maybe I'm not recalling well, but wasn't there a change in the way the obj are saved that has been introduced lately? Like to have the correct visualization there was this kind of inversion applied to all saved objs?
double * cPtr = centers; | ||
for (auto v : sfmData.getViews()) | ||
{ | ||
IndexT poseId = v.second->getPoseId(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IndexT poseId = v.second->getPoseId(); | |
const IndexT poseId = v.second->getPoseId(); |
thx for the comments. I'm going to review your suggestions and update the request accordingly. Yes, it has certainly something to do with the coordinate system convention used. It does not convert left-right handed or something, it is a pure 180° rotation around the x-axis. The inversion essentially makes z point to the sky and x/y correspond to right/up in the UTM coordinate system (which is still right-handed). From that point on it is also correct metric scale if it was not before. Admittedly, the rotation actually makes the visualization in Meshroom look a bit odd, which (I believe) assumes z to be forward and y point down. In a MeshRoom pipeline, the SfmTransform would plug in right after the StructureFromMotion node and before any (I don't recall the exact names) PrepareDense node. That makes the entire textured reconstruction at the very end of the pipeline correct. |
Hi @belveder79, |
Description
The changes were made to the SfmTransform executable mainly, plus building the dependency GeographicLib from public github repo, and having the feature ON/OFF in the main cmake configuration depending on GeographicLib being available (either built from the dependencies) or the system. The mode aligns the reconstruction using a Ceres implementation and a Huber Loss Function to the x/y/z coordinates given by the GPS coordinates (in metric UTM). The reconstruction ends up in a local coordinate system. The global center coordinates are stored in a separate file.
Features list
localcenter.json
in JSON format, which can be used later for global alignment.Implementation remarks
Depending on the real use case, one might want to make additional modifications like (not implemented):
sfm.abc
file at the end of the SfmTransform node. The best might be to have 2 files stored, one for making the textured model "correct" and one to have the correct alignment for localization.The alignment was tested on 2 sets of images from DJI Mavic drone footage and shown to work very well.