EasyMocap/myeasymocap/operations/triangulate.py
2023-06-19 16:39:27 +08:00

151 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import numpy as np
from itertools import combinations
from easymocap.mytools.camera_utils import Undistort
from easymocap.mytools.triangulator import iterative_triangulate
def batch_triangulate(keypoints_, Pall, min_view=2):
""" triangulate the keypoints of whole body
Args:
keypoints_ (nViews, nJoints, 3): 2D detections
Pall (nViews, 3, 4): projection matrix of each view
min_view (int, optional): min view for visible points. Defaults to 2.
Returns:
keypoints3d: (nJoints, 4)
"""
# keypoints: (nViews, nJoints, 3)
# Pall: (nViews, 3, 4)
# A: (nJoints, nViewsx2, 4), x: (nJoints, 4, 1); b: (nJoints, nViewsx2, 1)
v = (keypoints_[:, :, -1]>0).sum(axis=0)
valid_joint = np.where(v >= min_view)[0]
keypoints = keypoints_[:, valid_joint]
conf3d = keypoints[:, :, -1].sum(axis=0)/v[valid_joint]
# P2: P矩阵的最后一行(1, nViews, 1, 4)
P0 = Pall[None, :, 0, :]
P1 = Pall[None, :, 1, :]
P2 = Pall[None, :, 2, :]
# uP2: x坐标乘上P2: (nJoints, nViews, 1, 4)
uP2 = keypoints[:, :, 0].T[:, :, None] * P2
vP2 = keypoints[:, :, 1].T[:, :, None] * P2
conf = keypoints[:, :, 2].T[:, :, None]
Au = conf * (uP2 - P0)
Av = conf * (vP2 - P1)
A = np.hstack([Au, Av])
u, s, v = np.linalg.svd(A)
X = v[:, -1, :]
X = X / X[:, 3:]
# out: (nJoints, 4)
result = np.zeros((keypoints_.shape[1], 4))
result[valid_joint, :3] = X[:, :3]
result[valid_joint, 3] = conf3d #* (conf[..., 0].sum(axis=-1)>min_view)
return result
def project_wo_dist(keypoints, RT, einsum='vab,kb->vka'):
homo = np.concatenate([keypoints[..., :3], np.ones_like(keypoints[..., :1])], axis=-1)
kpts2d = np.einsum(einsum, RT, homo)
depth = kpts2d[..., 2]
kpts2d[..., :2] /= kpts2d[..., 2:]
return kpts2d, depth
class SimpleTriangulate:
def __init__(self, mode):
self.mode = mode
@staticmethod
def undistort(points, cameras):
nViews = len(points)
pelvis_undis = []
for nv in range(nViews):
camera = {key:cameras[key][nv] for key in ['R', 'T', 'K', 'dist']}
if points[nv].shape[0] > 0:
pelvis = Undistort.points(points[nv], camera['K'], camera['dist'])
else:
pelvis = points[nv].copy()
pelvis_undis.append(pelvis)
return pelvis_undis
def __call__(self, keypoints, cameras):
'''
keypoints: [nViews, nJoints, 3]
output:
keypoints3d: (nJoints, 4)
'''
keypoints = self.undistort(keypoints, cameras)
keypoints = np.stack(keypoints)
if self.mode == 'naive':
keypoints3d = batch_triangulate(keypoints, cameras['P'])
else:
keypoints3d, k2d = iterative_triangulate(keypoints, cameras['P'], dist_max=25)
return {'keypoints3d': keypoints3d}
class RobustTriangulate(SimpleTriangulate):
def __init__(self, mode, cfg):
super().__init__(mode)
self.cache_view = {}
self.cfg = cfg
def try_to_triangulate_and_project(self, index, keypoints, cameras):
# 选择最好的3个视角
P = cameras['P'][index]
kpts = keypoints[index][:, None]
k3d = batch_triangulate(kpts, P)
k2d, depth = project_wo_dist(k3d, P)
dist_repro = np.linalg.norm(k2d[..., :2] - kpts[..., :2], axis=-1).mean(axis=-1)
return k3d, dist_repro
def robust_triangulate(self, keypoints, cameras):
# 选择最好的3个视角
# TODO: 移除不合理的视角
nViews = keypoints.shape[0]
if nViews not in self.cache_view:
views = list(range(nViews))
combs = list(combinations(views, self.cfg.triangulate.init_views))
combs = np.array(combs)
self.cache_view[nViews] = combs
combs = self.cache_view[nViews]
keypoints_comb = keypoints[combs]
conf_sum = keypoints_comb[..., 2].mean(axis=1) * (keypoints_comb[..., 2]>0.05).all(axis=1)
comb_sort_id = (-conf_sum).argsort()
flag_find_init = False
for comb_id in comb_sort_id:
if conf_sum[comb_id] < 0.1:
break
comb = combs[comb_id]
k3d, dist_repro = self.try_to_triangulate_and_project(comb, keypoints, cameras)
if (dist_repro < self.cfg.triangulate.repro_init).all():
flag_find_init = True
init = comb.tolist()
break
if not flag_find_init:
print('Cannot find good initialize pair')
import ipdb; ipdb.set_trace()
view_idxs = (-keypoints[:, -1]).argsort()
for view_idx in view_idxs:
if view_idx in init:
continue
if keypoints[view_idx, 2] < 0.1:
continue
k3d, dist_repro = self.try_to_triangulate_and_project(init+[view_idx], keypoints, cameras)
if (dist_repro < self.cfg.triangulate.repro_2d).all():
# print('Add view {}'.format(view_idx))
init.append(view_idx)
return k3d, init
def __call__(self, keypoints, cameras):
"""
keypoints: (nViews, nJoints, 3)
cameras: (nViews, 3, 4)
"""
nViews, nJoints, _ = keypoints.shape
keypoints_undis = np.stack(self.undistort(keypoints, cameras))
# for each points, find good initial pairs
points_all = np.zeros((nJoints, 4))
keypoints_copy = keypoints.copy()
for nj in range(nJoints):
point, select_views = self.robust_triangulate(keypoints_undis[:, nj], cameras)
points_all[nj:nj+1] = point
keypoints_copy[select_views, nj, 2] += 10
keypoints_copy[:, nj, 2] = np.clip(keypoints_copy[:, nj, 2]-10, 0, 1)
return {'keypoints3d': points_all, 'keypoints_select': keypoints_copy}