def fundamental_3x3_from_projections(p_left_3x4: np.array, p_right__3x4: np.array) -> np.array:
# The following is based on OpenCv-contrib's c++ implementation.
# see https://github.com/opencv/opencv_contrib/blob/master/modules/sfm/src/fundamental.cpp#L109
# see https://sourishghosh.com/2016/fundamental-matrix-from-camera-matrices/
# see https://answers.opencv.org/question/131017/how-do-i-compute-the-fundamental-matrix-from-2-projection-matrices/
f_3x3 = np.zeros((3, 3))
p1, p2 = p_left_3x4, p_right__3x4
x = np.empty((3, 2, 4), dtype=np.float)
x[0, :, :] = np.vstack([p1[1, :], p1[2, :]])
x[1, :, :] = np.vstack([p1[2, :], p1[0, :]])
x[2, :, :] = np.vstack([p1[0, :], p1[1, :]])
y = np.empty((3, 2, 4), dtype=np.float)
y[0, :, :] = np.vstack([p2[1, :], p2[2, :]])
y[1, :, :] = np.vstack([p2[2, :], p2[0, :]])
y[2, :, :] = np.vstack([p2[0, :], p2[1, :]])
for i in range(3):
for j in range(3):
xy = np.vstack([x[j, :], y[i, :]])
f_3x3[i, j] = np.linalg.det(xy)
return f_3x3
```
I managed to create a vectorized version of this
def fundamental_3x3_from_projections_vectorized(p_left_nx3x4: np.array, p_right_mx3x4: np.array) -> np.array:
# The following is based on OpenCv-contrib's c++ implementation.
# see https://github.com/opencv/opencv_contrib/blob/master/modules/sfm/src/fundamental.cpp#L109
# see https://sourishghosh.com/2016/fundamental-matrix-from-camera-matrices/
# see https://answers.opencv.org/question/131017/how-do-i-compute-the-fundamental-matrix-from-2-projection-matrices/
assert p_left_nx3x4.shape[1:] == p_right_mx3x4.shape[1:] and p_left_nx3x4.shape[1:] == (3, 4)
n = p_left_nx3x4.shape[0]
m = p_right_mx3x4.shape[0]
f_nxmx3x3 = np.empty((n, m, 3, 3), np.float)
p1_nx3x4, p2_mx3x4 = p_left_nx3x4, p_right_mx3x4
x_nx3x2x4 = np.empty((n, 3, 2, 4), dtype=np.float)
x_nx3x2x4[:, 0, :, :] = np.stack([p1_nx3x4[:, 1, :], p1_nx3x4[:, 2, :]], axis=1) # (n, 2, 4)
x_nx3x2x4[:, 1, :, :] = np.stack([p1_nx3x4[:, 2, :], p1_nx3x4[:, 0, :]], axis=1) # (n, 2, 4)
x_nx3x2x4[:, 2, :, :] = np.stack([p1_nx3x4[:, 0, :], p1_nx3x4[:, 1, :]], axis=1) # (n, 2, 4)
y_mx3x2x4 = np.empty((m, 3, 2, 4), dtype=np.float)
y_mx3x2x4[:, 0, :, :] = np.stack([p2_mx3x4[:, 1, :], p2_mx3x4[:, 2, :]], axis=1) # (m, 2, 4)
y_mx3x2x4[:, 1, :, :] = np.stack([p2_mx3x4[:, 2, :], p2_mx3x4[:, 0, :]], axis=1) # (m, 2, 4)
y_mx3x2x4[:, 2, :, :] = np.stack([p2_mx3x4[:, 0, :], p2_mx3x4[:, 1, :]], axis=1) # (m, 2, 4)
x_nx1x3x2x4 = x_nx3x2x4[:, np.newaxis, :, :, :]
y_1xmx3x2x4 = y_mx3x2x4[np.newaxis, :, :, :, :]
xtile_nxmx3x2x4 = np.broadcast_to(x_nx1x3x2x4, (n, m, 3, 2, 4))
ytile_nxmx3x2x4 = np.broadcast_to(y_1xmx3x2x4, (n, m, 3, 2, 4))
for i in range(3):
for j in range(3):
x_mesh_nxmx2x4 = xtile_nxmx3x2x4[:, :, j, :, :]
y_mesh_nxmx2x4 = ytile_nxmx3x2x4[:, :, i, :, :]
# TODO this concatenate can be done manually and avoid re-allocation 9 times
xy_nxmx4x4 = np.concatenate([x_mesh_nxmx2x4, y_mesh_nxmx2x4], axis=2)
xy_nmx4x4 = xy_nxmx4x4.reshape((n*m, 4, 4))
det_nm = np.linalg.det(xy_nmx4x4)
det_nxm = np.reshape(det_nm, (n, m))
f_nxmx3x3[:, :, i, j] = det_nxm
return f_nxmx3x3
It runs much faster.
I still would like to be able to calculate this incrementally.