| import torch
|
| import torch.nn as nn
|
| from torch.autograd import Variable
|
|
|
|
|
| def get_channel_sum(input):
|
| temp = torch.sum(input, dim=3)
|
| output = torch.sum(temp, dim=2)
|
| return output
|
|
|
|
|
| def expand_two_dimensions_at_end(input, dim1, dim2):
|
| input = input.unsqueeze(-1).unsqueeze(-1)
|
| input = input.expand(-1, -1, dim1, dim2)
|
| return input
|
|
|
|
|
| class TestTimePCA(nn.Module):
|
| def __init__(self):
|
| super(TestTimePCA, self).__init__()
|
|
|
| def _make_grid(self, h, w):
|
| yy, xx = torch.meshgrid(
|
| torch.arange(h).float() / (h - 1) * 2 - 1,
|
| torch.arange(w).float() / (w - 1) * 2 - 1)
|
| return yy, xx
|
|
|
| def weighted_mean(self, heatmap):
|
| batch, npoints, h, w = heatmap.shape
|
|
|
| yy, xx = self._make_grid(h, w)
|
| yy = yy.view(1, 1, h, w).to(heatmap)
|
| xx = xx.view(1, 1, h, w).to(heatmap)
|
|
|
| yy_coord = (yy * heatmap).sum([2, 3])
|
| xx_coord = (xx * heatmap).sum([2, 3])
|
| coords = torch.stack([xx_coord, yy_coord], dim=-1)
|
| return coords
|
|
|
| def unbiased_weighted_covariance(self, htp, means, num_dim_image=2, EPSILON=1e-5):
|
| batch_size, num_points, height, width = htp.shape
|
|
|
| yv, xv = self._make_grid(height, width)
|
| xv = Variable(xv)
|
| yv = Variable(yv)
|
|
|
| if htp.is_cuda:
|
| xv = xv.cuda()
|
| yv = yv.cuda()
|
|
|
| xmean = means[:, :, 0]
|
| xv_minus_mean = xv.expand(batch_size, num_points, -1, -1) - expand_two_dimensions_at_end(xmean, height,
|
| width)
|
| ymean = means[:, :, 1]
|
| yv_minus_mean = yv.expand(batch_size, num_points, -1, -1) - expand_two_dimensions_at_end(ymean, height,
|
| width)
|
| wt_xv_minus_mean = xv_minus_mean
|
| wt_yv_minus_mean = yv_minus_mean
|
|
|
| wt_xv_minus_mean = wt_xv_minus_mean.view(batch_size * num_points, height * width)
|
| wt_xv_minus_mean = wt_xv_minus_mean.view(batch_size * num_points, 1, height * width)
|
| wt_yv_minus_mean = wt_yv_minus_mean.view(batch_size * num_points, height * width)
|
| wt_yv_minus_mean = wt_yv_minus_mean.view(batch_size * num_points, 1, height * width)
|
| vec_concat = torch.cat((wt_xv_minus_mean, wt_yv_minus_mean), 1)
|
|
|
| htp_vec = htp.view(batch_size * num_points, 1, height * width)
|
| htp_vec = htp_vec.expand(-1, 2, -1)
|
|
|
| covariance = torch.bmm(htp_vec * vec_concat, vec_concat.transpose(1, 2))
|
| covariance = covariance.view(batch_size, num_points, num_dim_image, num_dim_image)
|
|
|
| V_1 = htp.sum([2, 3]) + EPSILON
|
| V_2 = torch.pow(htp, 2).sum([2, 3]) + EPSILON
|
|
|
| denominator = V_1 - (V_2 / V_1)
|
| covariance = covariance / expand_two_dimensions_at_end(denominator, num_dim_image, num_dim_image)
|
|
|
| return covariance
|
|
|
| def forward(self, heatmap, groudtruth):
|
|
|
| batch, npoints, h, w = heatmap.shape
|
|
|
| heatmap_sum = torch.clamp(heatmap.sum([2, 3]), min=1e-6)
|
| heatmap = heatmap / heatmap_sum.view(batch, npoints, 1, 1)
|
|
|
|
|
| means = self.weighted_mean(heatmap)
|
|
|
|
|
| covars = self.unbiased_weighted_covariance(heatmap, means)
|
|
|
|
|
| covars = covars.view(batch * npoints, 2, 2).cpu()
|
| evalues, evectors = covars.symeig(eigenvectors=True)
|
| evalues = evalues.view(batch, npoints, 2)
|
| evectors = evectors.view(batch, npoints, 2, 2)
|
| means = means.cpu()
|
|
|
| results = [dict() for _ in range(batch)]
|
| for i in range(batch):
|
| results[i]['pred'] = means[i].numpy().tolist()
|
| results[i]['gt'] = groudtruth[i].cpu().numpy().tolist()
|
| results[i]['evalues'] = evalues[i].numpy().tolist()
|
| results[i]['evectors'] = evectors[i].numpy().tolist()
|
|
|
| return results
|
|
|