Skip to content

Commit 9f8bec5

Browse files
committed
draft initial gaussian_blur cvcuda kernel implementation
1 parent 98d7dfb commit 9f8bec5

File tree

3 files changed

+99
-6
lines changed

3 files changed

+99
-6
lines changed

test/test_transforms_v2.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3995,6 +3995,70 @@ def test_functional_image_correctness(self, dimensions, kernel_size, sigma, dtyp
39953995
torch.testing.assert_close(actual, expected, rtol=0, atol=1)
39963996

39973997

3998+
@pytest.mark.skipif(not CVCUDA_AVAILABLE, reason="test requires CVCUDA")
3999+
@needs_cuda
4000+
class TestGaussianBlurCVCUDA:
4001+
def test_kernel_image_errors(self):
4002+
image = make_image_cvcuda(batch_dims=(1,))
4003+
4004+
with pytest.raises(ValueError, match="kernel_size is a sequence its length should be 2"):
4005+
F.gaussian_blur_cvcuda(image, kernel_size=[1, 2, 3])
4006+
4007+
for kernel_size in [2, -1]:
4008+
with pytest.raises(ValueError, match="kernel_size should have odd and positive integers"):
4009+
F.gaussian_blur_cvcuda(image, kernel_size=kernel_size)
4010+
4011+
with pytest.raises(ValueError, match="sigma is a sequence, its length should be 2"):
4012+
F.gaussian_blur_cvcuda(image, kernel_size=1, sigma=[1, 2, 3])
4013+
4014+
with pytest.raises(TypeError, match="sigma should be either float or sequence of floats"):
4015+
F.gaussian_blur_cvcuda(image, kernel_size=1, sigma=object())
4016+
4017+
with pytest.raises(ValueError, match="sigma should have positive values"):
4018+
F.gaussian_blur_cvcuda(image, kernel_size=1, sigma=-1)
4019+
4020+
def test_functional(self):
4021+
check_functional(F.gaussian_blur, make_image_cvcuda(batch_dims=(1,)), kernel_size=(3, 3))
4022+
4023+
@pytest.mark.parametrize("device", cpu_and_cuda())
4024+
@pytest.mark.parametrize("sigma", [5, 2.0, (0.5, 2), [1.3, 2.7]])
4025+
def test_transform(self, device, sigma):
4026+
check_transform(
4027+
transforms.GaussianBlur(kernel_size=3, sigma=sigma), make_image_cvcuda(batch_dims=(1,), device=device)
4028+
)
4029+
4030+
@pytest.mark.parametrize(
4031+
("dimensions", "kernel_size", "sigma"),
4032+
[
4033+
((10, 12), (3, 3), 0.8),
4034+
((10, 12), (3, 3), 0.5),
4035+
((10, 12), (3, 5), 0.8),
4036+
((10, 12), (3, 5), 0.5),
4037+
((26, 28), (23, 23), 1.7),
4038+
],
4039+
)
4040+
@pytest.mark.parametrize("color_space", ["RGB", "GRAY"])
4041+
@pytest.mark.parametrize("batch_dims", [(1,), (2,), (4,)])
4042+
@pytest.mark.parametrize("dtype", [torch.uint8, torch.float32])
4043+
def test_functional_image_correctness(self, dimensions, kernel_size, sigma, color_space, batch_dims, dtype):
4044+
height, width = dimensions
4045+
4046+
image_tensor = make_image(
4047+
size=(height, width), color_space=color_space, batch_dims=batch_dims, dtype=dtype, device="cuda"
4048+
)
4049+
image_cvcuda = F.to_cvcuda_tensor(image_tensor)
4050+
4051+
expected = F.gaussian_blur_image(image_tensor, kernel_size=kernel_size, sigma=sigma)
4052+
actual = F.gaussian_blur_cvcuda(image_cvcuda, kernel_size=kernel_size, sigma=sigma)
4053+
actual_torch = F.cvcuda_to_tensor(actual)
4054+
4055+
if dtype.is_floating_point:
4056+
torch.testing.assert_close(actual_torch, expected, rtol=0, atol=0.3)
4057+
else:
4058+
# uint8/16 gaussians can differ by up to max-value, most likely an overflow issue
4059+
torch.testing.assert_close(actual_torch, expected, rtol=0, atol=get_max_value(dtype))
4060+
4061+
39984062
class TestGaussianNoise:
39994063
@pytest.mark.parametrize(
40004064
"make_input",

torchvision/transforms/v2/functional/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
from ._misc import (
148148
convert_image_dtype,
149149
gaussian_blur,
150+
gaussian_blur_cvcuda,
150151
gaussian_blur_image,
151152
gaussian_blur_video,
152153
gaussian_noise,

torchvision/transforms/v2/functional/_misc.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import math
2-
from typing import Optional, TYPE_CHECKING
2+
from typing import Optional, Sequence, TYPE_CHECKING
33

44
import PIL.Image
55
import torch
@@ -106,11 +106,10 @@ def _get_gaussian_kernel2d(
106106
return kernel2d
107107

108108

109-
@_register_kernel_internal(gaussian_blur, torch.Tensor)
110-
@_register_kernel_internal(gaussian_blur, tv_tensors.Image)
111-
def gaussian_blur_image(
112-
image: torch.Tensor, kernel_size: list[int], sigma: Optional[list[float]] = None
113-
) -> torch.Tensor:
109+
def _validate_kernel_size_and_sigma(
110+
kernel_size: Sequence[int] | int,
111+
sigma: Sequence[float | int] | float | int | None = None,
112+
) -> tuple[list[int], list[float]]:
114113
# TODO: consider deprecating integers from sigma on the future
115114
if isinstance(kernel_size, int):
116115
kernel_size = [kernel_size, kernel_size]
@@ -139,6 +138,16 @@ def gaussian_blur_image(
139138
if s <= 0.0:
140139
raise ValueError(f"sigma should have positive values. Got {sigma}")
141140

141+
return kernel_size, sigma
142+
143+
144+
@_register_kernel_internal(gaussian_blur, torch.Tensor)
145+
@_register_kernel_internal(gaussian_blur, tv_tensors.Image)
146+
def gaussian_blur_image(
147+
image: torch.Tensor, kernel_size: list[int], sigma: Optional[list[float]] = None
148+
) -> torch.Tensor:
149+
kernel_size, sigma = _validate_kernel_size_and_sigma(kernel_size, sigma)
150+
142151
if image.numel() == 0:
143152
return image
144153

@@ -188,6 +197,25 @@ def gaussian_blur_video(
188197
return gaussian_blur_image(video, kernel_size, sigma)
189198

190199

200+
def gaussian_blur_cvcuda(
201+
image: "cvcuda.Tensor",
202+
kernel_size: Sequence[int] | int,
203+
sigma: Sequence[float | int] | float | int | None = None,
204+
) -> "cvcuda.Tensor":
205+
kernel_size, sigma = _validate_kernel_size_and_sigma(kernel_size, sigma)
206+
207+
return cvcuda.gaussian(
208+
image,
209+
tuple(kernel_size),
210+
tuple(sigma),
211+
border=cvcuda.Border.REFLECT,
212+
)
213+
214+
215+
if CVCUDA_AVAILABLE:
216+
_gaussian_blur_cvcuda = _register_kernel_internal(gaussian_blur, cvcuda.Tensor)(gaussian_blur_cvcuda)
217+
218+
191219
def gaussian_noise(inpt: torch.Tensor, mean: float = 0.0, sigma: float = 0.1, clip: bool = True) -> torch.Tensor:
192220
"""See :class:`~torchvision.transforms.v2.GaussianNoise`"""
193221
if torch.jit.is_scripting():

0 commit comments

Comments
 (0)