Skip to content

Commit 1e6f0bf

Browse files
committed
drastically improve performance by using numba for loops
1 parent b03afd3 commit 1e6f0bf

File tree

3 files changed

+67
-61
lines changed

3 files changed

+67
-61
lines changed

bb_stitcher/core.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ def __init__(self, config=None):
3939
self._world_homo_left = None
4040
self._world_homo_right = None
4141

42+
self._stitcher = None
43+
4244
def _acept_filehandler(self, filehandler):
4345
filehandler.visit_surveyor(self)
4446

@@ -194,16 +196,16 @@ def map_points_angles(self, points, angles, cam_id):
194196
For all angles in ``angles`` it is assumed that a 0°-angle shows to the right border of
195197
the image and that a positive angle means clockwise rotation.
196198
"""
197-
stitch = stitcher.Stitcher(self.config)
199+
self._stitcher = self._stitcher or stitcher.Stitcher(self.config)
198200

199201
# using modified homographies to map points and angles to world coordinates
200-
stitch.load_parameters(self._world_homo_left, self._world_homo_right,
202+
self._stitcher.load_parameters(self._world_homo_left, self._world_homo_right,
201203
self.size_left, self.size_right)
202204

203205
if cam_id == self.cam_id_left:
204-
points, angles = stitch.map_left_points_angles(points, angles)
206+
points, angles = self._stitcher.map_left_points_angles(points, angles)
205207
elif cam_id == self.cam_id_right:
206-
points, angles = stitch.map_right_points_angles(points, angles)
208+
points, angles = self._stitcher.map_right_points_angles(points, angles)
207209
else:
208210
raise ValueError('Got invalid cam_id {invalid_ID} cam_id must be '
209211
'{left_ID} or {right_ID}.'.format(invalid_ID=cam_id,

bb_stitcher/helpers.py

Lines changed: 60 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import cv2
1919
import numpy as np
20+
from numba import njit
2021

2122
log = getLogger(__name__)
2223

@@ -378,7 +379,7 @@ def harmonize_rects(rect_a, rect_b):
378379
harm_rect_a = form_rectangle(harm_hori_a, harm_vert_a)
379380
return harm_rect_a, rect_b
380381

381-
382+
@njit
382383
def angles_to_points(angle_centers, angles, distance=22):
383384
r"""Calculate point representations of angles.
384385
@@ -413,13 +414,66 @@ def angles_to_points(angle_centers, angles, distance=22):
413414
"""
414415
assert len(angle_centers) == len(angles)
415416
points_repr = np.zeros((len(angle_centers), 2), dtype=np.float32)
416-
for i, center in enumerate(angle_centers):
417+
for i in range(angle_centers.shape[0]):
418+
center = angle_centers[i, :]
417419
z_rotation = np.array(angles[i])
418420
# remove round
419421
points_repr[i, 0] = center[0] + distance * np.cos(z_rotation)
420422
points_repr[i, 1] = center[1] + distance * np.sin(z_rotation)
421423
return points_repr
422424

425+
@njit
426+
def _process_points_to_angles(angle_centers, points_repr):
427+
angles = np.zeros(len(angle_centers), dtype=np.float32)
428+
429+
for i in range(angle_centers.shape[0]):
430+
angle_center = angle_centers[i, :]
431+
point_repr = points_repr[i]
432+
angle_center_x, angle_center_y = angle_center
433+
point_repr_x, point_repr_y = point_repr
434+
435+
# the 0-angle has to be a ray from the center, which is perpendicular to the right border
436+
# we abstract this ray as a point ``ray_pt`` which always lies on the right side of the
437+
# center, so ``ray_pt_dis`` has to be just greater 0, we take 80
438+
ray_pt_dis = np.float64(80.)
439+
ray_pt = np.array([angle_center_x + ray_pt_dis, angle_center_y])
440+
441+
"""
442+
angle_center p ray_pt
443+
*---------------*
444+
\ | /
445+
\ angle / /
446+
r \ / / d
447+
\ --´ /
448+
\ /
449+
\ /
450+
\ /
451+
point_repr *
452+
"""
453+
454+
d = np.linalg.norm(ray_pt - point_repr)
455+
p = ray_pt_dis
456+
r = np.linalg.norm(angle_center - point_repr)
457+
if r == 0:
458+
return None
459+
cos_angle = (p ** 2 + r ** 2 - d ** 2) / (2 * r * p)
460+
461+
# this is due to some arithmetic problems, where cos_angle is something like
462+
# cos_angle = 1.000000000000008 which leads to an error.
463+
if cos_angle > 1 and cos_angle < 1 + 1e-9:
464+
cos_angle = 1
465+
elif cos_angle < -1 and cos_angle > -1 - 1e-9:
466+
cos_angle = -1
467+
468+
angle = np.arccos(cos_angle)
469+
470+
if angle_center_y > point_repr_y:
471+
angle = -angle
472+
473+
angles[i] = angle
474+
475+
return angles
476+
423477

424478
def points_to_angles(angle_centers, points_repr):
425479
"""Convert angle point representation back to normal angle.
@@ -439,63 +493,12 @@ def points_to_angles(angle_centers, points_repr):
439493
"""Calculate angle between vertical line passing through angle_centers and line AB."""
440494
# https://de.wikipedia.org/wiki/Roll-Nick-Gier-Winkel#/media/File:RPY_angles_of_spaceships_(local_frame).png
441495
# TODO(zeor_angle) variablen Nullwinkel einbauen, momentan ist entspricht dieser der x-Achse
442-
import warnings
443-
warnings.filterwarnings('error')
444-
np.seterr(all='warn')
445-
446496
assert len(angle_centers) == len(points_repr)
447497

448-
angles = np.zeros(len(angle_centers), dtype=np.float32)
449-
for i, angle_center in enumerate(angle_centers):
450-
point_repr = points_repr[i]
451-
angle_center_x, angle_center_y = angle_center
452-
point_repr_x, point_repr_y = point_repr
453-
454-
# the 0-angle has to be a ray from the center, which is perpendicular to the right border
455-
# we abstract this ray as a point ``ray_pt`` which always lies on the right side of the
456-
# center, so ``ray_pt_dis`` has to be just greater 0, we take 80
457-
ray_pt_dis = np.float64(80)
458-
ray_pt = np.array([angle_center_x + ray_pt_dis, angle_center_y])
459-
460-
"""
461-
angle_center p ray_pt
462-
*---------------*
463-
\ | /
464-
\ angle / /
465-
r \ / / d
466-
\ --´ /
467-
\ /
468-
\ /
469-
\ /
470-
point_repr *
471-
"""
472-
473-
d = np.linalg.norm(ray_pt - point_repr)
474-
p = ray_pt_dis
475-
r = np.linalg.norm(np.float64(angle_center) - point_repr)
476-
if r == 0:
477-
raise Exception('Angle center point {} and angle point representation {}'
478-
' seams to be the same.'.format(angle_center, point_repr))
479-
cos_angle = (p ** 2 + r ** 2 - d ** 2) / (2 * r * p)
480-
481-
# this is due to some arithmetic problems, where cos_angle is something like
482-
# cos_angle = 1.000000000000008 which leads to an error.
483-
if cos_angle > 1 and np.isclose(cos_angle, 1, atol=1e-10, rtol=1e-9):
484-
cos_angle = 1
485-
elif cos_angle < -1 and np.isclose(cos_angle, -1, atol=1e-10, rtol=1e-9):
486-
cos_angle = -1
487-
try:
488-
angle = np.arccos(cos_angle)
489-
except Warning:
490-
print('angle_centers = {angle_centers}, '
491-
'points_repr = {points_repr}'.format(angle_centers=angle_centers,
492-
points_repr=points_repr))
493-
raise Exception('arccos can not handle {cos_angle}'.format(cos_angle=cos_angle))
494-
495-
if angle_center_y > point_repr_y:
496-
angle = -angle
497-
498-
angles[i] = angle
498+
angles = _process_points_to_angles(angle_centers, points_repr)
499+
if angles is None:
500+
raise Exception('Angle center point {} and angle point representation {}'
501+
' seams to be the same.'.format(angle_centers, points_repr))
499502

500503
return angles
501504

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
numpy>=1.11.3 # BSD 3-clause license
22
matplotlib>=1.5.3 # PSF license
3+
numba
34
# cv2 -> BSD 3-clause
45
# SURF from opencv_contrib is noncommercial licensed, see http://www.vision.ee.ethz.ch/~surf/download.html

0 commit comments

Comments
 (0)