@@ -318,8 +318,8 @@ py::tuple BAHelpers::BundleLocal(
318318bool BAHelpers::TriangulateGCP (
319319 const map::GroundControlPoint& point,
320320 const std::unordered_map<map::ShotId, map::Shot>& shots,
321+ float reproj_threshold,
321322 Vec3d& coordinates) {
322- constexpr auto reproj_threshold{1.0 };
323323 constexpr auto min_ray_angle = 0.1 * M_PI / 180.0 ;
324324 constexpr auto min_depth = 1e-3 ; // Assume GCPs 1mm+ away from the camera
325325 MatX3d os, bs;
@@ -361,48 +361,56 @@ size_t BAHelpers::AddGCPToBundle(
361361 ba.GetProjectionsCount () +
362362 ba.GetRelativeMotionsCount ();
363363
364- size_t total_terms = 0 ;
365- for (const auto & point : gcp) {
366- Vec3d coordinates;
367- if (TriangulateGCP (point, shots, coordinates) || !point.lla_ .empty ()) {
368- ++total_terms;
369- }
370- for (const auto & obs : point.observations_ ) {
371- total_terms += (shots.count (obs.shot_id_ ) > 0 );
372- }
373- }
374-
375- double global_weight = config[" gcp_global_weight" ].cast <double >() *
376- dominant_terms / std::max<size_t >(1 , total_terms);
364+ const float reproj_threshold =
365+ config[" gcp_reprojection_error_threshold" ].cast <float >();
377366
378367 size_t added_gcp_observations = 0 ;
379368 for (const auto & point : gcp) {
380369 const auto point_id = " gcp-" + point.id_ ;
381370 Vec3d coordinates;
382- if (!TriangulateGCP (point, shots, coordinates)) {
371+ if (!TriangulateGCP (point, shots, reproj_threshold, coordinates)) {
383372 if (!point.lla_ .empty ()) {
384373 coordinates = reference.ToTopocentric (point.GetLlaVec3d ());
385374 } else {
386375 continue ;
387376 }
388377 }
378+
379+ double avg_observations = 0 .;
380+ int valid_shots = 0 ;
381+ for (const auto & obs : point.observations_ ) {
382+ const auto shot_it = shots.find (obs.shot_id_ );
383+ if (shot_it != shots.end ()) {
384+ const auto & shot = (shot_it->second );
385+ avg_observations += shot.GetLandmarkObservations ().size ();
386+ ++valid_shots;
387+ }
388+ }
389+
390+ if (!valid_shots) {
391+ continue ;
392+ }
393+ avg_observations /= valid_shots;
394+
395+ const double prior_weight = config[" gcp_global_weight" ].cast <double >() * avg_observations;
389396 constexpr auto point_constant{false };
390397 ba.AddPoint (point_id, coordinates, point_constant);
391398 if (!point.lla_ .empty ()) {
392399 const auto point_std = Vec3d (config[" gcp_horizontal_sd" ].cast <double >(),
393400 config[" gcp_horizontal_sd" ].cast <double >(),
394401 config[" gcp_vertical_sd" ].cast <double >());
395402 ba.AddPointPrior (point_id, reference.ToTopocentric (point.GetLlaVec3d ()),
396- point_std / global_weight , point.has_altitude_ );
403+ point_std / prior_weight , point.has_altitude_ );
397404 }
398405
399406 // Now iterate through the observations
407+ const double obs_weight = config[" gcp_global_weight" ].cast <double >() * avg_observations;
400408 for (const auto & obs : point.observations_ ) {
401409 const auto & shot_id = obs.shot_id_ ;
402410 if (shots.count (shot_id) > 0 ) {
403411 constexpr double scale{0.001 };
404412 ba.AddPointProjectionObservation (shot_id, point_id, obs.projection_ ,
405- scale / global_weight );
413+ scale / obs_weight );
406414 ++added_gcp_observations;
407415 }
408416 }
@@ -718,7 +726,7 @@ py::dict BAHelpers::Bundle(
718726 AddGCPToBundle (ba, map, gcp, config);
719727 }
720728
721- if (config[" bundle_compensate_gps_bias" ].cast <bool >()) {
729+ if (config[" bundle_compensate_gps_bias" ].cast <bool >() && !gcp. empty () ) {
722730 const auto & biases = map.GetBiases ();
723731 for (const auto & camera : map.GetCameras ()) {
724732 ba.SetCameraBias (camera.first , biases.at (camera.first ));
@@ -861,7 +869,7 @@ void BAHelpers::AlignmentConstraints(
861869 continue ;
862870 }
863871 Vec3d coordinates;
864- if (TriangulateGCP (point, shots, coordinates)) {
872+ if (TriangulateGCP (point, shots, config[ " gcp_reprojection_error_threshold " ]. cast < float >(), coordinates)) {
865873 Xp.row (idx) = topocentricConverter.ToTopocentric (point.GetLlaVec3d ());
866874 X.row (idx) = coordinates;
867875 ++idx;
0 commit comments