Skip to content

Commit 248653c

Browse files
committed
numpy recarrays work
1 parent d28ac96 commit 248653c

13 files changed

Lines changed: 205 additions & 137 deletions

openptv_python/correspondences.py

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@
44
import numpy as np
55

66
from .calibration import Calibration
7-
from .constants import COORD_UNUSED, CORRES_NONE, MAX_TARGETS, MAXCAND, NMAX, PT_UNUSED
7+
from .constants import (
8+
COORD_UNUSED,
9+
CORRES_NONE,
10+
MAX_TARGETS,
11+
MAXCAND,
12+
NMAX,
13+
PT_UNUSED,
14+
)
815
from .epi import epi_mm
916
from .find_candidate import find_candidate
1017
from .parameters import ControlPar, VolumePar
@@ -44,8 +51,8 @@ def __init__(self, targs, cpar, cal, tol=0.00001, reset_numbers=True):
4451
don't.
4552
"""
4653
self._num_pts = len(targs)
47-
self.buf = np.empty(self._num_pts, \
48-
dtype=np.dtype([('x', np.float64), ('y', np.float64), ('pnr', np.int32)]))
54+
self.buf = np.empty(self._num_pts,
55+
dtype=np.dtype([('x', np.float64), ('y', np.float64), ('pnr', np.int32)]))
4956

5057
for tnum, targ in enumerate(targs):
5158
targ = targs[tnum]
@@ -57,7 +64,7 @@ def __init__(self, targs, cpar, cal, tol=0.00001, reset_numbers=True):
5764

5865
self.buf[tnum]['x'], self.buf[tnum]['y'] = dist_to_flat(
5966
self.buf[tnum]['x'], self.buf[tnum]['y'], cal, tol
60-
)
67+
)
6168
self.buf[tnum]['pnr'] = targ.pnr
6269

6370
self.buf.sort(order='x')
@@ -100,16 +107,17 @@ def __del__(self):
100107

101108

102109
Correspond_dtype = np.dtype([
103-
('p1', np.int32), # PT_UNUSED
104-
('n', np.int32), # 0
105-
('p2', (np.int32, MAXCAND)), # np.zeros
106-
('corr', (np.float64, MAXCAND)), # np.zeros
107-
('dist', (np.float64, MAXCAND)) # np.zeros
110+
('p1', np.int32), # PT_UNUSED
111+
('n', np.int32), # 0
112+
('p2', (np.int32, MAXCAND)), # np.zeros
113+
('corr', (np.float64, MAXCAND)), # np.zeros
114+
('dist', (np.float64, MAXCAND)) # np.zeros
108115
])
109116

117+
110118
def safely_allocate_target_usage_marks(
111119
num_cams: int, nmax: int = NMAX
112-
) -> np.ndarray: # num_cams x nmax instead of List[List[int]]:
120+
) -> np.ndarray: # num_cams x nmax instead of List[List[int]]:
113121
"""Allocate space for per-camera arrays marking whether a certain target was used.
114122
115123
If some allocation failed, it cleans up memory and returns NULL. Allocated arrays are zeroed
@@ -134,6 +142,7 @@ def safely_allocate_target_usage_marks(
134142

135143
return np.zeros((num_cams, nmax), dtype=np.int32)
136144

145+
137146
def safely_allocate_adjacency_lists(
138147
num_cams: int, target_counts: List[int]
139148
) -> np.recarray:
@@ -148,7 +157,8 @@ def safely_allocate_adjacency_lists(
148157
# for c1 in range(num_cams)
149158
# ]
150159

151-
lists = np.recarray((num_cams, num_cams, max(target_counts)),dtype=Correspond_dtype)
160+
lists = np.recarray((num_cams, num_cams, max(
161+
target_counts)), dtype=Correspond_dtype)
152162

153163
except MemoryError as exc:
154164
raise MemoryError("Failed to allocate adjacency lists.") from exc
@@ -160,9 +170,9 @@ def safely_allocate_adjacency_lists(
160170
lists.corr = np.zeros(MAXCAND)
161171
lists.dist = np.zeros(MAXCAND)
162172

163-
164173
return lists
165174

175+
166176
def four_camera_matching(
167177
corr_list: np.recarray,
168178
base_target_count,
@@ -240,7 +250,7 @@ def four_camera_matching(
240250

241251

242252
def three_camera_matching(
243-
corr_list: np.recarray, # num_cam, num_cam, num_targets
253+
corr_list: np.recarray, # num_cam, num_cam, num_targets
244254
num_cams,
245255
target_counts,
246256
accept_corr,
@@ -345,7 +355,8 @@ def consistent_pair_matching(
345355
if p2 >= nmax or tusage[i2][p2] > 0:
346356
continue
347357

348-
corr = corr_list[i1][i2][i].corr[0] / corr_list[i1][i2][i].dist[0]
358+
corr = corr_list[i1][i2][i].corr[0] / \
359+
corr_list[i1][i2][i].dist[0]
349360
if corr <= accept_corr:
350361
continue
351362

@@ -365,8 +376,8 @@ def consistent_pair_matching(
365376

366377

367378
def match_pairs(
368-
corr_lists: np.recarray, # num_cam, num_cam, num_targets
369-
corrected: np.recarray, # List[List[Coord2d]],
379+
corr_lists: np.recarray, # num_cam, num_cam, num_targets
380+
corrected: np.recarray, # List[List[Coord2d]],
370381
frm: Frame,
371382
vpar: VolumePar,
372383
cpar: ControlPar,
@@ -466,7 +477,8 @@ def match_pairs(
466477

467478

468479
def take_best_candidates(
469-
src: np.recarray, dst: np.recarray, num_cams: int, tusage: np.ndarray #List[n_tupel]
480+
# List[n_tupel]
481+
src: np.recarray, dst: np.recarray, num_cams: int, tusage: np.ndarray
470482
):
471483
"""
472484
Take the best candidates from the candidate list based on their correlation measure.
@@ -509,8 +521,8 @@ def take_best_candidates(
509521
taken = 0
510522

511523
# Sort candidates by match quality (.corr)
512-
src.sort(order='corr') # by corr
513-
src = src[::-1] # reverse order
524+
src.sort(order='corr') # by corr
525+
src = src[::-1] # reverse order
514526

515527
# Take candidates from the top to the bottom of the sorted list
516528
# Only take if none of the corresponding targets have been used
@@ -613,7 +625,8 @@ def py_correspondences(
613625
frm.targets[cam] = img_pts[cam]
614626

615627
# The biz:
616-
corresp_buf = correspondences(frm, flat_coords, vparam, cparam, calib, match_counts)
628+
corresp_buf = correspondences(
629+
frm, flat_coords, vparam, cparam, calib, match_counts)
617630

618631
# Distribute data to return structures:
619632
# sorted_pos = [None] * (num_cams - 1)
@@ -623,9 +636,12 @@ def py_correspondences(
623636
last_count = 0
624637

625638
for clique_type in range(num_cams - 1):
626-
num_points = match_counts[4 - num_cams + clique_type] # for 1-4 cameras
627-
clique_targs = np.full((num_cams, num_points, 2), PT_UNUSED, dtype=np.float64)
628-
clique_ids = np.full((num_cams, num_points), CORRES_NONE, dtype=np.int_)
639+
num_points = match_counts[4 - num_cams +
640+
clique_type] # for 1-4 cameras
641+
clique_targs = np.full((num_cams, num_points, 2),
642+
PT_UNUSED, dtype=np.float64)
643+
clique_ids = np.full((num_cams, num_points),
644+
CORRES_NONE, dtype=np.int_)
629645

630646
# Trace back the pixel target properties through the flat metric
631647
# intermediary that's x-sorted.
@@ -662,7 +678,7 @@ def correspondences(
662678
cpar: ControlPar,
663679
calib: List[Calibration],
664680
match_counts: List[int],
665-
) -> np.recarray: # n_tupel_dtype
681+
) -> np.recarray: # n_tupel_dtype
666682
"""Find correspondences between cameras.
667683
668684
/* correspondences() generates a list of tuple target numbers (one for each
@@ -699,8 +715,13 @@ def correspondences(
699715
nmax = NMAX
700716

701717
# Allocation of scratch buffers for internal tasks and return-value space
702-
con0 = np.recarray((nmax * cpar.num_cams), dtype=n_tupel_dtype)
703-
con = np.recarray((nmax * cpar.num_cams), dtype=n_tupel_dtype)
718+
con0 = np.recarray((nmax * cpar.num_cams,), dtype=n_tupel_dtype)
719+
con0.p = 0
720+
con0.corr = 0.0
721+
722+
con = np.recarray((nmax * cpar.num_cams,), dtype=n_tupel_dtype)
723+
con.p = 0
724+
con.corr = 0.0
704725

705726
tim = safely_allocate_target_usage_marks(cpar.num_cams, nmax)
706727

@@ -732,7 +753,7 @@ def correspondences(
732753
)
733754

734755
match_counts[1] = take_best_candidates(
735-
con0, con[match_counts[3] :], cpar.num_cams, tim
756+
con0, con[match_counts[3]:], cpar.num_cams, tim
736757
)
737758
match_counts[3] += match_counts[1]
738759

@@ -742,7 +763,7 @@ def correspondences(
742763
corr_list, cpar.num_cams, frm.num_targets, vpar.corrmin, con0, 4 * nmax, tim
743764
)
744765
match_counts[2] = take_best_candidates(
745-
con0, con[match_counts[3] :], cpar.num_cams, tim
766+
con0, con[match_counts[3]:], cpar.num_cams, tim
746767
)
747768
match_counts[3] += match_counts[2]
748769

@@ -766,7 +787,7 @@ def correspondences(
766787

767788

768789
def single_cam_correspondences(
769-
img_pts: List[Target], corrected: np.recarray #List[Coord2d]
790+
img_pts: List[Target], corrected: np.recarray # List[Coord2d]
770791
) -> Tuple[List[np.ndarray], List[np.ndarray], int]:
771792
"""
772793
Single camera correspondence is not a real correspondence, it will be only a projection.

openptv_python/find_candidate.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ def find_candidate(
145145
# cand.append(Candidate(pnr=j, tol=d, corr=corr))
146146
cand.append(np.array([(j, d, corr)], dtype=Candidate_dtype)) # type: ignore
147147

148-
out = np.array(cand).view(np.recarray).flatten()
148+
149+
out = np.array(cand).view(np.recarray).flatten()
149150
# print(f"appended: {cand[-1]}")
150151

151152
return out # type: ignore

openptv_python/track.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from .imgcoord import img_coord
2424
from .orientation import point_position
2525
from .parameters import ControlPar, SequencePar, TrackPar, VolumePar
26-
from .tracking_frame_buf import Corres_dtype, Frame, Pathinfo, Target
26+
from .tracking_frame_buf import Frame, Pathinfo, Target
2727
from .tracking_run import TrackingRun
2828
from .trafo import dist_to_flat, metric_to_pixel, pixel_to_metric
2929
from .vec_utils import vec_copy, vec_diff_norm, vec_subt
@@ -760,8 +760,8 @@ def add_particle(frm: Frame, pos: np.ndarray, cand_inds: np.ndarray) -> None:
760760
else:
761761
ref_path_inf = Pathinfo()
762762
frm.path_info.append(ref_path_inf)
763-
764-
frm.correspond.append(np.recarray((1,), dtype=Corres_dtype))
763+
frm.correspond = np.resize(frm.correspond, frm.correspond.shape[0] + 1).view(np.recarray)
764+
ref_corres = frm.correspond.view(np.recarray)
765765

766766
ref_path_inf.x = vec_copy(pos)
767767
ref_path_inf.reset_links()

openptv_python/tracking_frame_buf.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
PREV_NONE,
1616
PRIO_DEFAULT,
1717
PT_UNUSED,
18+
TR_UNUSED,
1819
)
1920
from .epi import Coord2d_dtype
2021
from .parameters import ControlPar
@@ -26,7 +27,7 @@
2627
])
2728

2829

29-
def quicksort_n_tupel(arr: np.ndarray) -> np.ndarray:
30+
def quicksort_n_tupel(arr: np.recarray) -> np.recarray:
3031
"""
3132
Quicksorts a list of n_tupel instances based on the corr attribute.
3233
@@ -40,7 +41,9 @@ def quicksort_n_tupel(arr: np.ndarray) -> np.ndarray:
4041
-------
4142
A list of n_tupel instances, sorted by the corr attribute.
4243
"""
43-
return np.sort(arr, order="corr")
44+
# inline sorting
45+
arr.sort(order="corr")
46+
return arr
4447

4548

4649

@@ -56,7 +59,7 @@ def quicksort_n_tupel(arr: np.ndarray) -> np.ndarray:
5659
# return self.nr == other.nr and np.all(self.p == other.p)
5760

5861

59-
def compare_corres(c1: np.ndarray, c2: np.ndarray) -> bool:
62+
def compare_corres(c1: np.recarray, c2: np.recarray) -> bool:
6063
"""
6164
Compare two Corres instances.
6265
@@ -69,7 +72,7 @@ def compare_corres(c1: np.ndarray, c2: np.ndarray) -> bool:
6972
-------
7073
True if the Corres instances are equal, False otherwise.
7174
"""
72-
return np.array_equal(c1, c2)
75+
return (c1 == c2).all()
7376

7477

7578
@dataclass
@@ -317,7 +320,9 @@ def __init__(self, num_cams: int, max_targets: int = MAX_TARGETS):
317320
"""
318321
self.path_info = [Pathinfo() for _ in range(max_targets)]
319322

320-
self.correspond = [np.recarray((1,), dtype=Corres_dtype) for _ in range(max_targets)]
323+
self.correspond = np.recarray((max_targets), dtype=Corres_dtype)
324+
self.correspond.p = TR_UNUSED
325+
self.correspond.nr = 0
321326

322327
self.targets = [[Target() for _ in range(max_targets)] for _ in range(num_cams)]
323328
# self.targets = [[] for _ in range(num_cams)]
@@ -689,7 +694,7 @@ def read_path_frame(
689694
linkage_file_base: str,
690695
prio_file_base: str,
691696
frame_num: int,
692-
) -> Tuple[List[np.recarray], List[Pathinfo]]: #List[Corres]
697+
) -> Tuple[np.recarray, List[Pathinfo]]: #List[Corres]
693698
"""Read a rt_is frames from the disk.
694699
695700
/* Reads rt_is files. these files contain both the path info and the
@@ -719,14 +724,14 @@ def read_path_frame(
719724
filein = open(fname, "r", encoding="utf-8")
720725
except IOError:
721726
print(f"Can't open ascii file: {fname}")
722-
return [np.recarray(0,dtype=Corres_dtype)], []
727+
return np.recarray(0, dtype=Corres_dtype), []
723728

724729
# we do not need number of particles, reading till EOF
725730
n_particles = int(filein.readline())
726731
# print(f"Reading {n_particles} particles from {fname}")
727732
# cor_buf = [Corres() for _ in range(n_particles)] # we do not want empty lists
728733

729-
cor_buf = [np.recarray((0,), dtype=Corres_dtype) for _ in range(n_particles)] # we do not want empty lists
734+
cor_buf = np.recarray((n_particles), dtype=Corres_dtype) # we do not want empty lists
730735

731736
path_buf = [Pathinfo() for _ in range(n_particles)]
732737

@@ -736,7 +741,7 @@ def read_path_frame(
736741
linkagein = open(fname, "r", encoding="utf-8")
737742
except IOError:
738743
print(f"Can't open linkage file: {fname}")
739-
return [np.recarray(0, dtype=Corres_dtype)], []
744+
return np.recarray(0, dtype=Corres_dtype), []
740745

741746
linkagein.readline()
742747
else:
@@ -748,7 +753,7 @@ def read_path_frame(
748753
prioin = open(fname, "r", encoding="utf-8")
749754
except IOError:
750755
print(f"Can't open prio file: {fname}")
751-
return [], []
756+
return np.recarray(0, dtype=Corres_dtype), []
752757

753758
prioin.readline()
754759
else:
@@ -781,7 +786,7 @@ def read_path_frame(
781786

782787
vals = np.fromstring(line, dtype=float, sep=" ")
783788
cor_buf[targets].nr = targets + 1
784-
cor_buf[targets].p = vals[-4:].astype(int).tolist()
789+
cor_buf[targets].p = vals[-4:].astype(int)
785790
path_buf[targets].x = vals[1:-4]
786791

787792
# print(cor_buf[targets].nr, cor_buf[targets].p, path_buf[targets].x)
@@ -798,7 +803,7 @@ def read_path_frame(
798803

799804

800805
def write_path_frame(
801-
cor_buf: List[np.recarray], #List[Corres],
806+
cor_buf: np.recarray, #List[Corres],
802807
path_buf: List[Pathinfo],
803808
num_parts: int,
804809
corres_file_base: str,

0 commit comments

Comments
 (0)