Skip to content

Commit c5bb371

Browse files
committed
Projected export geocoords support
1 parent 51c2e85 commit c5bb371

2 files changed

Lines changed: 56 additions & 6 deletions

File tree

opensfm/actions/export_geocoords.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from opensfm import io
88
from opensfm.dataset import DataSet, UndistortedDataSet
99
from opensfm.geo import TopocentricConverter
10+
from opensfm.reconstruction import bundle_shot_poses
1011
from typing import List, Sequence
1112

1213
logger: 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

137180
def _transform_dense_point_cloud(
138181
udata: UndistortedDataSet, transformation: np.ndarray, output_path: str

opensfm/commands/export_geocoords.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ def run_impl(self, dataset: DataSet, args: argparse.Namespace) -> None:
1818
args.reconstruction,
1919
args.dense,
2020
args.output,
21-
(args.offset_x, args.offset_y)
21+
(args.offset_x, args.offset_y),
22+
args.mode
2223
)
2324

2425
def add_arguments_impl(self, parser: argparse.ArgumentParser) -> None:
@@ -56,4 +57,10 @@ def add_arguments_impl(self, parser: argparse.ArgumentParser) -> None:
5657
parser.add_argument(
5758
"--offset-y", type=float, help="Value to add to the final translation Y axis", default=0.0
5859
)
59-
60+
parser.add_argument(
61+
"--mode",
62+
help="Georeferencing method",
63+
type=str,
64+
choices=["affine", "projected"],
65+
default="affine",
66+
)

0 commit comments

Comments
 (0)