Spaces:
Running
on
T4
Running
on
T4
File size: 4,330 Bytes
4dc3e99 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
import torch
import torchvision
from deepinv.physics.blur import rotate
from deepinv.physics.generator import PSFGenerator
def gaussian_blur_padded(sigma=(1, 1), angle=0, filt_size=None):
r"""
Padded gaussian blur filter.
Defined as
.. math::
\begin{equation*}
G(x, y) = \frac{1}{2\pi\sigma_x\sigma_y} \exp{\left(-\frac{x'^2}{2\sigma_x^2} - \frac{y'^2}{2\sigma_y^2}\right)}
\end{equation*}
where :math:`x'` and :math:`y'` are the rotated coordinates obtained by rotating $(x, y)$ around the origin
by an angle :math:`\theta`:
.. math::
\begin{align*}
x' &= x \cos(\theta) - y \sin(\theta) \\
y' &= x \sin(\theta) + y \cos(\theta)
\end{align*}
with :math:`\sigma_x` and :math:`\sigma_y` the standard deviations along the :math:`x'` and :math:`y'` axes.
:param float, tuple[float] sigma: standard deviation of the gaussian filter. If sigma is a float the filter is isotropic, whereas
if sigma is a tuple of floats (sigma_x, sigma_y) the filter is anisotropic.
:param float angle: rotation angle of the filter in degrees (only useful for anisotropic filters)
"""
if isinstance(sigma, (int, float)):
sigma = (sigma, sigma)
device = "cpu"
elif isinstance(sigma, torch.Tensor):
device = sigma.device
s = max(sigma)
c = int(s / 0.3 + 1)
k_size = 2 * c + 1
delta = torch.arange(k_size).to(device)
x, y = torch.meshgrid(delta, delta, indexing="ij")
x = x - c
y = y - c
filt = (x / sigma[0]).pow(2)
filt += (y / sigma[1]).pow(2)
filt = torch.exp(-filt / 2.0)
filt = (
rotate(
filt.unsqueeze(0).unsqueeze(0),
angle,
interpolation=torchvision.transforms.InterpolationMode.BILINEAR,
)
.squeeze(0)
.squeeze(0)
)
filt = filt / filt.flatten().sum()
filt = filt.unsqueeze(0).unsqueeze(0)
if filt_size is not None:
filt = torch.nn.functional.pad(
filt,
(
(filt_size[0] - filt.shape[-2]) // 2,
(filt_size[0] - filt.shape[-2] + 1) // 2,
(filt_size[1] - filt.shape[-1]) // 2,
(filt_size[1] - filt.shape[-1] + 1) // 2,
),
)
return filt
class GaussianBlurGenerator(PSFGenerator):
def __init__(
self,
psf_size: tuple,
num_channels: int = 1,
device: str = "cpu",
dtype: type = torch.float32,
l: float = 0.3,
sigma: float = 0.25,
sigma_min: float = 0.01,
sigma_max: float = 4.0,
) -> None:
kwargs = {
"l": l,
"sigma": sigma,
"sigma_min": sigma_min,
"sigma_max": sigma_max,
}
if len(psf_size) != 2:
raise ValueError(
"psf_size must 2D. Add channels via num_channels parameter"
)
super().__init__(
psf_size=psf_size,
num_channels=num_channels,
device=device,
dtype=dtype,
**kwargs,
)
def step(self, batch_size: int = 1, sigma: float = None, **kwargs):
r"""
Generate a random motion blur PSF with parameters :math:`\sigma` and :math:`l`
:param int batch_size: batch_size.
:param float sigma: the standard deviation of the Gaussian Process
:param float l: the length scale of the trajectory
:return: dictionary with key **'filter'**: the generated PSF of shape `(batch_size, 1, psf_size[0], psf_size[1])`
"""
sigmas = [
self.sigma_min
+ torch.rand(2, **self.factory_kwargs) * (self.sigma_max - self.sigma_min)
for batch in range(batch_size)
]
angles = [
(torch.rand(1, **self.factory_kwargs) * 180.0).item()
for batch in range(batch_size)
]
kernels = [
gaussian_blur_padded(sigma, angle, filt_size=self.psf_size)
for sigma, angle in zip(sigmas, angles)
]
kernel = torch.cat(kernels, dim=0)
return {
"filter": kernel.expand(
-1,
self.num_channels,
-1,
-1,
)
}
|