def apply_transformation(self, theta, points):
    if (theta.dim() == 2):
        theta = theta.unsqueeze(2).unsqueeze(3)
    batch_size = theta.size()[0]
    Q_X = theta[:, :self.N, :, :].squeeze(3)
    Q_Y = theta[:, self.N:, :, :].squeeze(3)
    Q_X = (Q_X + self.P_X_base.expand_as(Q_X))
    Q_Y = (Q_Y + self.P_Y_base.expand_as(Q_Y))
    points_b = points.size()[0]
    points_h = points.size()[1]
    points_w = points.size()[2]
    P_X = self.P_X.expand((1, points_h, points_w, 1, self.N))
    P_Y = self.P_Y.expand((1, points_h, points_w, 1, self.N))
    W_X = torch.bmm(self.Li[:, :self.N, :self.N].expand((batch_size, self.N, self.N)), Q_X)
    W_Y = torch.bmm(self.Li[:, :self.N, :self.N].expand((batch_size, self.N, self.N)), Q_Y)
    W_X = W_X.unsqueeze(3).unsqueeze(4).transpose(1, 4).repeat(1, points_h, points_w, 1, 1)
    W_Y = W_Y.unsqueeze(3).unsqueeze(4).transpose(1, 4).repeat(1, points_h, points_w, 1, 1)
    A_X = torch.bmm(self.Li[:, self.N:, :self.N].expand((batch_size, 3, self.N)), Q_X)
    A_Y = torch.bmm(self.Li[:, self.N:, :self.N].expand((batch_size, 3, self.N)), Q_Y)
    A_X = A_X.unsqueeze(3).unsqueeze(4).transpose(1, 4).repeat(1, points_h, points_w, 1, 1)
    A_Y = A_Y.unsqueeze(3).unsqueeze(4).transpose(1, 4).repeat(1, points_h, points_w, 1, 1)
    points_X_for_summation = points[:, :, :, 0].unsqueeze(3).unsqueeze(4).expand((points[:, :, :, 0].size() + (1, self.N)))
    points_Y_for_summation = points[:, :, :, 1].unsqueeze(3).unsqueeze(4).expand((points[:, :, :, 1].size() + (1, self.N)))
    if (points_b == 1):
        delta_X = (points_X_for_summation - P_X)
        delta_Y = (points_Y_for_summation - P_Y)
    else:
        delta_X = (points_X_for_summation - P_X.expand_as(points_X_for_summation))
        delta_Y = (points_Y_for_summation - P_Y.expand_as(points_Y_for_summation))
    dist_squared = (torch.pow(delta_X, 2) + torch.pow(delta_Y, 2))
    dist_squared[(dist_squared == 0)] = 1
    U = torch.mul(dist_squared, torch.log(dist_squared))
    points_X_batch = points[:, :, :, 0].unsqueeze(3)
    points_Y_batch = points[:, :, :, 1].unsqueeze(3)
    if (points_b == 1):
        points_X_batch = points_X_batch.expand(((batch_size,) + points_X_batch.size()[1:]))
        points_Y_batch = points_Y_batch.expand(((batch_size,) + points_Y_batch.size()[1:]))
    points_X_prime = (((A_X[:, :, :, :, 0] + torch.mul(A_X[:, :, :, :, 1], points_X_batch)) + torch.mul(A_X[:, :, :, :, 2], points_Y_batch)) + torch.sum(torch.mul(W_X, U.expand_as(W_X)), 4))
    points_Y_prime = (((A_Y[:, :, :, :, 0] + torch.mul(A_Y[:, :, :, :, 1], points_X_batch)) + torch.mul(A_Y[:, :, :, :, 2], points_Y_batch)) + torch.sum(torch.mul(W_Y, U.expand_as(W_Y)), 4))
    return torch.cat((points_X_prime, points_Y_prime), 3)
def apply_transformation(self, theta, points):
    if (theta.dim() == 2):
        theta = theta.unsqueeze(2).unsqueeze(3)
    batch_size = theta.size()[0]
    Q_X = theta[:, :self.N, :, :].squeeze(3)
    Q_Y = theta[:, self.N:, :, :].squeeze(3)
    Q_X = (Q_X + self.P_X_base.expand_as(Q_X))
    Q_Y = (Q_Y + self.P_Y_base.expand_as(Q_Y))
    points_b = points.size()[0]
    points_h = points.size()[1]
    points_w = points.size()[2]
    P_X = self.P_X.expand((1, points_h, points_w, 1, self.N))
    P_Y = self.P_Y.expand((1, points_h, points_w, 1, self.N))
    W_X = torch.bmm(self.Li[:, :self.N, :self.N].expand((batch_size, self.N, self.N)), Q_X)
    W_Y = torch.bmm(self.Li[:, :self.N, :self.N].expand((batch_size, self.N, self.N)), Q_Y)
    W_X = W_X.unsqueeze(3).unsqueeze(4).transpose(1, 4).repeat(1, points_h, points_w, 1, 1)
    W_Y = W_Y.unsqueeze(3).unsqueeze(4).transpose(1, 4).repeat(1, points_h, points_w, 1, 1)
    A_X = torch.bmm(self.Li[:, self.N:, :self.N].expand((batch_size, 3, self.N)), Q_X)
    A_Y = torch.bmm(self.Li[:, self.N:, :self.N].expand((batch_size, 3, self.N)), Q_Y)
    A_X = A_X.unsqueeze(3).unsqueeze(4).transpose(1, 4).repeat(1, points_h, points_w, 1, 1)
    A_Y = A_Y.unsqueeze(3).unsqueeze(4).transpose(1, 4).repeat(1, points_h, points_w, 1, 1)
    points_X_for_summation = points[:, :, :, 0].unsqueeze(3).unsqueeze(4).expand((points[:, :, :, 0].size() + (1, self.N)))
    points_Y_for_summation = points[:, :, :, 1].unsqueeze(3).unsqueeze(4).expand((points[:, :, :, 1].size() + (1, self.N)))
    if (points_b == 1):
        delta_X = (points_X_for_summation - P_X)
        delta_Y = (points_Y_for_summation - P_Y)
    else:
        delta_X = (points_X_for_summation - P_X.expand_as(points_X_for_summation))
        delta_Y = (points_Y_for_summation - P_Y.expand_as(points_Y_for_summation))
    dist_squared = (torch.pow(delta_X, 2) + torch.pow(delta_Y, 2))
    dist_squared[(dist_squared == 0)] = 1
    U = torch.mul(dist_squared, torch.log(dist_squared))
    points_X_batch = points[:, :, :, 0].unsqueeze(3)
    points_Y_batch = points[:, :, :, 1].unsqueeze(3)
    if (points_b == 1):
        points_X_batch = points_X_batch.expand(((batch_size,) + points_X_batch.size()[1:]))
        points_Y_batch = points_Y_batch.expand(((batch_size,) + points_Y_batch.size()[1:]))
    points_X_prime = (((A_X[:, :, :, :, 0] + torch.mul(A_X[:, :, :, :, 1], points_X_batch)) + torch.mul(A_X[:, :, :, :, 2], points_Y_batch)) + torch.sum(torch.mul(W_X, U.expand_as(W_X)), 4))
    points_Y_prime = (((A_Y[:, :, :, :, 0] + torch.mul(A_Y[:, :, :, :, 1], points_X_batch)) + torch.mul(A_Y[:, :, :, :, 2], points_Y_batch)) + torch.sum(torch.mul(W_Y, U.expand_as(W_Y)), 4))
    return torch.cat((points_X_prime, points_Y_prime), 3)