Skip to content

Commit bb070fb

Browse files
committed
improved code, ray_tracing should be faster
1 parent 2c4a2a7 commit bb070fb

3 files changed

Lines changed: 197 additions & 59 deletions

File tree

openptv_python/lsqadj.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
"""Least squares adjustment of the camera parameters."""
22
import numpy as np
3+
from numba import njit
34

45

5-
def ata(a: np.ndarray, ata: np.ndarray, m: int, n: int, n_large: int) -> None:
6+
@njit
7+
def ata(a: np.ndarray, at: np.ndarray, m: int, n: int, n_large: int) -> None:
68
"""
79
Multiply transpose of a matrix A by matrix A itself, creating a symmetric matrix inplace.
810
@@ -16,9 +18,9 @@ def ata(a: np.ndarray, ata: np.ndarray, m: int, n: int, n_large: int) -> None:
1618
"""
1719
for i in range(n):
1820
for j in range(n):
19-
ata.flat[i * n + j] = 0.0
21+
at.flat[i * n + j] = 0.0
2022
for k in range(m):
21-
ata.flat[i * n + j] += a.flat[k * n_large + i] * a.flat[k * n_large + j]
23+
at.flat[i * n + j] += a.flat[k * n_large + i] * a.flat[k * n_large + j]
2224

2325

2426
# def ata(a: np.ndarray, ata: np.ndarray, m: int, n: int, n_large: int) -> None:
@@ -45,7 +47,7 @@ def ata(a: np.ndarray, ata: np.ndarray, m: int, n: int, n_large: int) -> None:
4547
# a.flat[k * n_large + i] * a.flat[k * n_large + j]
4648
# )
4749

48-
50+
@njit
4951
def atl(u: np.ndarray, a: np.ndarray, b: np.ndarray, m: int, n: int, n_large: int):
5052
"""Multiply transpose of a matrix A by vector b, creating vector u.
5153
@@ -229,7 +231,7 @@ def atl(u: np.ndarray, a: np.ndarray, b: np.ndarray, m: int, n: int, n_large: in
229231
# pa += k
230232
# c += 1
231233

232-
234+
@njit
233235
def matmul(
234236
a: np.ndarray,
235237
b: np.ndarray,

openptv_python/ray_tracing.py

Lines changed: 172 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Tuple
33

44
import numpy as np
5+
from numba import njit
56

67
from .calibration import Calibration
78
from .lsqadj import matmul
@@ -48,70 +49,202 @@ def ray_tracing(
4849
-------
4950
_type_: _description_
5051
"""
51-
# Initial ray direction in global coordinate system
52-
tmp1 = np.r_[x, y, -1 * cal.int_par.cc]
53-
tmp1 = unit_vector(tmp1)
54-
start_dir = np.empty(3, dtype=float)
55-
matmul(start_dir, cal.ext_par.dm, tmp1, 3, 3, 1, 3, 3)
56-
5752
primary_point = np.r_[cal.ext_par.x0, cal.ext_par.y0, cal.ext_par.z0]
53+
glass = np.r_[cal.glass_par.vec_x, cal.glass_par.vec_y, cal.glass_par.vec_z]
54+
return fast_ray_tracing(
55+
x,
56+
y,
57+
cal.int_par.cc,
58+
cal.ext_par.dm,
59+
primary_point,
60+
glass,
61+
mm.d[0],
62+
mm.n1,
63+
mm.n2[0],
64+
mm.n3,
65+
)
66+
67+
@njit
68+
def fast_ray_tracing(
69+
camera_x: float,
70+
camera_y: float,
71+
camera_cc: float,
72+
distortion_matrix: np.ndarray,
73+
primary_point: np.ndarray,
74+
glass_vector: np.ndarray,
75+
distance_param: float,
76+
refractive_index_medium1: float,
77+
refractive_index_medium2: float,
78+
refractive_index_medium3: float,
79+
) -> Tuple[np.ndarray, np.ndarray]:
80+
"""
81+
Fast ray tracing.
5882
59-
tmp1 = np.r_[cal.glass_par.vec_x, cal.glass_par.vec_y, cal.glass_par.vec_z]
60-
glass_dir = unit_vector(tmp1)
61-
c = vec_norm(tmp1) + mm.d[0]
83+
Parameters
84+
----------
85+
- camera_x, camera_y, camera_cc: Camera parameters
86+
- distortion_matrix: Distortion matrix
87+
- primary_point: Primary point coordinates
88+
- glass_vector: Glass vector
89+
- distance_param: Distance parameter
90+
- refractive_index_medium1, refractive_index_medium2, refractive_index_medium3: Refractive indices
91+
92+
Returns
93+
-------
94+
- Tuple containing the resulting point X and output direction vector
95+
"""
96+
# Initial ray direction in global coordinate system
97+
initial_ray_direction = np.array([camera_x, camera_y, -1 * camera_cc])
98+
initial_ray_direction = unit_vector(initial_ray_direction)
99+
transformed_direction = np.empty(3, dtype=float)
100+
matmul(transformed_direction, distortion_matrix, initial_ray_direction, 3, 3, 1, 3, 3)
101+
102+
glass_direction = unit_vector(glass_vector)
103+
c_param = vec_norm(glass_vector) + distance_param
62104

63105
# Project start ray on glass vector to find n1/n2 interface.
64-
dist_cam_glass = vec_dot(glass_dir, primary_point) - c
65-
tmp1 = vec_dot(glass_dir, start_dir)
106+
dist_cam_glass = vec_dot(glass_direction, primary_point) - c_param
107+
dot_product_start_dir = float(vec_dot(glass_direction, transformed_direction))
66108

67109
# avoid division by zero
68-
if tmp1 == 0:
69-
tmp1 = 1.0
70-
d1 = -dist_cam_glass / tmp1
110+
if dot_product_start_dir == 0.0:
111+
dot_product_start_dir = 1.0
112+
d1 = -dist_cam_glass / dot_product_start_dir
71113

72-
tmp1 = vec_scalar_mul(start_dir, d1)
73-
Xb = vec_add(primary_point, tmp1)
114+
transformed_direction_scaled = vec_scalar_mul(transformed_direction, d1)
115+
Xb = vec_add(primary_point, transformed_direction_scaled)
74116

75117
# Break down ray into glass-normal and glass-parallel components. */
76-
n = vec_dot(start_dir, glass_dir)
77-
tmp1 = vec_scalar_mul(glass_dir, n)
118+
n = vec_dot(transformed_direction, glass_direction)
119+
transformed_direction_parallel = vec_scalar_mul(glass_direction, n)
78120

79-
tmp2 = vec_subt(start_dir, tmp1)
80-
bp = unit_vector(tmp2)
121+
transformed_direction_perpendicular = vec_subt(transformed_direction, transformed_direction_parallel)
122+
bp = unit_vector(transformed_direction_perpendicular)
81123

82124
# Transform to direction inside glass, using Snell's law
83-
p = np.sqrt(1 - n * n) * mm.n1 / mm.n2[0]
125+
p = np.sqrt(1 - n * n) * refractive_index_medium1 / refractive_index_medium2
84126
# glass parallel
85127
n = -np.sqrt(1 - p * p)
86128
# glass normal
87129

88130
# Propagation length in glass parallel to glass vector */
89-
tmp1 = vec_scalar_mul(bp, p)
90-
tmp2 = vec_scalar_mul(glass_dir, n)
91-
a2 = vec_add(tmp1, tmp2)
131+
transformed_direction_parallel_scaled = vec_scalar_mul(bp, p)
132+
transformed_direction_perpendicular_scaled = vec_scalar_mul(glass_direction, n)
133+
a2 = vec_add(transformed_direction_parallel_scaled, transformed_direction_perpendicular_scaled)
92134

93-
tmp1 = np.abs(vec_dot(glass_dir, a2))
135+
abs_dot_product = np.abs(vec_dot(glass_direction, a2))
94136

95137
# avoid division by zero
96-
if tmp1 == 0:
97-
tmp1 = 1.0
98-
d2 = mm.d[0] / tmp1
138+
if abs_dot_product == 0:
139+
abs_dot_product = 1.0
140+
d2 = distance_param / abs_dot_product
99141

100-
# point on the horizontal plane between n2,n3 */
101-
tmp1 = vec_scalar_mul(a2, d2)
102-
X = vec_add(Xb, tmp1)
142+
# point on the horizontal plane between n2,n3
143+
a2_scaled = vec_scalar_mul(a2, d2)
144+
X = vec_add(Xb, a2_scaled)
103145

104-
# Again, direction in next medium */
105-
n = vec_dot(a2, glass_dir)
106-
tmp2 = vec_subt(a2, tmp2)
107-
bp = unit_vector(tmp2)
146+
# Again, direction in next medium
147+
n = vec_dot(a2, glass_direction)
148+
transformed_direction_perpendicular_scaled = vec_subt(a2, transformed_direction_perpendicular_scaled)
149+
bp = unit_vector(transformed_direction_perpendicular_scaled)
108150

109151
p = np.sqrt(1 - n * n)
110-
p = p * mm.n2[0] / mm.n3
152+
p = p * refractive_index_medium2 / refractive_index_medium3
111153
n = -np.sqrt(1 - p * p)
112154

113-
tmp1 = vec_scalar_mul(bp, p)
114-
tmp2 = vec_scalar_mul(glass_dir, n)
115-
out = vec_add(tmp1, tmp2)
155+
transformed_direction_parallel_scaled = vec_scalar_mul(bp, p)
156+
transformed_direction_perpendicular_scaled = vec_scalar_mul(glass_direction, n)
157+
out = vec_add(transformed_direction_parallel_scaled, transformed_direction_perpendicular_scaled)
116158

117159
return X, out
160+
161+
# def fast_ray_tracing(
162+
# x,
163+
# y,
164+
# cc,
165+
# dm,
166+
# primary_point,
167+
# glass,
168+
# d,
169+
# n1,
170+
# n2,
171+
# n3,
172+
# ) -> Tuple[np.ndarray, np.ndarray]:
173+
# """ Fast ray tracing.
174+
175+
# Parameters:
176+
# - x, y, cc: Camera parameters
177+
# - dm: Distortion matrix
178+
# - primary_point: Primary point coordinates
179+
# - glass: Glass vector
180+
# - d: Distance parameter
181+
# - n1, n2, n3: Refractive indices
182+
183+
# Returns:
184+
# - Tuple containing the resulting point X and output direction vector
185+
# """
186+
# # Initial ray direction in global coordinate system
187+
# tmp1 = np.array([x, y, -1 * cc])
188+
# tmp1 = unit_vector(tmp1)
189+
# start_dir = np.empty(3, dtype=float)
190+
# matmul(start_dir, dm, tmp1, 3, 3, 1, 3, 3)
191+
192+
193+
# glass_dir = unit_vector(glass)
194+
# c = vec_norm(glass) + d
195+
196+
# # Project start ray on glass vector to find n1/n2 interface.
197+
# dist_cam_glass = vec_dot(glass_dir, primary_point) - c
198+
# tmp1 = float(vec_dot(glass_dir, start_dir))
199+
200+
# # avoid division by zero
201+
# if tmp1 == 0.0:
202+
# tmp1 = 1.0
203+
# d1 = -dist_cam_glass / tmp1
204+
205+
# tmp1 = vec_scalar_mul(start_dir, d1)
206+
# Xb = vec_add(primary_point, tmp1)
207+
208+
# # Break down ray into glass-normal and glass-parallel components. */
209+
# n = vec_dot(start_dir, glass_dir)
210+
# tmp1 = vec_scalar_mul(glass_dir, n)
211+
212+
# tmp2 = vec_subt(start_dir, tmp1)
213+
# bp = unit_vector(tmp2)
214+
215+
# # Transform to direction inside glass, using Snell's law
216+
# p = np.sqrt(1 - n * n) * n1 / n2
217+
# # glass parallel
218+
# n = -np.sqrt(1 - p * p)
219+
# # glass normal
220+
221+
# # Propagation length in glass parallel to glass vector */
222+
# tmp1 = vec_scalar_mul(bp, p)
223+
# tmp2 = vec_scalar_mul(glass_dir, n)
224+
# a2 = vec_add(tmp1, tmp2)
225+
226+
# tmp1 = np.abs(vec_dot(glass_dir, a2))
227+
228+
# # avoid division by zero
229+
# if tmp1 == 0:
230+
# tmp1 = 1.0
231+
# d2 = d / tmp1
232+
233+
# # point on the horizontal plane between n2,n3 */
234+
# tmp1 = vec_scalar_mul(a2, d2)
235+
# X = vec_add(Xb, tmp1)
236+
237+
# # Again, direction in next medium */
238+
# n = vec_dot(a2, glass_dir)
239+
# tmp2 = vec_subt(a2, tmp2)
240+
# bp = unit_vector(tmp2)
241+
242+
# p = np.sqrt(1 - n * n)
243+
# p = p * n2 / n3
244+
# n = -np.sqrt(1 - p * p)
245+
246+
# tmp1 = vec_scalar_mul(bp, p)
247+
# tmp2 = vec_scalar_mul(glass_dir, n)
248+
# out = vec_add(tmp1, tmp2)
249+
250+
# return X, out

openptv_python/vec_utils.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,81 +8,84 @@
88
import math
99

1010
import numpy as np
11+
from numba import njit
1112

1213
# Define the np.ndarray type as an numpy array of 3 floats
1314
# vec3d = np.empty(3, dtype=float)
1415

1516
# and 2 floats
1617
# = np.empty(2, dtype=float)
1718

18-
19+
@njit
1920
def norm(x: float, y: float, z: float) -> float:
2021
"""Return the norm of a 3D vector given by 3 float components."""
21-
return float(np.linalg.norm(vec_set(x, y, z)))
22-
22+
return vec_norm(vec_set(x, y, z))
2323

24+
@njit
2425
def vec_set(x: float, y: float, z: float) -> np.ndarray:
2526
"""Set the components of a 3D vector from separate doubles."""
26-
return np.r_[x, y, z]
27+
return np.array([x, y, z])
2728

2829

2930
def vec_copy(src: np.ndarray) -> np.ndarray:
3031
"""Copy one 3D vector into another."""
3132
return src.copy()
3233

33-
34+
@njit
3435
def vec_subt(from_: np.ndarray, sub: np.ndarray) -> np.ndarray:
3536
"""Subtract two 3D vectors."""
3637
return from_ - sub
3738

38-
39+
@njit
3940
def vec_add(vec1: np.ndarray, vec2: np.ndarray) -> np.ndarray:
4041
"""Add two 3D vectors."""
4142
return vec1 + vec2
4243

43-
44+
@njit
4445
def vec_scalar_mul(vec: np.ndarray, scalar: float) -> np.ndarray:
4546
"""vec_scalar_mul(np.ndarray, scalar) multiplies a vector by a scalar."""
4647
return vec * scalar
4748

48-
49+
@njit
4950
def vec_diff_norm(vec1: np.ndarray, vec2: np.ndarray) -> float:
5051
"""vec_diff_norm() gives the norm of the difference between two vectors."""
5152
# return np.linalg.norm(vec1 - vec2)
5253
vec = vec1 - vec2
5354
return math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2)
5455

55-
56+
@njit
5657
def vec_norm(vec: np.ndarray) -> float:
5758
"""vec_norm() gives the norm of a vector."""
5859
return math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2)
5960

60-
61+
@njit
6162
def vec_dot(vec1: np.ndarray, vec2: np.ndarray) -> float:
6263
"""vec_dot() gives the dot product of two vectors as lists of floats."""
6364
# return np.dot(vec1, vec2)
6465
return float(vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2])
6566

66-
67+
@njit
6768
def vec_cross(vec1: np.ndarray, vec2: np.ndarray) -> np.ndarray:
6869
"""Cross product of two vectors."""
6970
# return np.cross(vec1, vec2)
70-
return np.r_[vec1[1]*vec2[2] - vec1[2]*vec2[1],
71+
return np.array([vec1[1]*vec2[2] - vec1[2]*vec2[1],
7172
vec1[2]*vec2[0] - vec1[0]*vec2[2],
72-
vec1[0]*vec2[1] - vec1[1]*vec2[0]]
73-
73+
vec1[0]*vec2[1] - vec1[1]*vec2[0]])
7474

75+
@njit
7576
def vec_cmp(vec1: np.ndarray, vec2: np.ndarray, tol: float = 1e-6) -> bool:
7677
"""vec_cmp() checks whether two vectors are equal within a tolerance."""
7778
return np.allclose(vec1, vec2, atol=tol)
7879

80+
@njit
7981
def unit_vector(vec: np.ndarray) -> np.ndarray:
8082
"""Normalize a vector to a unit vector."""
81-
magnitude = np.linalg.norm(vec)
83+
magnitude = vec_norm(vec)
8284
if magnitude == 0:
8385
return vec # Avoid division by zero for zero vectors
8486
return vec / magnitude
8587

88+
@njit
8689
def vec_init(length=3) -> np.ndarray:
8790
"""Initialize a vector to zero."""
8891
return np.zeros(length, dtype=float)

0 commit comments

Comments
 (0)