mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	Merge pull request #7108 from radarhere/filter
This commit is contained in:
		
						commit
						74a851965c
					
				
							
								
								
									
										
											BIN
										
									
								
								Tests/images/hopper_emboss_I.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/hopper_emboss_I.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 13 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/hopper_emboss_more_I.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/hopper_emboss_more_I.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
						 | 
					@ -30,15 +30,16 @@ from .helper import assert_image_equal, hopper
 | 
				
			||||||
        ImageFilter.UnsharpMask(10),
 | 
					        ImageFilter.UnsharpMask(10),
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("L", "RGB", "CMYK"))
 | 
					@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
 | 
				
			||||||
def test_sanity(filter_to_apply, mode):
 | 
					def test_sanity(filter_to_apply, mode):
 | 
				
			||||||
    im = hopper(mode)
 | 
					    im = hopper(mode)
 | 
				
			||||||
 | 
					    if mode != "I" or isinstance(filter_to_apply, ImageFilter.BuiltinFilter):
 | 
				
			||||||
        out = im.filter(filter_to_apply)
 | 
					        out = im.filter(filter_to_apply)
 | 
				
			||||||
        assert out.mode == im.mode
 | 
					        assert out.mode == im.mode
 | 
				
			||||||
        assert out.size == im.size
 | 
					        assert out.size == im.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("L", "RGB", "CMYK"))
 | 
					@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
 | 
				
			||||||
def test_sanity_error(mode):
 | 
					def test_sanity_error(mode):
 | 
				
			||||||
    with pytest.raises(TypeError):
 | 
					    with pytest.raises(TypeError):
 | 
				
			||||||
        im = hopper(mode)
 | 
					        im = hopper(mode)
 | 
				
			||||||
| 
						 | 
					@ -130,10 +131,12 @@ def test_kernel_not_enough_coefficients():
 | 
				
			||||||
        ImageFilter.Kernel((3, 3), (0, 0))
 | 
					        ImageFilter.Kernel((3, 3), (0, 0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("L", "LA", "RGB", "CMYK"))
 | 
					@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
 | 
				
			||||||
def test_consistency_3x3(mode):
 | 
					def test_consistency_3x3(mode):
 | 
				
			||||||
    with Image.open("Tests/images/hopper.bmp") as source:
 | 
					    with Image.open("Tests/images/hopper.bmp") as source:
 | 
				
			||||||
        with Image.open("Tests/images/hopper_emboss.bmp") as reference:
 | 
					        reference_name = "hopper_emboss"
 | 
				
			||||||
 | 
					        reference_name += "_I.png" if mode == "I" else ".bmp"
 | 
				
			||||||
 | 
					        with Image.open("Tests/images/" + reference_name) as reference:
 | 
				
			||||||
            kernel = ImageFilter.Kernel(
 | 
					            kernel = ImageFilter.Kernel(
 | 
				
			||||||
                (3, 3),
 | 
					                (3, 3),
 | 
				
			||||||
                # fmt: off
 | 
					                # fmt: off
 | 
				
			||||||
| 
						 | 
					@ -146,16 +149,20 @@ def test_consistency_3x3(mode):
 | 
				
			||||||
            source = source.split() * 2
 | 
					            source = source.split() * 2
 | 
				
			||||||
            reference = reference.split() * 2
 | 
					            reference = reference.split() * 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert_image_equal(
 | 
					            if mode == "I":
 | 
				
			||||||
                Image.merge(mode, source[: len(mode)]).filter(kernel),
 | 
					                source = source[0].convert(mode)
 | 
				
			||||||
                Image.merge(mode, reference[: len(mode)]),
 | 
					            else:
 | 
				
			||||||
            )
 | 
					                source = Image.merge(mode, source[: len(mode)])
 | 
				
			||||||
 | 
					            reference = Image.merge(mode, reference[: len(mode)])
 | 
				
			||||||
 | 
					            assert_image_equal(source.filter(kernel), reference)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("L", "LA", "RGB", "CMYK"))
 | 
					@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
 | 
				
			||||||
def test_consistency_5x5(mode):
 | 
					def test_consistency_5x5(mode):
 | 
				
			||||||
    with Image.open("Tests/images/hopper.bmp") as source:
 | 
					    with Image.open("Tests/images/hopper.bmp") as source:
 | 
				
			||||||
        with Image.open("Tests/images/hopper_emboss_more.bmp") as reference:
 | 
					        reference_name = "hopper_emboss_more"
 | 
				
			||||||
 | 
					        reference_name += "_I.png" if mode == "I" else ".bmp"
 | 
				
			||||||
 | 
					        with Image.open("Tests/images/" + reference_name) as reference:
 | 
				
			||||||
            kernel = ImageFilter.Kernel(
 | 
					            kernel = ImageFilter.Kernel(
 | 
				
			||||||
                (5, 5),
 | 
					                (5, 5),
 | 
				
			||||||
                # fmt: off
 | 
					                # fmt: off
 | 
				
			||||||
| 
						 | 
					@ -170,10 +177,12 @@ def test_consistency_5x5(mode):
 | 
				
			||||||
            source = source.split() * 2
 | 
					            source = source.split() * 2
 | 
				
			||||||
            reference = reference.split() * 2
 | 
					            reference = reference.split() * 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert_image_equal(
 | 
					            if mode == "I":
 | 
				
			||||||
                Image.merge(mode, source[: len(mode)]).filter(kernel),
 | 
					                source = source[0].convert(mode)
 | 
				
			||||||
                Image.merge(mode, reference[: len(mode)]),
 | 
					            else:
 | 
				
			||||||
            )
 | 
					                source = Image.merge(mode, source[: len(mode)])
 | 
				
			||||||
 | 
					            reference = Image.merge(mode, reference[: len(mode)])
 | 
				
			||||||
 | 
					            assert_image_equal(source.filter(kernel), reference)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_invalid_box_blur_filter():
 | 
					def test_invalid_box_blur_filter():
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,17 @@ clip8(float in) {
 | 
				
			||||||
    return (UINT8)in;
 | 
					    return (UINT8)in;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline INT32
 | 
				
			||||||
 | 
					clip32(float in) {
 | 
				
			||||||
 | 
					    if (in <= 0.0) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (in >= pow(2, 31) - 1) {
 | 
				
			||||||
 | 
					        return pow(2, 31) - 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return (INT32)in;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Imaging
 | 
					Imaging
 | 
				
			||||||
ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
 | 
					ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
 | 
				
			||||||
    Imaging imOut;
 | 
					    Imaging imOut;
 | 
				
			||||||
| 
						 | 
					@ -96,8 +107,8 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
					ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
#define KERNEL1x3(in0, x, kernel, d)                                             \
 | 
					#define KERNEL1x3(in0, x, kernel, d)                                             \
 | 
				
			||||||
    (_i2f((UINT8)in0[x - d]) * (kernel)[0] + _i2f((UINT8)in0[x]) * (kernel)[1] + \
 | 
					    (_i2f(in0[x - d]) * (kernel)[0] + _i2f(in0[x]) * (kernel)[1] + \
 | 
				
			||||||
     _i2f((UINT8)in0[x + d]) * (kernel)[2])
 | 
					     _i2f(in0[x + d]) * (kernel)[2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int x = 0, y = 0;
 | 
					    int x = 0, y = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,6 +116,24 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
    if (im->bands == 1) {
 | 
					    if (im->bands == 1) {
 | 
				
			||||||
        // Add one time for rounding
 | 
					        // Add one time for rounding
 | 
				
			||||||
        offset += 0.5;
 | 
					        offset += 0.5;
 | 
				
			||||||
 | 
					        if (im->type == IMAGING_TYPE_INT32) {
 | 
				
			||||||
 | 
					            for (y = 1; y < im->ysize - 1; y++) {
 | 
				
			||||||
 | 
					                INT32 *in_1 = (INT32 *)im->image[y - 1];
 | 
				
			||||||
 | 
					                INT32 *in0 = (INT32 *)im->image[y];
 | 
				
			||||||
 | 
					                INT32 *in1 = (INT32 *)im->image[y + 1];
 | 
				
			||||||
 | 
					                INT32 *out = (INT32 *)imOut->image[y];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                out[0] = in0[0];
 | 
				
			||||||
 | 
					                for (x = 1; x < im->xsize - 1; x++) {
 | 
				
			||||||
 | 
					                    float ss = offset;
 | 
				
			||||||
 | 
					                    ss += KERNEL1x3(in1, x, &kernel[0], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x3(in0, x, &kernel[3], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x3(in_1, x, &kernel[6], 1);
 | 
				
			||||||
 | 
					                    out[x] = clip32(ss);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                out[x] = in0[x];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
            for (y = 1; y < im->ysize - 1; y++) {
 | 
					            for (y = 1; y < im->ysize - 1; y++) {
 | 
				
			||||||
                UINT8 *in_1 = (UINT8 *)im->image[y - 1];
 | 
					                UINT8 *in_1 = (UINT8 *)im->image[y - 1];
 | 
				
			||||||
                UINT8 *in0 = (UINT8 *)im->image[y];
 | 
					                UINT8 *in0 = (UINT8 *)im->image[y];
 | 
				
			||||||
| 
						 | 
					@ -121,6 +150,7 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                out[x] = in0[x];
 | 
					                out[x] = in0[x];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        // Add one time for rounding
 | 
					        // Add one time for rounding
 | 
				
			||||||
        offset += 0.5;
 | 
					        offset += 0.5;
 | 
				
			||||||
| 
						 | 
					@ -195,10 +225,10 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
					ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
#define KERNEL1x5(in0, x, kernel, d)                                             \
 | 
					#define KERNEL1x5(in0, x, kernel, d)                                             \
 | 
				
			||||||
    (_i2f((UINT8)in0[x - d - d]) * (kernel)[0] +                                 \
 | 
					    (_i2f(in0[x - d - d]) * (kernel)[0] +                                 \
 | 
				
			||||||
     _i2f((UINT8)in0[x - d]) * (kernel)[1] + _i2f((UINT8)in0[x]) * (kernel)[2] + \
 | 
					     _i2f(in0[x - d]) * (kernel)[1] + _i2f(in0[x]) * (kernel)[2] + \
 | 
				
			||||||
     _i2f((UINT8)in0[x + d]) * (kernel)[3] +                                     \
 | 
					     _i2f(in0[x + d]) * (kernel)[3] +                                     \
 | 
				
			||||||
     _i2f((UINT8)in0[x + d + d]) * (kernel)[4])
 | 
					     _i2f(in0[x + d + d]) * (kernel)[4])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int x = 0, y = 0;
 | 
					    int x = 0, y = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,6 +237,30 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
    if (im->bands == 1) {
 | 
					    if (im->bands == 1) {
 | 
				
			||||||
        // Add one time for rounding
 | 
					        // Add one time for rounding
 | 
				
			||||||
        offset += 0.5;
 | 
					        offset += 0.5;
 | 
				
			||||||
 | 
					        if (im->type == IMAGING_TYPE_INT32) {
 | 
				
			||||||
 | 
					            for (y = 2; y < im->ysize - 2; y++) {
 | 
				
			||||||
 | 
					                INT32 *in_2 = (INT32 *)im->image[y - 2];
 | 
				
			||||||
 | 
					                INT32 *in_1 = (INT32 *)im->image[y - 1];
 | 
				
			||||||
 | 
					                INT32 *in0 = (INT32 *)im->image[y];
 | 
				
			||||||
 | 
					                INT32 *in1 = (INT32 *)im->image[y + 1];
 | 
				
			||||||
 | 
					                INT32 *in2 = (INT32 *)im->image[y + 2];
 | 
				
			||||||
 | 
					                INT32 *out = (INT32 *)imOut->image[y];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                out[0] = in0[0];
 | 
				
			||||||
 | 
					                out[1] = in0[1];
 | 
				
			||||||
 | 
					                for (x = 2; x < im->xsize - 2; x++) {
 | 
				
			||||||
 | 
					                    float ss = offset;
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in2, x, &kernel[0], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in1, x, &kernel[5], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in0, x, &kernel[10], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in_1, x, &kernel[15], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in_2, x, &kernel[20], 1);
 | 
				
			||||||
 | 
					                    out[x] = clip32(ss);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                out[x + 0] = in0[x + 0];
 | 
				
			||||||
 | 
					                out[x + 1] = in0[x + 1];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
            for (y = 2; y < im->ysize - 2; y++) {
 | 
					            for (y = 2; y < im->ysize - 2; y++) {
 | 
				
			||||||
                UINT8 *in_2 = (UINT8 *)im->image[y - 2];
 | 
					                UINT8 *in_2 = (UINT8 *)im->image[y - 2];
 | 
				
			||||||
                UINT8 *in_1 = (UINT8 *)im->image[y - 1];
 | 
					                UINT8 *in_1 = (UINT8 *)im->image[y - 1];
 | 
				
			||||||
| 
						 | 
					@ -229,6 +283,7 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
                out[x + 0] = in0[x + 0];
 | 
					                out[x + 0] = in0[x + 0];
 | 
				
			||||||
                out[x + 1] = in0[x + 1];
 | 
					                out[x + 1] = in0[x + 1];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        // Add one time for rounding
 | 
					        // Add one time for rounding
 | 
				
			||||||
        offset += 0.5;
 | 
					        offset += 0.5;
 | 
				
			||||||
| 
						 | 
					@ -327,7 +382,7 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32 *kernel, FLOAT32 o
 | 
				
			||||||
    Imaging imOut;
 | 
					    Imaging imOut;
 | 
				
			||||||
    ImagingSectionCookie cookie;
 | 
					    ImagingSectionCookie cookie;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!im || im->type != IMAGING_TYPE_UINT8) {
 | 
					    if (im->type != IMAGING_TYPE_UINT8 && im->type != IMAGING_TYPE_INT32) {
 | 
				
			||||||
        return (Imaging)ImagingError_ModeError();
 | 
					        return (Imaging)ImagingError_ModeError();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user