Skip to content

Commit 768aaaa

Browse files
authored
Merge pull request #158 from alexlib/single_cam
Single camera case
2 parents f125847 + eda5163 commit 768aaaa

40 files changed

Lines changed: 458 additions & 25 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ liboptv/build
1414
py_bind/build
1515
*~
1616

17+
py_bind/optv/.vscode/settings.json
18+
py_bind/optv/.vscode/c_cpp_properties.json

liboptv/src/correspondences.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ Routine: correspondences.c
44
55
Author/Copyright: Hans-Gerd Maas
66
7-
Address: Institute of correcteddesy and Photogrammetry
7+
Address: Institute of Geodesy and Photogrammetry
88
ETH - Hoenggerberg
99
CH - 8093 Zurich
1010
1111
Creation Date: 1988/89
1212
1313
Description: establishment of correspondences for 2/3/4 cameras
1414
15+
Modified for OpenPTV: Yosef Meller and Alex Liberzon
16+
17+
Date: 2013-2018
18+
1519
****************************************************************************/
1620

1721
#include <stdio.h>

liboptv/src/segmentation.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ int targ_rec (unsigned char *img, target_par *targ_par, int xmin,
113113

114114
for (n=0; n<4; n++) {
115115
xn = x4[n]; yn = y4[n];
116+
if (!(xn < xmax) || !(yn < ymax)) continue;
116117
gv = *(img0 + imx*yn + xn);
117118

118119
/* conditions for threshold, discontinuity, image borders */

liboptv/tests/check_track.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ int copy_res_dir(char *src, char *dest) {
6565
return 1;
6666
}
6767
}
68+
return 0;
6869
}
6970

7071
int empty_res_dir() {
@@ -87,6 +88,7 @@ int empty_res_dir() {
8788
return 1;
8889
}
8990
}
91+
return 0;
9092
}
9193

9294
START_TEST(test_predict)

py_bind/optv/.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"python.pythonPath": "/Users/alex/miniconda3/envs/pyptv/bin/python"
3+
}

py_bind/optv/correspondences.pyx

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
# -*- coding: utf-8 -*-
21
"""
32
Implementation of bindings for correspondences and related data structures.
43
54
Created on Fri Oct 28 13:46:39 2016
65
7-
@author: yosef
6+
@author: Yosef Meller, Alex Liberzon, TAU
87
"""
98

109
from libc.stdlib cimport malloc, calloc, free
@@ -17,7 +16,7 @@ from optv.calibration cimport Calibration, calibration
1716
from optv.orientation cimport COORD_UNUSED
1817
from optv.tracking_framebuf cimport TargetArray, Target, target, frame, \
1918
PT_UNUSED, CORRES_NONE
20-
19+
2120
cdef class MatchedCoords:
2221
"""
2322
Keeps a block of 2D flat coordinates, each with a "point number", the same
@@ -145,9 +144,15 @@ def correspondences(list img_pts, list flat_coords, list cals,
145144
num_targs - total number of targets (must be greater than the sum of
146145
previous 3).
147146
"""
147+
cdef int num_cams = len(cals)
148+
149+
# Special case of a single camera, follow the single_cam_correspondence docstring
150+
if num_cams == 1:
151+
sorted_pos, sorted_corresp, num_targs = single_cam_correspondence(img_pts, flat_coords, cals)
152+
return sorted_pos, sorted_corresp, num_targs
153+
148154
cdef:
149155
int pt, cam
150-
int num_cams = len(cals)
151156

152157
calibration **calib = <calibration **> malloc(
153158
num_cams * sizeof(calibration *))
@@ -171,49 +176,115 @@ def correspondences(list img_pts, list flat_coords, list cals,
171176
frm.targets[cam] = (<TargetArray>img_pts[cam])._tarr
172177
frm.num_targets[cam] = len(img_pts[cam])
173178
corrected[cam] = (<MatchedCoords>flat_coords[cam]).buf
174-
179+
175180
# The biz:
176181
corresp_buf = corresp(&frm, corrected,
177182
vparam._volume_par, cparam._control_par, calib, match_counts)
178-
183+
179184
# Distribute data to return structures:
180185
sorted_pos = [None]*(num_cams - 1)
181186
sorted_corresp = [None]*(num_cams - 1)
182187
last_count = 0
183-
188+
184189
for clique_type in xrange(num_cams - 1):
185190
num_points = match_counts[clique_type]
186191
clique_targs = np.full((num_cams, num_points, 2), PT_UNUSED,
187192
dtype=np.float64)
188193
clique_ids = np.full((num_cams, num_points), CORRES_NONE,
189194
dtype=np.int_)
190-
195+
191196
# Trace back the pixel target properties through the flat metric
192197
# intermediary that's x-sorted.
193198
for cam in range(num_cams):
194199
for pt in range(num_points):
195200
geo_id = corresp_buf[pt + last_count].p[cam]
196201
if geo_id < 0:
197202
continue
198-
203+
199204
p1 = corrected[cam][geo_id].pnr
200205
clique_ids[cam, pt] = p1
201206

202207
if p1 > -1:
203208
targ = img_pts[cam][p1]
204209
clique_targs[cam, pt, 0] = (<Target> targ)._targ.x
205210
clique_targs[cam, pt, 1] = (<Target> targ)._targ.y
206-
211+
207212
last_count += num_points
208213
sorted_pos[clique_type] = clique_targs
209214
sorted_corresp[clique_type] = clique_ids
210-
215+
211216
# Clean up.
212217
num_targs = match_counts[num_cams - 1]
218+
219+
220+
213221
free(frm.targets)
214222
free(frm.num_targets)
215223
free(calib)
216224
free(match_counts)
217225
free(corresp_buf) # Note this for future returning of correspondences.
218226

219227
return sorted_pos, sorted_corresp, num_targs
228+
229+
230+
def single_cam_correspondence(list img_pts, list flat_coords, list cals):
231+
"""
232+
Single camera correspondence is not a real correspondence, it will be only a projection
233+
of a 2D target from the image space into the 3D position, x,y,z using epi_mm_2d
234+
function. Here we only update the pointers of the targets and return it in a proper format.
235+
236+
Arguments:
237+
img_pts - a list of c := len(cals), containing TargetArray objects, each
238+
with the target coordinates of n detections in the respective image.
239+
The target arrays are clobbered: returned arrays have the tnr property
240+
set. the pnr property should be set to the target index in its array.
241+
flat_coords - a list of MatchedCoordinates objects, one per camera, holding
242+
the x-sorted flat-coordinates conversion of the respective image
243+
targets.
244+
cals - a list of Calibration objects, each for the camera taking one image.
245+
246+
Returns:
247+
sorted_pos - a tuple of (c,?,2) arrays, each with the positions in each of
248+
c image planes of points belonging to quadruplets, triplets, pairs
249+
found.
250+
sorted_corresp - a tuple of (c,?) arrays, each with the point identifiers
251+
of targets belonging to a quad/trip/etc per camera.
252+
num_targs - total number of targets (must be greater than the sum of
253+
previous 3).
254+
"""
255+
cdef:
256+
int pt, num_points
257+
coord_2d *corrected = <coord_2d *> malloc(sizeof(coord_2d *))
258+
259+
corrected = (<MatchedCoords>flat_coords[0]).buf
260+
261+
sorted_pos = [None]
262+
sorted_corresp = [None]
263+
264+
num_points = len(img_pts[0])
265+
266+
clique_targs = np.full((1, num_points, 2), PT_UNUSED,
267+
dtype=np.float64)
268+
clique_ids = np.full((1, num_points), CORRES_NONE,
269+
dtype=np.int_)
270+
271+
# Trace back the pixel target properties through the flat metric
272+
# intermediary that's x-sorted.
273+
for pt in range(num_points):
274+
275+
# From Beat code (issue #118) pix[0][geo[0][i].pnr].tnr=i;
276+
277+
p1 = corrected[pt].pnr
278+
clique_ids[0, pt] = p1
279+
280+
if p1 > -1:
281+
targ = img_pts[0][p1]
282+
clique_targs[0, pt, 0] = (<Target> targ)._targ.x
283+
clique_targs[0, pt, 1] = (<Target> targ)._targ.x
284+
# we also update the tnr, see docstring of correspondences
285+
(<Target> targ)._targ.tnr = pt
286+
287+
sorted_pos[0] = clique_targs
288+
sorted_corresp[0] = clique_ids
289+
290+
return sorted_pos, sorted_corresp, num_points

py_bind/optv/epipolar.pxd

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Created on Thu Apr 18 11:12:36 2018
4+
5+
@author: alexlib
6+
"""
7+
8+
from optv.calibration cimport calibration
9+
from optv.parameters cimport mm_np, volume_par
10+
from optv.vec_utils cimport vec3d
11+
12+
cdef extern from "optv/epi.h":
13+
void epi_mm_2D (double xl, double yl, calibration *cal, mm_np *mmp,
14+
volume_par *vpar, vec3d out);

py_bind/optv/orientation.pxd

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from optv.calibration cimport calibration
2-
from optv.parameters cimport control_par, mm_np
2+
from optv.parameters cimport control_par, mm_np, volume_par
33
from optv.tracking_framebuf cimport target
44
from optv.vec_utils cimport vec3d
5+
from optv.epipolar cimport epi_mm_2D
56

67
cdef extern from "optv/sortgrid.h":
78
target *sortgrid(calibration *cal, control_par *cpar, int nfix, vec3d fix[], int num, int eps, target pix[])
@@ -21,6 +22,10 @@ cdef extern from "optv/orientation.h":
2122

2223
double point_position(vec2d targets[], int num_cams, mm_np *multimed_pars,
2324
calibration* cals[], vec3d res);
25+
double single_cam_point_position(vec2d targets[], int num_cams, mm_np *multimed_pars,
26+
calibration* cals[], vec3d res);
27+
double multi_cam_point_position(vec2d targets[], int num_cams, mm_np *multimed_pars,
28+
calibration* cals[], vec3d res);
2429
int raw_orient(calibration* cal, control_par *cpar, int nfix, vec3d fix[],
2530
target pix[]);
2631
double* orient (calibration* cal_in, control_par *cpar, int nfix,

py_bind/optv/orientation.pyx

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ from libc.stdlib cimport calloc, free
66

77
from optv.tracking_framebuf cimport TargetArray
88
from optv.calibration cimport Calibration
9-
from optv.parameters cimport ControlParams
9+
from optv.parameters cimport ControlParams, VolumeParams
10+
from optv.epipolar cimport epi_mm_2D
11+
1012

1113
def match_detection_to_ref(Calibration cal,
1214
np.ndarray[ndim=2, dtype=pos_t] ref_pts,
@@ -68,6 +70,75 @@ cdef calibration** cal_list2arr(list cals):
6870
return calib
6971

7072
def point_positions(np.ndarray[ndim=3, dtype=pos_t] targets,
73+
ControlParams cparam, cals, VolumeParams vparam):
74+
"""
75+
Calculate the 3D positions of the points given by their 2D projections
76+
using one of the options:
77+
- for a single camera, uses single_cam_point_positions()
78+
- for multiple cameras, uses multi_cam_point_positions()
79+
80+
Arguments:
81+
np.ndarray[ndim=3, dtype=pos_t] targets - (num_targets, num_cams, 2) array,
82+
containing the metric coordinates of each target on the image plane of
83+
each camera. Cameras must be in the same order for all targets.
84+
ControlParams cparam - needed for the parameters of the tank through which
85+
we see the targets.
86+
cals - a sequence of Calibration objects for each of the cameras, in the
87+
camera order of ``targets``.
88+
VolumeParams vparam - an object holding observed volume size parameters, needed
89+
for the single camera case only.
90+
91+
Returns:
92+
res - (n,3) array for n points represented by their targets.
93+
rcm - n-length array, the Ray Convergence Measure for eachpoint for multi camera
94+
option, or zeros for a single camera option
95+
"""
96+
cdef:
97+
np.ndarray[ndim=2, dtype=pos_t] res
98+
np.ndarray[ndim=1, dtype=pos_t] rcm
99+
100+
if len(cals) == 1:
101+
res, rcm = single_cam_point_positions(targets, cparam, cals, vparam)
102+
elif len(cals) > 1:
103+
res, rcm = multi_cam_point_positions(targets,cparam,cals)
104+
else:
105+
raise ValueError("wrong number of cameras in point_positions")
106+
107+
return res, rcm
108+
109+
110+
def single_cam_point_positions(np.ndarray[ndim=3, dtype=pos_t] targets,
111+
ControlParams cparam, cals, VolumeParams vparam):
112+
"""
113+
Calculates the 3D positions of the points from a single camera using
114+
the 2D target positions given in metric coordinates
115+
116+
"""
117+
cdef:
118+
np.ndarray[ndim=2, dtype=pos_t] res
119+
np.ndarray[ndim=1, dtype=pos_t] rcm
120+
np.ndarray[ndim=2, dtype=pos_t] targ
121+
calibration ** calib = cal_list2arr(cals)
122+
int cam, num_cams
123+
124+
# So we can address targets.data directly instead of get_ptr stuff:
125+
targets = np.ascontiguousarray(targets)
126+
127+
num_targets = targets.shape[0]
128+
num_cams = targets.shape[1]
129+
res = np.empty((num_targets, 3))
130+
rcm = np.zeros(num_targets)
131+
132+
for pt in range(num_targets):
133+
targ = targets[pt]
134+
epi_mm_2D (targ[0][0], targ[0][1],
135+
calib[0], cparam._control_par.mm, vparam._volume_par,
136+
<vec3d> np.PyArray_GETPTR2(res, pt, 0));
137+
138+
return res, rcm
139+
140+
141+
def multi_cam_point_positions(np.ndarray[ndim=3, dtype=pos_t] targets,
71142
ControlParams cparam, cals):
72143
"""
73144
Calculate the 3D positions of the points given by their 2D projections.

py_bind/optv/segmentation.pyx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
"""
32
Bindings for image segmentation / target recognition routins in liboptv.
43

0 commit comments

Comments
 (0)