Skip to content

Commit 27ffad4

Browse files
authored
Make benchmark translation-angle errors invariant to reconstruction scale (#4932)
1 parent 105817b commit 27ffad4

3 files changed

Lines changed: 50 additions & 49 deletions

File tree

‎benchmark/reconstruction/evaluation/geometry.py‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
import numpy.typing as npt
55

66

7-
def normalize_vec(
8-
vec: npt.NDArray[np.floating], eps: float = 1e-10
9-
) -> npt.NDArray[np.floating]:
10-
return vec / max(eps, float(np.linalg.norm(vec)))
7+
def normalize_vec(vec: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
8+
scale = float(np.max(np.abs(vec)))
9+
if scale == 0.0:
10+
return np.zeros_like(vec)
11+
scaled = vec / scale
12+
return scaled / float(np.linalg.norm(scaled))
1113

1214

1315
def vec_angular_dist_deg(
Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,56 @@
11
# SPDX-License-Identifier: BSD-3-Clause
22

33
import numpy as np
4+
import pytest
45

56
from .geometry import normalize_vec, vec_angular_dist_deg
67

78

89
class TestNormalizeVec:
9-
def test_unit_vector(self) -> None:
10-
vec = np.array([1.0, 0.0, 0.0])
10+
@pytest.mark.parametrize("scale", [1e-200, 1e-12, 1.0, 1e200])
11+
def test_nonzero_vector(self, scale: float) -> None:
12+
vec = scale * np.array([3.0, 4.0, 0.0])
13+
vec_before = vec.copy()
1114
normalized = normalize_vec(vec)
12-
np.testing.assert_allclose(normalized, vec)
13-
np.testing.assert_almost_equal(np.linalg.norm(normalized), 1.0)
14-
15-
def test_non_unit_vector(self) -> None:
16-
vec = np.array([3.0, 4.0, 0.0])
17-
normalized = normalize_vec(vec)
18-
expected = np.array([0.6, 0.8, 0.0])
19-
np.testing.assert_allclose(normalized, expected)
15+
np.testing.assert_allclose(normalized, [0.6, 0.8, 0.0])
2016
np.testing.assert_almost_equal(np.linalg.norm(normalized), 1.0)
17+
np.testing.assert_array_equal(vec, vec_before)
2118

2219
def test_zero_vector(self) -> None:
23-
vec = np.array([0.0, 0.0, 0.0])
20+
vec = np.zeros(3)
2421
normalized = normalize_vec(vec)
25-
assert np.linalg.norm(normalized) < 1e-8
22+
assert normalized is not vec
23+
np.testing.assert_array_equal(normalized, np.zeros(3))
2624

2725

2826
class TestVecAngularDistDeg:
29-
def test_identical_vectors(self) -> None:
30-
vec1 = np.array([1.0, 0.0, 0.0])
31-
vec2 = np.array([1.0, 0.0, 0.0])
32-
dist = vec_angular_dist_deg(vec1, vec2)
33-
np.testing.assert_almost_equal(dist, 0.0)
34-
35-
def test_opposite_vectors(self) -> None:
36-
vec1 = np.array([1.0, 0.0, 0.0])
37-
vec2 = np.array([-1.0, 0.0, 0.0])
38-
dist = vec_angular_dist_deg(vec1, vec2)
39-
np.testing.assert_almost_equal(dist, 180.0)
40-
41-
def test_perpendicular_vectors(self) -> None:
42-
vec1 = np.array([1.0, 0.0, 0.0])
43-
vec2 = np.array([0.0, 1.0, 0.0])
44-
dist = vec_angular_dist_deg(vec1, vec2)
45-
np.testing.assert_almost_equal(dist, 90.0)
46-
47-
def test_45_degree_vectors(self) -> None:
48-
vec1 = np.array([1.0, 0.0, 0.0])
49-
vec2 = np.array([1.0, 1.0, 0.0])
50-
dist = vec_angular_dist_deg(vec1, vec2)
51-
np.testing.assert_almost_equal(dist, 45.0)
52-
53-
def test_non_unit_vectors(self) -> None:
54-
vec1 = np.array([2.0, 0.0, 0.0])
55-
vec2 = np.array([0.0, 3.0, 0.0])
56-
dist = vec_angular_dist_deg(vec1, vec2)
57-
# Should normalize internally
58-
np.testing.assert_almost_equal(dist, 90.0)
27+
@pytest.mark.parametrize("scale1", [1e-200, 1e-12, 1.0, 2.0, 1e200])
28+
@pytest.mark.parametrize("scale2", [1e-200, 1e-12, 1.0, 3.0, 1e200])
29+
@pytest.mark.parametrize(
30+
("target_dir", "expected"),
31+
[
32+
([1.0, 0.0, 0.0], 0.0),
33+
([1.0, 1.0, 0.0], 45.0),
34+
([0.0, 1.0, 0.0], 90.0),
35+
([-1.0, 0.0, 0.0], 180.0),
36+
],
37+
)
38+
def test_scale_invariant_angles(
39+
self,
40+
scale1: float,
41+
scale2: float,
42+
target_dir: list[float],
43+
expected: float,
44+
) -> None:
45+
vec1 = scale1 * np.array([1.0, 0.0, 0.0])
46+
vec2 = scale2 * np.array(target_dir)
47+
vec1_before, vec2_before = vec1.copy(), vec2.copy()
48+
assert vec_angular_dist_deg(vec1, vec2) == pytest.approx(
49+
expected, abs=1e-8
50+
)
51+
np.testing.assert_array_equal(vec1, vec1_before)
52+
np.testing.assert_array_equal(vec2, vec2_before)
53+
54+
def test_zero_vector(self) -> None:
55+
dist = vec_angular_dist_deg(np.zeros(3), np.array([1.0, 0.0, 0.0]))
56+
assert dist == 90.0

‎benchmark/reconstruction/evaluation/utils_test.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -755,15 +755,16 @@ def test_identical_reconstruction(self) -> None:
755755
assert len(errors) == num_images * (num_images - 1)
756756
np.testing.assert_allclose(errors, 0.0, atol=1e-5)
757757

758-
def test_transformed_reconstruction(self) -> None:
758+
@pytest.mark.parametrize("scale", [1e-200, 1e-12, 1.0, 1e200])
759+
def test_transformed_reconstruction(self, scale: float) -> None:
759760
gt_reconstruction = create_test_reconstruction()
760761
reconstruction = create_test_reconstruction()
761762
# A global similarity transform leaves relative poses unchanged.
762763
reconstruction.transform(
763764
pycolmap.Sim3d(
764-
1.0,
765+
scale,
765766
pycolmap.Rotation3d(np.array([0, 1, 0, 0])),
766-
np.array([1, 2, 3]),
767+
scale * np.array([1.0, 2.0, 3.0]),
767768
)
768769
)
769770

0 commit comments

Comments
 (0)