mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-30 23:47:27 +03:00 
			
		
		
		
	Allow GaussianBlur and BoxBlur to accept a sequence of x and y radii
This commit is contained in:
		
							parent
							
								
									08b538780d
								
							
						
					
					
						commit
						c167d7a269
					
				|  | @ -22,7 +22,7 @@ def test_imageops_box_blur(): | |||
| 
 | ||||
| 
 | ||||
| def box_blur(image, radius=1, n=1): | ||||
|     return image._new(image.im.box_blur(radius, n)) | ||||
|     return image._new(image.im.box_blur((radius, radius), n)) | ||||
| 
 | ||||
| 
 | ||||
| def assert_image(im, data, delta=0): | ||||
|  |  | |||
|  | @ -24,8 +24,10 @@ from .helper import assert_image_equal, hopper | |||
|         ImageFilter.ModeFilter, | ||||
|         ImageFilter.GaussianBlur, | ||||
|         ImageFilter.GaussianBlur(5), | ||||
|         ImageFilter.GaussianBlur((2, 5)), | ||||
|         ImageFilter.BoxBlur(0), | ||||
|         ImageFilter.BoxBlur(5), | ||||
|         ImageFilter.BoxBlur((2, 5)), | ||||
|         ImageFilter.UnsharpMask, | ||||
|         ImageFilter.UnsharpMask(10), | ||||
|     ), | ||||
|  | @ -185,12 +187,21 @@ def test_consistency_5x5(mode): | |||
|             assert_image_equal(source.filter(kernel), reference) | ||||
| 
 | ||||
| 
 | ||||
| def test_invalid_box_blur_filter(): | ||||
| @pytest.mark.parametrize( | ||||
|     "radius", | ||||
|     ( | ||||
|         -2, | ||||
|         (-2, -2), | ||||
|         (-2, 2), | ||||
|         (2, -2), | ||||
|     ), | ||||
| ) | ||||
| def test_invalid_box_blur_filter(radius): | ||||
|     with pytest.raises(ValueError): | ||||
|         ImageFilter.BoxBlur(-2) | ||||
|         ImageFilter.BoxBlur(radius) | ||||
| 
 | ||||
|     im = hopper() | ||||
|     box_blur_filter = ImageFilter.BoxBlur(2) | ||||
|     box_blur_filter.radius = -2 | ||||
|     box_blur_filter.radius = radius | ||||
|     with pytest.raises(ValueError): | ||||
|         im.filter(box_blur_filter) | ||||
|  |  | |||
|  | @ -157,7 +157,8 @@ class GaussianBlur(MultibandFilter): | |||
|     approximates a Gaussian kernel. For details on accuracy see | ||||
|     <https://www.mia.uni-saarland.de/Publications/gwosdek-ssvm11.pdf> | ||||
| 
 | ||||
|     :param radius: Standard deviation of the Gaussian kernel. | ||||
|     :param radius: Standard deviation of the Gaussian kernel. Either a sequence of two | ||||
|                    numbers for x and y, or a single number for both. | ||||
|     """ | ||||
| 
 | ||||
|     name = "GaussianBlur" | ||||
|  | @ -166,7 +167,10 @@ class GaussianBlur(MultibandFilter): | |||
|         self.radius = radius | ||||
| 
 | ||||
|     def filter(self, image): | ||||
|         return image.gaussian_blur(self.radius) | ||||
|         xy = self.radius | ||||
|         if not isinstance(xy, (tuple, list)): | ||||
|             xy = (xy, xy) | ||||
|         return image.gaussian_blur(xy) | ||||
| 
 | ||||
| 
 | ||||
| class BoxBlur(MultibandFilter): | ||||
|  | @ -176,21 +180,29 @@ class BoxBlur(MultibandFilter): | |||
|     which runs in linear time relative to the size of the image | ||||
|     for any radius value. | ||||
| 
 | ||||
|     :param radius: Size of the box in one direction. Radius 0 does not blur, | ||||
|                    returns an identical image. Radius 1 takes 1 pixel | ||||
|                    in each direction, i.e. 9 pixels in total. | ||||
|     :param radius: Size of the box in a direction. Either a sequence of two numbers for | ||||
|                    x and y, or a single number for both. | ||||
| 
 | ||||
|                    Radius 0 does not blur, returns an identical image. | ||||
|                    Radius 1 takes 1 pixel in each direction, i.e. 9 pixels in total. | ||||
|     """ | ||||
| 
 | ||||
|     name = "BoxBlur" | ||||
| 
 | ||||
|     def __init__(self, radius): | ||||
|         if radius < 0: | ||||
|         xy = radius | ||||
|         if not isinstance(xy, (tuple, list)): | ||||
|             xy = (xy, xy) | ||||
|         if xy[0] < 0 or xy[1] < 0: | ||||
|             msg = "radius must be >= 0" | ||||
|             raise ValueError(msg) | ||||
|         self.radius = radius | ||||
| 
 | ||||
|     def filter(self, image): | ||||
|         return image.box_blur(self.radius) | ||||
|         xy = self.radius | ||||
|         if not isinstance(xy, (tuple, list)): | ||||
|             xy = (xy, xy) | ||||
|         return image.box_blur(xy) | ||||
| 
 | ||||
| 
 | ||||
| class UnsharpMask(MultibandFilter): | ||||
|  |  | |||
|  | @ -1075,9 +1075,9 @@ _gaussian_blur(ImagingObject *self, PyObject *args) { | |||
|     Imaging imIn; | ||||
|     Imaging imOut; | ||||
| 
 | ||||
|     float radius = 0; | ||||
|     float xradius, yradius; | ||||
|     int passes = 3; | ||||
|     if (!PyArg_ParseTuple(args, "f|i", &radius, &passes)) { | ||||
|     if (!PyArg_ParseTuple(args, "(ff)|i", &xradius, &yradius, &passes)) { | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -1087,7 +1087,7 @@ _gaussian_blur(ImagingObject *self, PyObject *args) { | |||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     if (!ImagingGaussianBlur(imOut, imIn, radius, passes)) { | ||||
|     if (!ImagingGaussianBlur(imOut, imIn, xradius, yradius, passes)) { | ||||
|         ImagingDelete(imOut); | ||||
|         return NULL; | ||||
|     } | ||||
|  | @ -2131,9 +2131,9 @@ _box_blur(ImagingObject *self, PyObject *args) { | |||
|     Imaging imIn; | ||||
|     Imaging imOut; | ||||
| 
 | ||||
|     float radius; | ||||
|     float xradius, yradius; | ||||
|     int n = 1; | ||||
|     if (!PyArg_ParseTuple(args, "f|i", &radius, &n)) { | ||||
|     if (!PyArg_ParseTuple(args, "(ff)|i", &xradius, &yradius, &n)) { | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -2143,7 +2143,7 @@ _box_blur(ImagingObject *self, PyObject *args) { | |||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     if (!ImagingBoxBlur(imOut, imIn, radius, n)) { | ||||
|     if (!ImagingBoxBlur(imOut, imIn, xradius, yradius, n)) { | ||||
|         ImagingDelete(imOut); | ||||
|         return NULL; | ||||
|     } | ||||
|  |  | |||
|  | @ -230,14 +230,14 @@ ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) { | |||
| } | ||||
| 
 | ||||
| Imaging | ||||
| ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n) { | ||||
| ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n) { | ||||
|     int i; | ||||
|     Imaging imTransposed; | ||||
| 
 | ||||
|     if (n < 1) { | ||||
|         return ImagingError_ValueError("number of passes must be greater than zero"); | ||||
|     } | ||||
|     if (radius < 0) { | ||||
|     if (xradius < 0 || yradius < 0) { | ||||
|         return ImagingError_ValueError("radius must be >= 0"); | ||||
|     } | ||||
| 
 | ||||
|  | @ -266,16 +266,16 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n) { | |||
|     /* Apply blur in one dimension.
 | ||||
|        Use imOut as a destination at first pass, | ||||
|        then use imOut as a source too. */ | ||||
|     ImagingHorizontalBoxBlur(imOut, imIn, radius); | ||||
|     ImagingHorizontalBoxBlur(imOut, imIn, xradius); | ||||
|     for (i = 1; i < n; i++) { | ||||
|         ImagingHorizontalBoxBlur(imOut, imOut, radius); | ||||
|         ImagingHorizontalBoxBlur(imOut, imOut, xradius); | ||||
|     } | ||||
|     /* Transpose result for blur in another direction. */ | ||||
|     ImagingTranspose(imTransposed, imOut); | ||||
| 
 | ||||
|     /* Reuse imTransposed as a source and destination there. */ | ||||
|     for (i = 0; i < n; i++) { | ||||
|         ImagingHorizontalBoxBlur(imTransposed, imTransposed, radius); | ||||
|         ImagingHorizontalBoxBlur(imTransposed, imTransposed, yradius); | ||||
|     } | ||||
|     /* Restore original orientation. */ | ||||
|     ImagingTranspose(imOut, imTransposed); | ||||
|  | @ -285,8 +285,8 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n) { | |||
|     return imOut; | ||||
| } | ||||
| 
 | ||||
| Imaging | ||||
| ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius, int passes) { | ||||
| static float | ||||
| _gaussian_blur_radius(float radius, int passes) { | ||||
|     float sigma2, L, l, a; | ||||
| 
 | ||||
|     sigma2 = radius * radius / passes; | ||||
|  | @ -299,5 +299,16 @@ ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius, int passes) { | |||
|     a = (2 * l + 1) * (l * (l + 1) - 3 * sigma2); | ||||
|     a /= 6 * (sigma2 - (l + 1) * (l + 1)); | ||||
| 
 | ||||
|     return ImagingBoxBlur(imOut, imIn, l + a, passes); | ||||
|     return l + a; | ||||
| } | ||||
| 
 | ||||
| Imaging | ||||
| ImagingGaussianBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int passes) { | ||||
|     return ImagingBoxBlur( | ||||
|       imOut, | ||||
|       imIn, | ||||
|       _gaussian_blur_radius(xradius, passes), | ||||
|       _gaussian_blur_radius(yradius, passes), | ||||
|       passes | ||||
|     ); | ||||
| } | ||||
|  |  | |||
|  | @ -309,7 +309,7 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn); | |||
| extern Imaging | ||||
| ImagingFlipTopBottom(Imaging imOut, Imaging imIn); | ||||
| extern Imaging | ||||
| ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius, int passes); | ||||
| ImagingGaussianBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int passes); | ||||
| extern Imaging | ||||
| ImagingGetBand(Imaging im, int band); | ||||
| extern Imaging | ||||
|  | @ -376,7 +376,7 @@ ImagingTransform( | |||
| extern Imaging | ||||
| ImagingUnsharpMask(Imaging imOut, Imaging im, float radius, int percent, int threshold); | ||||
| extern Imaging | ||||
| ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n); | ||||
| ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n); | ||||
| extern Imaging | ||||
| ImagingColorLUT3D_linear( | ||||
|     Imaging imOut, | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ ImagingUnsharpMask( | |||
| 
 | ||||
|     /* First, do a gaussian blur on the image, putting results in imOut
 | ||||
|        temporarily. All format checks are in gaussian blur. */ | ||||
|     result = ImagingGaussianBlur(imOut, imIn, radius, 3); | ||||
|     result = ImagingGaussianBlur(imOut, imIn, radius, radius, 3); | ||||
|     if (!result) { | ||||
|         return NULL; | ||||
|     } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user