Skip to content

Commit b54d80f

Browse files
Fabien Servantservantftransperfect
authored andcommitted
Add relative landmarks concept
1 parent e2a8f59 commit b54d80f

File tree

6 files changed

+180
-3
lines changed

6 files changed

+180
-3
lines changed

src/aliceVision/sfm/bundle/BundleAdjustmentCeres.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,17 @@ void BundleAdjustmentCeres::addLandmarksToProblem(const sfmData::SfMData& sfmDat
533533

534534
std::array<double, 3>& landmarkBlock = _landmarksBlocks[landmarkId];
535535
for (std::size_t i = 0; i < 3; ++i)
536+
{
536537
landmarkBlock.at(i) = landmark.X(Eigen::Index(i));
538+
}
539+
540+
//If the landmark has a referenceViewIndex set, then retrieve the reference pose
541+
double * referencePoseBlockPtr = nullptr;
542+
if (landmark.referenceViewIndex != UndefinedIndexT)
543+
{
544+
const sfmData::View& refview = sfmData.getView(landmark.referenceViewIndex);
545+
referencePoseBlockPtr = _posesBlocks.at(refview.getPoseId()).data();
546+
}
537547

538548
double* landmarkBlockPtr = landmarkBlock.data();
539549
problem.AddParameterBlock(landmarkBlockPtr, 3);
@@ -590,6 +600,23 @@ void BundleAdjustmentCeres::addLandmarksToProblem(const sfmData::SfMData& sfmDat
590600

591601
problem.AddResidualBlock(costFunction, lossFunction, params);
592602
}
603+
if (referencePoseBlockPtr != nullptr)
604+
{
605+
bool samePose = (referencePoseBlockPtr == poseBlockPtr);
606+
ceres::CostFunction* costFunction = ProjectionRelativeErrorFunctor::createCostFunction(intrinsic, observation, samePose);
607+
608+
std::vector<double*> params;
609+
params.push_back(intrinsicBlockPtr);
610+
params.push_back(distortionBlockPtr);
611+
if (!samePose)
612+
{
613+
params.push_back(poseBlockPtr);
614+
params.push_back(referencePoseBlockPtr);
615+
}
616+
params.push_back(landmarkBlockPtr);
617+
618+
problem.AddResidualBlock(costFunction, lossFunction, params);
619+
}
593620
else
594621
{
595622
ceres::CostFunction* costFunction = ProjectionSimpleErrorFunctor::createCostFunction(intrinsic, observation);

src/aliceVision/sfm/bundle/costfunctions/projection.hpp

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,130 @@ struct ProjectionErrorFunctor
281281
ceres::DynamicCostFunctionToFunctorTmp _intrinsicFunctor;
282282
};
283283

284+
struct ProjectionRelativeErrorFunctor
285+
{
286+
explicit ProjectionRelativeErrorFunctor(const sfmData::Observation& obs
287+
, const std::shared_ptr<camera::IntrinsicBase>& intrinsics
288+
, bool noPose)
289+
: _intrinsicFunctor(new CostIntrinsicsProject(obs, intrinsics))
290+
, withoutPose(noPose)
291+
{
292+
}
293+
294+
template<typename T>
295+
bool operator()(T const* const* parameters, T* residuals) const
296+
{
297+
const T* parameter_intrinsics = parameters[0];
298+
const T* parameter_distortion = parameters[1];
299+
300+
301+
T transformedPoint[3];
302+
if (withoutPose)
303+
{
304+
//withoutPose is used if reference view and current view are the same views
305+
const T* parameter_relativepoint = parameters[2];
306+
transformedPoint[0] = parameter_relativepoint[0];
307+
transformedPoint[1] = parameter_relativepoint[1];
308+
transformedPoint[2] = parameter_relativepoint[2];
309+
}
310+
else
311+
{
312+
const T* parameter_pose = parameters[2];
313+
const T* parameter_refpose = parameters[3];
314+
const T* parameter_relativepoint = parameters[4];
315+
316+
//Retrieve point
317+
T relpoint[3];
318+
relpoint[0] = parameter_relativepoint[0];
319+
relpoint[1] = parameter_relativepoint[1];
320+
relpoint[2] = parameter_relativepoint[2];
321+
322+
const T* refcam_R = parameter_refpose;
323+
const T* refcam_t = &parameter_refpose[3];
324+
325+
// Apply transformation such that the point
326+
// Which was defined in the camera geometric frame
327+
// is now defined in the world frame
328+
relpoint[0] = relpoint[0] - refcam_t[0];
329+
relpoint[1] = relpoint[1] - refcam_t[1];
330+
relpoint[2] = relpoint[2] - refcam_t[2];
331+
332+
T invrefcam_R[3];
333+
invrefcam_R[0] = -refcam_R[0];
334+
invrefcam_R[1] = -refcam_R[1];
335+
invrefcam_R[2] = -refcam_R[2];
336+
337+
T absolutePoint[3];
338+
ceres::AngleAxisRotatePoint(invrefcam_R, relpoint, absolutePoint);
339+
340+
//--
341+
// Apply external parameters (Pose)
342+
//--
343+
const T* cam_R = parameter_pose;
344+
const T* cam_t = &parameter_pose[3];
345+
346+
347+
// Rotate the point according the camera rotation
348+
ceres::AngleAxisRotatePoint(cam_R, absolutePoint, transformedPoint);
349+
350+
// Apply the camera translation
351+
transformedPoint[0] += cam_t[0];
352+
transformedPoint[1] += cam_t[1];
353+
transformedPoint[2] += cam_t[2];
354+
}
355+
356+
const T * innerParameters[3];
357+
innerParameters[0] = parameter_intrinsics;
358+
innerParameters[1] = parameter_distortion;
359+
innerParameters[2] = transformedPoint;
360+
361+
return _intrinsicFunctor(innerParameters, residuals);
362+
}
363+
364+
/**
365+
* @brief Create the appropriate cost functor according the provided input camera intrinsic model
366+
* @param[in] intrinsicPtr The intrinsic pointer
367+
* @param[in] observation The corresponding observation
368+
* @param[in] withoutPose When reference view and current view are the same, poses must be ignored
369+
* @return cost functor
370+
*/
371+
inline static ceres::CostFunction* createCostFunction(
372+
const std::shared_ptr<camera::IntrinsicBase> intrinsic,
373+
const sfmData::Observation& observation,
374+
bool withoutPose)
375+
{
376+
auto costFunction = new ceres::DynamicAutoDiffCostFunction<ProjectionRelativeErrorFunctor>(new ProjectionRelativeErrorFunctor(observation, intrinsic, withoutPose));
377+
378+
int distortionSize = 1;
379+
auto isod = camera::IntrinsicScaleOffsetDisto::cast(intrinsic);
380+
if (isod)
381+
{
382+
auto distortion = isod->getDistortion();
383+
if (distortion)
384+
{
385+
distortionSize = distortion->getParameters().size();
386+
}
387+
}
388+
389+
costFunction->AddParameterBlock(intrinsic->getParameters().size());
390+
costFunction->AddParameterBlock(distortionSize);
391+
392+
if (!withoutPose)
393+
{
394+
costFunction->AddParameterBlock(6);
395+
costFunction->AddParameterBlock(6);
396+
}
397+
398+
costFunction->AddParameterBlock(3);
399+
costFunction->SetNumResiduals(2);
400+
401+
return costFunction;
402+
}
403+
404+
ceres::DynamicCostFunctionToFunctorTmp _intrinsicFunctor;
405+
bool withoutPose;
406+
};
407+
284408

285409
} // namespace sfm
286410
} // namespace aliceVision

src/aliceVision/sfmData/Landmark.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ class Landmark
4141
feature::EImageDescriberType descType = feature::EImageDescriberType::UNINITIALIZED;
4242
image::RGBColor rgb = image::WHITE; //!> the color associated to the point
4343
EEstimatorParameterState state = EEstimatorParameterState::REFINED;
44+
IndexT referenceViewIndex = UndefinedIndexT;
4445

4546
bool operator==(const Landmark& other) const
4647
{
47-
return AreVecNearEqual(X, other.X, 1e-3) && AreVecNearEqual(rgb, other.rgb, 1e-3) && _observations == other._observations &&
48-
descType == other.descType;
48+
return AreVecNearEqual(X, other.X, 1e-3)
49+
&& AreVecNearEqual(rgb, other.rgb, 1e-3)
50+
&& _observations == other._observations
51+
&& descType == other.descType
52+
&& referenceViewIndex == other.referenceViewIndex;
4953
}
5054

5155
inline bool operator!=(const Landmark& other) const { return !(*this == other); }

src/aliceVision/sfmDataIO/AlembicExporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,11 +505,13 @@ void AlembicExporter::addLandmarks(const sfmData::Landmarks& landmarks,
505505
return;
506506

507507
// Fill vector with the values taken from AliceVision
508+
std::vector<IndexT> referenceViewIndices;
508509
std::vector<V3f> positions;
509510
std::vector<Imath::C3f> colors;
510511
std::vector<Alembic::Util::uint32_t> descTypes;
511512
positions.reserve(landmarks.size());
512513
descTypes.reserve(landmarks.size());
514+
referenceViewIndices.reserve(landmarks.size());
513515

514516
// For all the 3d points in the hash_map
515517
for (const auto& landmark : landmarks)
@@ -520,6 +522,7 @@ void AlembicExporter::addLandmarks(const sfmData::Landmarks& landmarks,
520522
positions.emplace_back(pt[0], -pt[1], -pt[2]);
521523
colors.emplace_back(color.r() / 255.f, color.g() / 255.f, color.b() / 255.f);
522524
descTypes.emplace_back(static_cast<Alembic::Util::uint8_t>(landmark.second.descType));
525+
referenceViewIndices.emplace_back(landmark.second.referenceViewIndex);
523526
}
524527

525528
std::vector<Alembic::Util::uint64_t> ids(positions.size());
@@ -542,6 +545,7 @@ void AlembicExporter::addLandmarks(const sfmData::Landmarks& landmarks,
542545
OCompoundProperty userProps = pSchema.getUserProperties();
543546

544547
OUInt32ArrayProperty(userProps, "mvg_describerType").set(descTypes);
548+
OUInt32ArrayProperty(userProps, "mvg_referenceViewIndices").set(referenceViewIndices);
545549

546550
if (withVisibility)
547551
{

src/aliceVision/sfmDataIO/AlembicImporter.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,18 @@ bool readPointCloud(const Version& abcVersion, IObject iObj, M44d mat, sfmData::
186186
}
187187
}
188188

189+
AV_UInt32ArraySamplePtr sampleReferenceViewIndices;
190+
if (userProps && userProps.getPropertyHeader("mvg_referenceViewIndices"))
191+
{
192+
sampleReferenceViewIndices.read(userProps, "mvg_referenceViewIndices");
193+
if (sampleReferenceViewIndices.size() != positions->size())
194+
{
195+
ALICEVISION_LOG_WARNING("[Alembic Importer] Describer type will be ignored. describerType vector size: "
196+
<< sampleReferenceViewIndices.size() << ", positions vector size: " << positions->size());
197+
sampleReferenceViewIndices.reset();
198+
}
199+
}
200+
189201
// Number of points before adding the Alembic data
190202
const std::size_t nbPointsInit = sfmdata.getLandmarks().size();
191203
for (std::size_t point3d_i = 0; point3d_i < positions->size(); ++point3d_i)
@@ -217,6 +229,11 @@ bool readPointCloud(const Version& abcVersion, IObject iObj, M44d mat, sfmData::
217229
const std::size_t descType_i = sampleDescs[point3d_i];
218230
landmark.descType = static_cast<feature::EImageDescriberType>(descType_i);
219231
}
232+
233+
if (sampleReferenceViewIndices)
234+
{
235+
landmark.referenceViewIndex = sampleReferenceViewIndices[point3d_i];
236+
}
220237
}
221238

222239
// for compatibility with files generated with a previous version

src/aliceVision/sfmDataIO/jsonIO.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ void saveLandmark(const std::string& name,
500500
bpt::ptree landmarkTree;
501501

502502
landmarkTree.put("landmarkId", landmarkId);
503+
landmarkTree.put("referenceViewIndex", landmark.referenceViewIndex);
503504
landmarkTree.put("descType", feature::EImageDescriberType_enumToString(landmark.descType));
504505

505506
saveMatrix("color", landmark.rgb, landmarkTree);
@@ -538,7 +539,7 @@ void loadLandmark(IndexT& landmarkId, sfmData::Landmark& landmark, bpt::ptree& l
538539
{
539540
landmarkId = landmarkTree.get<IndexT>("landmarkId");
540541
landmark.descType = feature::EImageDescriberType_stringToEnum(landmarkTree.get<std::string>("descType"));
541-
542+
landmark.referenceViewIndex = landmarkTree.get<IndexT>("referenceViewIndex", UndefinedIndexT);
542543
loadMatrix("color", landmark.rgb, landmarkTree);
543544
loadMatrix("X", landmark.X, landmarkTree);
544545

0 commit comments

Comments
 (0)