| 494 | |
| 495 | |
| 496 | class SORTSkeleton(SORTBase): |
| 497 | def __init__(self, n_bodyparts, max_age=20, min_hits=3, oks_threshold=0.5): |
| 498 | self.n_bodyparts = n_bodyparts |
| 499 | self.max_age = max_age |
| 500 | self.min_hits = min_hits |
| 501 | self.oks_threshold = oks_threshold |
| 502 | SkeletonTracker.n_trackers = 0 |
| 503 | super().__init__() |
| 504 | |
| 505 | @staticmethod |
| 506 | def weighted_hausdorff(x, y): |
| 507 | # Modified from scipy source code: |
| 508 | # - to restrict its use to 2D |
| 509 | # - to get rid of shuffling (since arrays are only (nbodyparts * 3) element long) |
| 510 | # TODO - factor in keypoint confidence (and weight by # of observations??) |
| 511 | cmax = 0 |
| 512 | for i in range(x.shape[0]): |
| 513 | no_break_occurred = True |
| 514 | cmin = np.inf |
| 515 | for j in range(y.shape[0]): |
| 516 | d = (x[i, 0] - y[j, 0]) ** 2 + (x[i, 1] - y[j, 1]) ** 2 |
| 517 | if d < cmax: |
| 518 | no_break_occurred = False |
| 519 | break |
| 520 | if d < cmin: |
| 521 | cmin = d |
| 522 | if cmin != np.inf and cmin > cmax and no_break_occurred: |
| 523 | cmax = cmin |
| 524 | return np.sqrt(cmax) |
| 525 | |
| 526 | @staticmethod |
| 527 | def object_keypoint_similarity(x, y): |
| 528 | mask = ~np.isnan(x * y).all(axis=1) # Intersection visible keypoints |
| 529 | xx = x[mask] |
| 530 | yy = y[mask] |
| 531 | dist = np.linalg.norm(xx - yy, axis=1) |
| 532 | scale = np.sqrt(np.product(np.ptp(yy, axis=0))) # square root of bounding box area |
| 533 | oks = np.exp(-0.5 * (dist / (0.05 * scale)) ** 2) |
| 534 | return np.mean(oks) |
| 535 | |
| 536 | def calc_pairwise_hausdorff_dist(self, poses, poses_ref): |
| 537 | mat = np.zeros((len(poses), len(poses_ref))) |
| 538 | for i, pose in enumerate(poses): |
| 539 | for j, pose_ref in enumerate(poses_ref): |
| 540 | mat[i, j] = self.weighted_hausdorff(pose, pose_ref) |
| 541 | return mat |
| 542 | |
| 543 | def calc_pairwise_oks(self, poses, poses_ref): |
| 544 | mat = np.zeros((len(poses), len(poses_ref))) |
| 545 | for i, pose in enumerate(poses): |
| 546 | for j, pose_ref in enumerate(poses_ref): |
| 547 | mat[i, j] = self.object_keypoint_similarity(pose, pose_ref) |
| 548 | return mat |
| 549 | |
| 550 | def track(self, poses): |
| 551 | self.n_frames += 1 |
| 552 | |
| 553 | if not len(self.trackers): |
no outgoing calls
no test coverage detected