Skip to content

Adjust Gaussian blur padding mode #9109

Open
@stephen-huan

Description

@stephen-huan

🚀 The feature

Currently torchvision.transforms.v2.GaussianBlur takes a kernel_size and sigma but has no means of adjusting the padding_mode (as in Conv2d). I propose either (1) adding such an argument or (2) changing the default padding mode from "reflect" to "symmetric".

Happy to submit a PR if there's interest.

Motivation, pitch

The padding mode "reflect" is not intensity preserving, that is, the average pixel value can change after blurring (even though the kernel sums to 1). This would be fixed if the padding mode "symmetric" was used. This mirrors opencv's GaussianBlur(), which defaults to BORDER_REFLECT_101, and instead BORDER_REFLECT must be used for the intensity preserving behavior. Instead, scipy's scipy.ndimage.gaussian_filter defaults to the confusingly named "reflect" (pytorch's "symmetric") and pytorch's "reflect" is instead "mirror".

In summary,

  • torch "reflect" / opencv BORDER_REFLECT_101/ scipy "mirror": not intensity preserving, default for torch and opencv.
  • torch "symmetric" / opencv BORDER_REFLECT/ scipy "reflect": intensity preserving, default for scipy.

I find the scipy behavior more intuitive and I see no reason to prefer "reflect" over "symmetric" as a default. If we want to maintain strict equivalence to opencv, then we should add an argument to customize the padding like opencv and scipy.

import cv2 as cv
import numpy as np
import torch
from torchvision.transforms.v2.functional import gaussian_blur
from scipy.ndimage import gaussian_filter

if __name__ == "__main__":
    img = np.zeros((8, 8))
    img[[0, -1], :] = 1
    img[:, [0, -1]] = 1
    img /= np.sum(img)
    print(f"original: {np.sum(img):.6f}")

    blur = gaussian_filter(img, 1, radius=2)
    print(f"   scipy: {np.sum(blur):.6f}")

    blur = gaussian_filter(img, 1, radius=2, mode="mirror")
    print(f"   scipy: {np.sum(blur):.6f}")

    blur = cv.GaussianBlur(img, (5, 5), 1, borderType=cv.BORDER_REFLECT)
    print(f"  opencv: {np.sum(blur):.6f}")

    blur = cv.GaussianBlur(img, (5, 5), 1)
    print(f"  opencv: {np.sum(blur):.6f}")

    x = torch.tensor(np.expand_dims(img, 0))
    blur = gaussian_blur(x, [5, 5], [1, 1])
    print(f"   torch: {torch.sum(blur):.6f}")
original: 1.000000
   scipy: 1.000000
   scipy: 0.731235
  opencv: 1.000000
  opencv: 0.731235
   torch: 0.731235

somewhat related: opencv/opencv#9863

Alternatives

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions