2 GTSAM Copyright 2010-2018, Georgia Tech Research Corporation, 3 Atlanta, Georgia 30332-0415 5 Authors: Frank Dellaert, et al. (see THANKS for the full author list) 7 See LICENSE for the license information 9 This example shows how 1dsfm uses outlier rejection (MFAS) and optimization (translation recovery) 10 together for estimating global translations from relative translation directions and global rotations. 11 The purpose of this example is to illustrate the connection between these two classes using a small SfM dataset. 13 Author: Akshay Krishnan 17 from collections
import defaultdict
18 from typing
import List, Tuple
26 MAX_1DSFM_PROJECTION_DIRECTIONS = 48
27 OUTLIER_WEIGHT_THRESHOLD = 0.1
30 def get_data() -> Tuple[gtsam.Values, List[gtsam.BinaryMeasurementUnit3]]:
31 """"Returns global rotations and unit translation directions between 8 cameras 32 that lie on a circle and face the center. The poses of 8 cameras are obtained from SFMdata 33 and the unit translations directions between some camera pairs are computed from their 34 global translations. """ 35 fx, fy, s, u0, v0 = 50.0, 50.0, 0.0, 50.0, 50.0
36 wTc_list = SFMdata.createPoses(
gtsam.Cal3_S2(fx, fy, s, u0, v0))
42 for i
in range(0,
len(wTc_list) - 2):
45 wRc_values.insert(i, wRi)
47 for j
in range(i + 1, i + 3):
49 w_itj = wTc_list[j].translation() - wTc_list[i].translation()
51 i_itj = wRi.unrotate(w_itj)
54 i_iZj_list.append(gtsam.BinaryMeasurementUnit3(
57 wRc_values.insert(
len(wTc_list) - 1, wTc_list[-1].
rotation())
58 wRc_values.insert(
len(wTc_list) - 2, wTc_list[-2].
rotation())
59 return wRc_values, i_iZj_list
62 def filter_outliers(w_iZj_list: gtsam.BinaryMeasurementsUnit3) -> gtsam.BinaryMeasurementsUnit3:
63 """Removes outliers from a list of Unit3 measurements that are the 64 translation directions from camera i to camera j in the world frame.""" 68 num_directions_to_sample =
min(
69 MAX_1DSFM_PROJECTION_DIRECTIONS,
len(w_iZj_list))
70 sampled_indices = np.random.choice(
71 len(w_iZj_list), num_directions_to_sample, replace=
False)
74 projection_directions = [w_iZj_list[idx].
measured()
75 for idx
in sampled_indices]
79 for direction
in projection_directions:
81 outlier_weights.append(algorithm.computeOutlierWeights())
86 avg_outlier_weights = defaultdict(float)
87 for outlier_weight_dict
in outlier_weights:
88 for keypair, weight
in outlier_weight_dict.items():
89 avg_outlier_weights[keypair] += weight /
len(outlier_weights)
93 [w_iZj_inliers.append(w_iZj)
for w_iZj
in w_iZj_list
if avg_outlier_weights[(
94 w_iZj.key1(), w_iZj.key2())] < OUTLIER_WEIGHT_THRESHOLD]
101 """Estimate poses given rotations and normalized translation directions between cameras. 104 i_iZj_list: List of normalized translation direction measurements between camera pairs, 105 Z here refers to measurements. The measurements are of camera j with reference 106 to camera i (iZj), in camera i's coordinate frame (i_). iZj represents a unit 107 vector to j in i's frame and is not a transformation. 108 wRc_values: Rotations of the cameras in the world frame. 111 gtsam.Values: Estimated poses. 116 for i_iZj
in i_iZj_list:
117 w_iZj =
gtsam.Unit3(wRc_values.atRot3(i_iZj.key1())
119 w_iZj_list.append(gtsam.BinaryMeasurementUnit3(
120 i_iZj.key1(), i_iZj.key2(), w_iZj, i_iZj.noiseModel()))
129 for key
in wRc_values.keys():
131 wRc_values.atRot3(key), wtc_values.atPoint3(key)))
138 print(
"**** Translation averaging output ****")
140 print(
"**************************************")
143 if __name__ ==
'__main__':
void print(const Matrix &A, const string &s, ostream &stream)
Rot3_ rotation(const Pose3_ &pose)
std::vector< BinaryMeasurement< Unit3 >> BinaryMeasurementsUnit3
Represents a 3D point on a unit sphere.
static const Point3 point3(0.08, 0.08, 0.0)
Point3_ rotate(const Rot3_ &x, const Point3_ &p)
void run(Expr &expr, Dev &dev)
static shared_ptr Sigma(size_t dim, double sigma, bool smart=true)