Skip to content

Commit fea7f89

Browse files
authored
Merge pull request #22 from originlake/fast_undistort
added lru caching for faster undistortion
2 parents 589816e + 1d4cfdc commit fea7f89

File tree

5 files changed

+44
-4
lines changed

5 files changed

+44
-4
lines changed

opensfm/commands/undistort.py

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def run_impl(self, dataset: DataSet, args: argparse.Namespace) -> None:
1616
args.reconstruction_index,
1717
args.tracks,
1818
args.output,
19+
None,
1920
args.skip_images
2021
)
2122

opensfm/context.py

+35-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
import cv2
1313
from joblib import Parallel, delayed, parallel_backend
14-
14+
from multiprocessing import Lock as ProcessLock, Manager as ProcessManager
15+
from multiprocessing.dummy import Lock as ThreadLock
16+
import functools
1517

1618
logger: logging.Logger = logging.getLogger(__name__)
1719

@@ -61,7 +63,38 @@ def parallel_map(func, args, num_proc: int, max_batch_size: int = 1, backend="th
6163
cv2.setNumThreads(threads_used)
6264
return res
6365

64-
66+
def lru_cache(maxsize=128, use_multiprocessing=False):
67+
"""Least-recently-used cache decorator."""
68+
def decorator(func):
69+
if use_multiprocessing:
70+
manager = ProcessManager()
71+
cache = manager.dict()
72+
keys = manager.list()
73+
lock = ProcessLock()
74+
else:
75+
cache = {}
76+
keys = []
77+
lock = ThreadLock()
78+
79+
@functools.wraps(func)
80+
def wrapper(*args, **kwargs):
81+
key = (args, tuple(kwargs.items()))
82+
with lock:
83+
if key in cache:
84+
keys.remove(key)
85+
keys.append(key)
86+
return cache[key]
87+
result = func(*args, **kwargs)
88+
if len(keys) >= maxsize:
89+
old_key = keys.pop(0)
90+
del cache[old_key]
91+
keys.append(key)
92+
cache[key] = result
93+
return result
94+
95+
return wrapper
96+
97+
return decorator
6598
# Memory usage
6699

67100
if sys.platform == "win32":

opensfm/src/robust/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ set(ROBUST_FILES
1414
src/relative_pose_model.cc
1515
src/line_model.cc
1616
src/instanciations.cc
17+
src/similarity_model.cc
1718
)
1819
add_library(robust ${ROBUST_FILES})
1920
target_link_libraries(robust
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#include "robust/similarity_model.h"
2+
3+
4+
const int Similarity::MINIMAL_SAMPLES;

opensfm/undistort.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
types,
1414
features_processing,
1515
)
16-
from opensfm.context import parallel_map
16+
from opensfm.context import parallel_map, lru_cache
1717
from opensfm.dataset import UndistortedDataSet
1818
from opensfm.dataset_base import DataSetBase
1919

@@ -153,6 +153,7 @@ def undistort_image_and_masks(arguments) -> None:
153153
for k, v in undistorted.items():
154154
udata.save_undistorted_segmentation(k, v)
155155

156+
compute_camera_mapping_lru = lru_cache(maxsize=100)(pygeometry.compute_camera_mapping)
156157

157158
def undistort_image(
158159
shot: pymap.Shot,
@@ -180,7 +181,7 @@ def undistort_image(
180181
[undistorted_shot] = undistorted_shots
181182
new_camera = undistorted_shot.camera
182183
height, width = original.shape[:2]
183-
map1, map2 = pygeometry.compute_camera_mapping(
184+
map1, map2 = compute_camera_mapping_lru(
184185
shot.camera, new_camera, width, height
185186
)
186187
undistorted = cv2.remap(original, map1, map2, interpolation)

0 commit comments

Comments
 (0)