77from opensfm import io
88from opensfm .dataset import DataSet , UndistortedDataSet
99from opensfm .geo import TopocentricConverter
10+ from opensfm .reconstruction import bundle_shot_poses
1011from typing import List , Sequence
1112
1213logger : logging .Logger = logging .getLogger (__name__ )
@@ -20,7 +21,8 @@ def run_dataset(
2021 reconstruction : bool ,
2122 dense : bool ,
2223 output : str ,
23- offset = (0 , 0 )
24+ offset = (0 , 0 ),
25+ mode = "affine"
2426) -> None :
2527 """Export reconstructions in geographic coordinates
2628
@@ -32,7 +34,7 @@ def run_dataset(
3234 dense : export dense point cloud (depthmaps/merged.ply)
3335 output : path of the output file relative to the dataset
3436 offset : offset to substract from the translation (optional)
35-
37+ mode : Method of georeferencing (optional)
3638 """
3739
3840 if not (transformation or image_positions or reconstruction or dense ):
@@ -57,8 +59,15 @@ def run_dataset(
5759
5860 if reconstruction :
5961 reconstructions = data .load_reconstruction ()
60- for r in reconstructions :
61- _transform_reconstruction (r , t )
62+ if mode == "affine" :
63+ for r in reconstructions :
64+ _transform_reconstruction (r , t )
65+ elif mode == "projected" :
66+ for r in reconstructions :
67+ _transform_reconstruction_projected (r , t , offset [0 ], offset [1 ], reference , projection , data )
68+ else :
69+ raise Exception (f"Invalid mode: { mode } " )
70+
6271 output = output or "reconstruction.geocoords.json"
6372 data .save_reconstruction (reconstructions , output )
6473
@@ -133,6 +142,40 @@ def _transform_reconstruction(
133142 for point in reconstruction .points .values ():
134143 point .coordinates = list (np .dot (A , point .coordinates ) + b )
135144
145+ def _transform_points_projected (pts , offset_x , offset_y , reference , projection ):
146+ lat , lon , alt = reference .to_lla (pts [:,0 ], pts [:,1 ], pts [:,2 ])
147+ easting , northing = projection (lon , lat )
148+ return easting - offset_x , northing - offset_y , alt
149+
150+ def _transform_reconstruction_projected (
151+ reconstruction : types .Reconstruction , transformation : np .ndarray , offset_x , offset_y , reference , projection , data
152+ ) -> None :
153+ """Apply a transformation to a reconstruction in-place by projection."""
154+
155+ # Points
156+ pts = np .array ([p .coordinates for p in reconstruction .points .values ()])
157+ easting , northing , alt = _transform_points_projected (pts , offset_x , offset_y , reference , projection )
158+
159+ for i , point in enumerate (reconstruction .points .values ()):
160+ point .coordinates = [easting [i ], northing [i ], alt [i ]]
161+
162+ # Cameras
163+ A , b = transformation [:3 , :3 ], transformation [:3 , 3 ]
164+ A1 = np .linalg .inv (A )
165+
166+ pts = np .array ([shot .pose .get_origin () for shot in reconstruction .shots .values ()])
167+ easting , northing , alt = _transform_points_projected (pts , offset_x , offset_y , reference , projection )
168+
169+ for i , shot in enumerate (reconstruction .shots .values ()):
170+ R = shot .pose .get_rotation_matrix ()
171+ shot .pose .set_rotation_matrix (np .dot (R , A1 ))
172+ shot .pose .set_origin ([easting [i ], northing [i ], alt [i ]])
173+
174+ logger .info ("Bundle shot poses" )
175+ camera_priors = data .load_camera_models ()
176+ rig_camera_priors = data .load_rig_cameras ()
177+ bundle_shot_poses (reconstruction , set (reconstruction .shots .keys ()), camera_priors , rig_camera_priors , data .config )
178+
136179
137180def _transform_dense_point_cloud (
138181 udata : UndistortedDataSet , transformation : np .ndarray , output_path : str
0 commit comments