Spaces:
Running
Running
| import cv2 | |
| import numpy as np | |
| from mmpose.core.post_processing import (get_warp_matrix, | |
| warp_affine_joints) | |
| from mmpose.datasets.builder import PIPELINES | |
| from .post_transforms import (affine_transform, | |
| get_affine_transform) | |
| class TopDownAffineFewShot: | |
| """Affine transform the image to make input. | |
| Required keys:'img', 'joints_3d', 'joints_3d_visible', 'ann_info','scale', | |
| 'rotation' and 'center'. Modified keys:'img', 'joints_3d', and | |
| 'joints_3d_visible'. | |
| Args: | |
| use_udp (bool): To use unbiased data processing. | |
| Paper ref: Huang et al. The Devil is in the Details: Delving into | |
| Unbiased Data Processing for Human Pose Estimation (CVPR 2020). | |
| """ | |
| def __init__(self, use_udp=False): | |
| self.use_udp = use_udp | |
| def __call__(self, results): | |
| image_size = results['ann_info']['image_size'] | |
| img = results['img'] | |
| joints_3d = results['joints_3d'] | |
| joints_3d_visible = results['joints_3d_visible'] | |
| c = results['center'] | |
| s = results['scale'] | |
| r = results['rotation'] | |
| if self.use_udp: | |
| trans = get_warp_matrix(r, c * 2.0, image_size - 1.0, s * 200.0) | |
| img = cv2.warpAffine( | |
| img, | |
| trans, (int(image_size[0]), int(image_size[1])), | |
| flags=cv2.INTER_LINEAR) | |
| joints_3d[:, 0:2] = \ | |
| warp_affine_joints(joints_3d[:, 0:2].copy(), trans) | |
| else: | |
| trans = get_affine_transform(c, s, r, image_size) | |
| img = cv2.warpAffine( | |
| img, | |
| trans, (int(image_size[0]), int(image_size[1])), | |
| flags=cv2.INTER_LINEAR) | |
| for i in range(len(joints_3d)): | |
| if joints_3d_visible[i, 0] > 0.0: | |
| joints_3d[i, | |
| 0:2] = affine_transform(joints_3d[i, 0:2], trans) | |
| results['img'] = img | |
| results['joints_3d'] = joints_3d | |
| results['joints_3d_visible'] = joints_3d_visible | |
| return results | |
| class TopDownGenerateTargetFewShot: | |
| """Generate the target heatmap. | |
| Required keys: 'joints_3d', 'joints_3d_visible', 'ann_info'. | |
| Modified keys: 'target', and 'target_weight'. | |
| Args: | |
| sigma: Sigma of heatmap gaussian for 'MSRA' approach. | |
| kernel: Kernel of heatmap gaussian for 'Megvii' approach. | |
| encoding (str): Approach to generate target heatmaps. | |
| Currently supported approaches: 'MSRA', 'Megvii', 'UDP'. | |
| Default:'MSRA' | |
| unbiased_encoding (bool): Option to use unbiased | |
| encoding methods. | |
| Paper ref: Zhang et al. Distribution-Aware Coordinate | |
| Representation for Human Pose Estimation (CVPR 2020). | |
| keypoint_pose_distance: Keypoint pose distance for UDP. | |
| Paper ref: Huang et al. The Devil is in the Details: Delving into | |
| Unbiased Data Processing for Human Pose Estimation (CVPR 2020). | |
| target_type (str): supported targets: 'GaussianHeatMap', | |
| 'CombinedTarget'. Default:'GaussianHeatMap' | |
| CombinedTarget: The combination of classification target | |
| (response map) and regression target (offset map). | |
| Paper ref: Huang et al. The Devil is in the Details: Delving into | |
| Unbiased Data Processing for Human Pose Estimation (CVPR 2020). | |
| """ | |
| def __init__(self, | |
| sigma=2, | |
| kernel=(11, 11), | |
| valid_radius_factor=0.0546875, | |
| target_type='GaussianHeatMap', | |
| encoding='MSRA', | |
| unbiased_encoding=False): | |
| self.sigma = sigma | |
| self.unbiased_encoding = unbiased_encoding | |
| self.kernel = kernel | |
| self.valid_radius_factor = valid_radius_factor | |
| self.target_type = target_type | |
| self.encoding = encoding | |
| def _msra_generate_target(self, cfg, joints_3d, joints_3d_visible, sigma): | |
| """Generate the target heatmap via "MSRA" approach. | |
| Args: | |
| cfg (dict): data config | |
| joints_3d: np.ndarray ([num_joints, 3]) | |
| joints_3d_visible: np.ndarray ([num_joints, 3]) | |
| sigma: Sigma of heatmap gaussian | |
| Returns: | |
| tuple: A tuple containing targets. | |
| - target: Target heatmaps. | |
| - target_weight: (1: visible, 0: invisible) | |
| """ | |
| num_joints = len(joints_3d) | |
| image_size = cfg['image_size'] | |
| W, H = cfg['heatmap_size'] | |
| joint_weights = cfg['joint_weights'] | |
| use_different_joint_weights = cfg['use_different_joint_weights'] | |
| assert not use_different_joint_weights | |
| target_weight = np.zeros((num_joints, 1), dtype=np.float32) | |
| target = np.zeros((num_joints, H, W), dtype=np.float32) | |
| # 3-sigma rule | |
| tmp_size = sigma * 3 | |
| if self.unbiased_encoding: | |
| for joint_id in range(num_joints): | |
| target_weight[joint_id] = joints_3d_visible[joint_id, 0] | |
| feat_stride = image_size / [W, H] | |
| mu_x = joints_3d[joint_id][0] / feat_stride[0] | |
| mu_y = joints_3d[joint_id][1] / feat_stride[1] | |
| # Check that any part of the gaussian is in-bounds | |
| ul = [mu_x - tmp_size, mu_y - tmp_size] | |
| br = [mu_x + tmp_size + 1, mu_y + tmp_size + 1] | |
| if ul[0] >= W or ul[1] >= H or br[0] < 0 or br[1] < 0: | |
| target_weight[joint_id] = 0 | |
| if target_weight[joint_id] == 0: | |
| continue | |
| x = np.arange(0, W, 1, np.float32) | |
| y = np.arange(0, H, 1, np.float32) | |
| y = y[:, None] | |
| if target_weight[joint_id] > 0.5: | |
| target[joint_id] = np.exp(-((x - mu_x) ** 2 + | |
| (y - mu_y) ** 2) / | |
| (2 * sigma ** 2)) | |
| else: | |
| for joint_id in range(num_joints): | |
| target_weight[joint_id] = joints_3d_visible[joint_id, 0] | |
| feat_stride = image_size / [W, H] | |
| mu_x = int(joints_3d[joint_id][0] / feat_stride[0] + 0.5) | |
| mu_y = int(joints_3d[joint_id][1] / feat_stride[1] + 0.5) | |
| # Check that any part of the gaussian is in-bounds | |
| ul = [int(mu_x - tmp_size), int(mu_y - tmp_size)] | |
| br = [int(mu_x + tmp_size + 1), int(mu_y + tmp_size + 1)] | |
| if ul[0] >= W or ul[1] >= H or br[0] < 0 or br[1] < 0: | |
| target_weight[joint_id] = 0 | |
| if target_weight[joint_id] > 0.5: | |
| size = 2 * tmp_size + 1 | |
| x = np.arange(0, size, 1, np.float32) | |
| y = x[:, None] | |
| x0 = y0 = size // 2 | |
| # The gaussian is not normalized, | |
| # we want the center value to equal 1 | |
| g = np.exp(-((x - x0) ** 2 + (y - y0) ** 2) / (2 * sigma ** 2)) | |
| # Usable gaussian range | |
| g_x = max(0, -ul[0]), min(br[0], W) - ul[0] | |
| g_y = max(0, -ul[1]), min(br[1], H) - ul[1] | |
| # Image range | |
| img_x = max(0, ul[0]), min(br[0], W) | |
| img_y = max(0, ul[1]), min(br[1], H) | |
| target[joint_id][img_y[0]:img_y[1], img_x[0]:img_x[1]] = \ | |
| g[g_y[0]:g_y[1], g_x[0]:g_x[1]] | |
| if use_different_joint_weights: | |
| target_weight = np.multiply(target_weight, joint_weights) | |
| return target, target_weight | |
| def _udp_generate_target(self, cfg, joints_3d, joints_3d_visible, factor, | |
| target_type): | |
| """Generate the target heatmap via 'UDP' approach. Paper ref: Huang et | |
| al. The Devil is in the Details: Delving into Unbiased Data Processing | |
| for Human Pose Estimation (CVPR 2020). | |
| Note: | |
| num keypoints: K | |
| heatmap height: H | |
| heatmap width: W | |
| num target channels: C | |
| C = K if target_type=='GaussianHeatMap' | |
| C = 3*K if target_type=='CombinedTarget' | |
| Args: | |
| cfg (dict): data config | |
| joints_3d (np.ndarray[K, 3]): Annotated keypoints. | |
| joints_3d_visible (np.ndarray[K, 3]): Visibility of keypoints. | |
| factor (float): kernel factor for GaussianHeatMap target or | |
| valid radius factor for CombinedTarget. | |
| target_type (str): 'GaussianHeatMap' or 'CombinedTarget'. | |
| GaussianHeatMap: Heatmap target with gaussian distribution. | |
| CombinedTarget: The combination of classification target | |
| (response map) and regression target (offset map). | |
| Returns: | |
| tuple: A tuple containing targets. | |
| - target (np.ndarray[C, H, W]): Target heatmaps. | |
| - target_weight (np.ndarray[K, 1]): (1: visible, 0: invisible) | |
| """ | |
| num_joints = len(joints_3d) | |
| image_size = cfg['image_size'] | |
| heatmap_size = cfg['heatmap_size'] | |
| joint_weights = cfg['joint_weights'] | |
| use_different_joint_weights = cfg['use_different_joint_weights'] | |
| assert not use_different_joint_weights | |
| target_weight = np.ones((num_joints, 1), dtype=np.float32) | |
| target_weight[:, 0] = joints_3d_visible[:, 0] | |
| assert target_type in ['GaussianHeatMap', 'CombinedTarget'] | |
| if target_type == 'GaussianHeatMap': | |
| target = np.zeros((num_joints, heatmap_size[1], heatmap_size[0]), | |
| dtype=np.float32) | |
| tmp_size = factor * 3 | |
| # prepare for gaussian | |
| size = 2 * tmp_size + 1 | |
| x = np.arange(0, size, 1, np.float32) | |
| y = x[:, None] | |
| for joint_id in range(num_joints): | |
| feat_stride = (image_size - 1.0) / (heatmap_size - 1.0) | |
| mu_x = int(joints_3d[joint_id][0] / feat_stride[0] + 0.5) | |
| mu_y = int(joints_3d[joint_id][1] / feat_stride[1] + 0.5) | |
| # Check that any part of the gaussian is in-bounds | |
| ul = [int(mu_x - tmp_size), int(mu_y - tmp_size)] | |
| br = [int(mu_x + tmp_size + 1), int(mu_y + tmp_size + 1)] | |
| if ul[0] >= heatmap_size[0] or ul[1] >= heatmap_size[1] \ | |
| or br[0] < 0 or br[1] < 0: | |
| # If not, just return the image as is | |
| target_weight[joint_id] = 0 | |
| continue | |
| # # Generate gaussian | |
| mu_x_ac = joints_3d[joint_id][0] / feat_stride[0] | |
| mu_y_ac = joints_3d[joint_id][1] / feat_stride[1] | |
| x0 = y0 = size // 2 | |
| x0 += mu_x_ac - mu_x | |
| y0 += mu_y_ac - mu_y | |
| g = np.exp(-((x - x0) ** 2 + (y - y0) ** 2) / (2 * factor ** 2)) | |
| # Usable gaussian range | |
| g_x = max(0, -ul[0]), min(br[0], heatmap_size[0]) - ul[0] | |
| g_y = max(0, -ul[1]), min(br[1], heatmap_size[1]) - ul[1] | |
| # Image range | |
| img_x = max(0, ul[0]), min(br[0], heatmap_size[0]) | |
| img_y = max(0, ul[1]), min(br[1], heatmap_size[1]) | |
| v = target_weight[joint_id] | |
| if v > 0.5: | |
| target[joint_id][img_y[0]:img_y[1], img_x[0]:img_x[1]] = \ | |
| g[g_y[0]:g_y[1], g_x[0]:g_x[1]] | |
| elif target_type == 'CombinedTarget': | |
| target = np.zeros( | |
| (num_joints, 3, heatmap_size[1] * heatmap_size[0]), | |
| dtype=np.float32) | |
| feat_width = heatmap_size[0] | |
| feat_height = heatmap_size[1] | |
| feat_x_int = np.arange(0, feat_width) | |
| feat_y_int = np.arange(0, feat_height) | |
| feat_x_int, feat_y_int = np.meshgrid(feat_x_int, feat_y_int) | |
| feat_x_int = feat_x_int.flatten() | |
| feat_y_int = feat_y_int.flatten() | |
| # Calculate the radius of the positive area in classification | |
| # heatmap. | |
| valid_radius = factor * heatmap_size[1] | |
| feat_stride = (image_size - 1.0) / (heatmap_size - 1.0) | |
| for joint_id in range(num_joints): | |
| mu_x = joints_3d[joint_id][0] / feat_stride[0] | |
| mu_y = joints_3d[joint_id][1] / feat_stride[1] | |
| x_offset = (mu_x - feat_x_int) / valid_radius | |
| y_offset = (mu_y - feat_y_int) / valid_radius | |
| dis = x_offset ** 2 + y_offset ** 2 | |
| keep_pos = np.where(dis <= 1)[0] | |
| v = target_weight[joint_id] | |
| if v > 0.5: | |
| target[joint_id, 0, keep_pos] = 1 | |
| target[joint_id, 1, keep_pos] = x_offset[keep_pos] | |
| target[joint_id, 2, keep_pos] = y_offset[keep_pos] | |
| target = target.reshape(num_joints * 3, heatmap_size[1], | |
| heatmap_size[0]) | |
| if use_different_joint_weights: | |
| target_weight = np.multiply(target_weight, joint_weights) | |
| return target, target_weight | |
| def __call__(self, results): | |
| """Generate the target heatmap.""" | |
| joints_3d = results['joints_3d'] | |
| joints_3d_visible = results['joints_3d_visible'] | |
| assert self.encoding in ['MSRA', 'UDP'] | |
| if self.encoding == 'MSRA': | |
| if isinstance(self.sigma, list): | |
| num_sigmas = len(self.sigma) | |
| cfg = results['ann_info'] | |
| num_joints = len(joints_3d) | |
| heatmap_size = cfg['heatmap_size'] | |
| target = np.empty( | |
| (0, num_joints, heatmap_size[1], heatmap_size[0]), | |
| dtype=np.float32) | |
| target_weight = np.empty((0, num_joints, 1), dtype=np.float32) | |
| for i in range(num_sigmas): | |
| target_i, target_weight_i = self._msra_generate_target( | |
| cfg, joints_3d, joints_3d_visible, self.sigma[i]) | |
| target = np.concatenate([target, target_i[None]], axis=0) | |
| target_weight = np.concatenate( | |
| [target_weight, target_weight_i[None]], axis=0) | |
| else: | |
| target, target_weight = self._msra_generate_target( | |
| results['ann_info'], joints_3d, joints_3d_visible, | |
| self.sigma) | |
| elif self.encoding == 'UDP': | |
| if self.target_type == 'CombinedTarget': | |
| factors = self.valid_radius_factor | |
| channel_factor = 3 | |
| elif self.target_type == 'GaussianHeatMap': | |
| factors = self.sigma | |
| channel_factor = 1 | |
| if isinstance(factors, list): | |
| num_factors = len(factors) | |
| cfg = results['ann_info'] | |
| num_joints = len(joints_3d) | |
| W, H = cfg['heatmap_size'] | |
| target = np.empty((0, channel_factor * num_joints, H, W), | |
| dtype=np.float32) | |
| target_weight = np.empty((0, num_joints, 1), dtype=np.float32) | |
| for i in range(num_factors): | |
| target_i, target_weight_i = self._udp_generate_target( | |
| cfg, joints_3d, joints_3d_visible, factors[i], | |
| self.target_type) | |
| target = np.concatenate([target, target_i[None]], axis=0) | |
| target_weight = np.concatenate( | |
| [target_weight, target_weight_i[None]], axis=0) | |
| else: | |
| target, target_weight = self._udp_generate_target( | |
| results['ann_info'], joints_3d, joints_3d_visible, factors, | |
| self.target_type) | |
| else: | |
| raise ValueError( | |
| f'Encoding approach {self.encoding} is not supported!') | |
| results['target'] = target | |
| results['target_weight'] = target_weight | |
| return results | |