| |
| import torch |
| from torch import Tensor, nn |
| from torch.autograd import Function |
| from torch.autograd.function import once_differentiable |
| from torch.nn.modules.utils import _pair |
|
|
| from ..utils import ext_loader |
|
|
| ext_module = ext_loader.load_ext( |
| '_ext', ['correlation_forward', 'correlation_backward']) |
|
|
|
|
| class CorrelationFunction(Function): |
|
|
| @staticmethod |
| def forward(ctx, |
| input1, |
| input2, |
| kernel_size=1, |
| max_displacement=1, |
| stride=1, |
| padding=1, |
| dilation=1, |
| dilation_patch=1): |
|
|
| ctx.save_for_backward(input1, input2) |
|
|
| kH, kW = ctx.kernel_size = _pair(kernel_size) |
| patch_size = max_displacement * 2 + 1 |
| ctx.patch_size = patch_size |
| dH, dW = ctx.stride = _pair(stride) |
| padH, padW = ctx.padding = _pair(padding) |
| dilationH, dilationW = ctx.dilation = _pair(dilation) |
| dilation_patchH, dilation_patchW = ctx.dilation_patch = _pair( |
| dilation_patch) |
|
|
| output_size = CorrelationFunction._output_size(ctx, input1) |
|
|
| output = input1.new_zeros(output_size) |
|
|
| ext_module.correlation_forward( |
| input1, |
| input2, |
| output, |
| kH=kH, |
| kW=kW, |
| patchH=patch_size, |
| patchW=patch_size, |
| padH=padH, |
| padW=padW, |
| dilationH=dilationH, |
| dilationW=dilationW, |
| dilation_patchH=dilation_patchH, |
| dilation_patchW=dilation_patchW, |
| dH=dH, |
| dW=dW) |
|
|
| return output |
|
|
| @staticmethod |
| @once_differentiable |
| def backward(ctx, grad_output): |
| input1, input2 = ctx.saved_tensors |
|
|
| kH, kW = ctx.kernel_size |
| patch_size = ctx.patch_size |
| padH, padW = ctx.padding |
| dilationH, dilationW = ctx.dilation |
| dilation_patchH, dilation_patchW = ctx.dilation_patch |
| dH, dW = ctx.stride |
| grad_input1 = torch.zeros_like(input1) |
| grad_input2 = torch.zeros_like(input2) |
|
|
| ext_module.correlation_backward( |
| grad_output, |
| input1, |
| input2, |
| grad_input1, |
| grad_input2, |
| kH=kH, |
| kW=kW, |
| patchH=patch_size, |
| patchW=patch_size, |
| padH=padH, |
| padW=padW, |
| dilationH=dilationH, |
| dilationW=dilationW, |
| dilation_patchH=dilation_patchH, |
| dilation_patchW=dilation_patchW, |
| dH=dH, |
| dW=dW) |
| return grad_input1, grad_input2, None, None, None, None, None, None |
|
|
| @staticmethod |
| def _output_size(ctx, input1): |
| iH, iW = input1.size(2), input1.size(3) |
| batch_size = input1.size(0) |
| kH, kW = ctx.kernel_size |
| patch_size = ctx.patch_size |
| dH, dW = ctx.stride |
| padH, padW = ctx.padding |
| dilationH, dilationW = ctx.dilation |
| dilatedKH = (kH - 1) * dilationH + 1 |
| dilatedKW = (kW - 1) * dilationW + 1 |
|
|
| oH = int((iH + 2 * padH - dilatedKH) / dH + 1) |
| oW = int((iW + 2 * padW - dilatedKW) / dW + 1) |
|
|
| output_size = (batch_size, patch_size, patch_size, oH, oW) |
| return output_size |
|
|
|
|
| class Correlation(nn.Module): |
| r"""Correlation operator |
| |
| This correlation operator works for optical flow correlation computation. |
| |
| There are two batched tensors with shape :math:`(N, C, H, W)`, |
| and the correlation output's shape is :math:`(N, max\_displacement \times |
| 2 + 1, max\_displacement * 2 + 1, H_{out}, W_{out})` |
| |
| where |
| |
| .. math:: |
| H_{out} = \left\lfloor\frac{H_{in} + 2 \times padding - |
| dilation \times (kernel\_size - 1) - 1} |
| {stride} + 1\right\rfloor |
| |
| .. math:: |
| W_{out} = \left\lfloor\frac{W_{in} + 2 \times padding - dilation |
| \times (kernel\_size - 1) - 1} |
| {stride} + 1\right\rfloor |
| |
| the correlation item :math:`(N_i, dy, dx)` is formed by taking the sliding |
| window convolution between input1 and shifted input2, |
| |
| .. math:: |
| Corr(N_i, dx, dy) = |
| \sum_{c=0}^{C-1} |
| input1(N_i, c) \star |
| \mathcal{S}(input2(N_i, c), dy, dx) |
| |
| where :math:`\star` is the valid 2d sliding window convolution operator, |
| and :math:`\mathcal{S}` means shifting the input features (auto-complete |
| zero marginal), and :math:`dx, dy` are shifting distance, :math:`dx, dy \in |
| [-max\_displacement \times dilation\_patch, max\_displacement \times |
| dilation\_patch]`. |
| |
| Args: |
| kernel_size (int): The size of sliding window i.e. local neighborhood |
| representing the center points and involved in correlation |
| computation. Defaults to 1. |
| max_displacement (int): The radius for computing correlation volume, |
| but the actual working space can be dilated by dilation_patch. |
| Defaults to 1. |
| stride (int): The stride of the sliding blocks in the input spatial |
| dimensions. Defaults to 1. |
| padding (int): Zero padding added to all four sides of the input1. |
| Defaults to 0. |
| dilation (int): The spacing of local neighborhood that will involved |
| in correlation. Defaults to 1. |
| dilation_patch (int): The spacing between position need to compute |
| correlation. Defaults to 1. |
| """ |
|
|
| def __init__(self, |
| kernel_size: int = 1, |
| max_displacement: int = 1, |
| stride: int = 1, |
| padding: int = 0, |
| dilation: int = 1, |
| dilation_patch: int = 1) -> None: |
| super().__init__() |
| self.kernel_size = kernel_size |
| self.max_displacement = max_displacement |
| self.stride = stride |
| self.padding = padding |
| self.dilation = dilation |
| self.dilation_patch = dilation_patch |
|
|
| def forward(self, input1: Tensor, input2: Tensor) -> Tensor: |
| return CorrelationFunction.apply(input1, input2, self.kernel_size, |
| self.max_displacement, self.stride, |
| self.padding, self.dilation, |
| self.dilation_patch) |
|
|
| def __repr__(self) -> str: |
| s = self.__class__.__name__ |
| s += f'(kernel_size={self.kernel_size}, ' |
| s += f'max_displacement={self.max_displacement}, ' |
| s += f'stride={self.stride}, ' |
| s += f'padding={self.padding}, ' |
| s += f'dilation={self.dilation}, ' |
| s += f'dilation_patch={self.dilation_patch})' |
| return s |
|
|