From 0e9beed76df025928afab9d00360c7ece87b5db8 Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 16 Jun 2016 20:04:20 +0300 Subject: [PATCH 1/5] add constants for filters: BOX, HAMMING, MITCHELL rearrange filters everywhere --- PIL/Image.py | 24 +++++++++++++++--------- libImaging/Imaging.h | 5 ++++- libImaging/Resample.c | 41 ++++++++++++++++++++--------------------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 64f461358..ff78c84b7 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -168,13 +168,15 @@ MESH = 4 # resampling filters NEAREST = NONE = 0 -LANCZOS = ANTIALIAS = 1 +BOX = 4 BILINEAR = LINEAR = 2 +HAMMING = 5 BICUBIC = CUBIC = 3 +MITCHELL = 6 +LANCZOS = ANTIALIAS = 1 # dithers -NONE = 0 -NEAREST = 0 +NEAREST = NONE = 0 ORDERED = 1 # Not yet implemented RASTERIZE = 2 # Not yet implemented FLOYDSTEINBERG = 3 # default @@ -1518,16 +1520,20 @@ class Image(object): :param size: The requested size in pixels, as a 2-tuple: (width, height). :param resample: An optional resampling filter. This can be - one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), - :py:attr:`PIL.Image.BILINEAR` (linear interpolation), - :py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation), or - :py:attr:`PIL.Image.LANCZOS` (a high-quality downsampling filter). + one of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BOX`, + :py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.HAMMING`, + :py:attr:`PIL.Image.BICUBIC`, :py:attr:`PIL.Image.MITCHELL` or + :py:attr:`PIL.Image.LANCZOS`. If omitted, or if the image has mode "1" or "P", it is set :py:attr:`PIL.Image.NEAREST`. + See: :ref:`concept-filters`. :returns: An :py:class:`~PIL.Image.Image` object. """ - if resample not in (NEAREST, BILINEAR, BICUBIC, LANCZOS): + if resample not in ( + NEAREST, BILINEAR, BICUBIC, LANCZOS, + BOX, HAMMING, MITCHELL, + ): raise ValueError("unknown resampling filter") self.load() @@ -1560,7 +1566,7 @@ class Image(object): environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation in a 4x4 environment). If omitted, or if the image has mode "1" or "P", it is - set :py:attr:`PIL.Image.NEAREST`. + set :py:attr:`PIL.Image.NEAREST`. See :ref:`concept-filters`. :param expand: Optional expansion flag. If true, expands the output image to make it large enough to hold the entire rotated image. If false or omitted, make the output image the same size as the diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index adefbc65b..7c0d847cd 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -229,9 +229,12 @@ extern void ImagingError_Clear(void); /* standard filters */ #define IMAGING_TRANSFORM_NEAREST 0 -#define IMAGING_TRANSFORM_LANCZOS 1 +#define IMAGING_TRANSFORM_BOX 4 #define IMAGING_TRANSFORM_BILINEAR 2 +#define IMAGING_TRANSFORM_HAMMING 5 #define IMAGING_TRANSFORM_BICUBIC 3 +#define IMAGING_TRANSFORM_MITCHELL 6 +#define IMAGING_TRANSFORM_LANCZOS 1 typedef int (*ImagingTransformMap)(double* X, double* Y, int x, int y, void* data); diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 23a83e151..01c9a2fcd 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -5,28 +5,11 @@ #define ROUND_UP(f) ((int) ((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F)) - struct filter { double (*filter)(double x); double support; }; -static inline double sinc_filter(double x) -{ - if (x == 0.0) - return 1.0; - x = x * M_PI; - return sin(x) / x; -} - -static inline double lanczos_filter(double x) -{ - /* truncated sinc */ - if (-3.0 <= x && x < 3.0) - return sinc_filter(x) * sinc_filter(x/3); - return 0.0; -} - static inline double bilinear_filter(double x) { if (x < 0.0) @@ -50,9 +33,25 @@ static inline double bicubic_filter(double x) #undef a } -static struct filter LANCZOS = { lanczos_filter, 3.0 }; +static inline double sinc_filter(double x) +{ + if (x == 0.0) + return 1.0; + x = x * M_PI; + return sin(x) / x; +} + +static inline double lanczos_filter(double x) +{ + /* truncated sinc */ + if (-3.0 <= x && x < 3.0) + return sinc_filter(x) * sinc_filter(x/3); + return 0.0; +} + static struct filter BILINEAR = { bilinear_filter, 1.0 }; static struct filter BICUBIC = { bicubic_filter, 2.0 }; +static struct filter LANCZOS = { lanczos_filter, 3.0 }; @@ -524,15 +523,15 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter) /* check filter */ switch (filter) { - case IMAGING_TRANSFORM_LANCZOS: - filterp = &LANCZOS; - break; case IMAGING_TRANSFORM_BILINEAR: filterp = &BILINEAR; break; case IMAGING_TRANSFORM_BICUBIC: filterp = &BICUBIC; break; + case IMAGING_TRANSFORM_LANCZOS: + filterp = &LANCZOS; + break; default: return (Imaging) ImagingError_ValueError( "unsupported resampling filter" From c3d702538d60390309ee3728befd21e6185e72b8 Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 16 Jun 2016 21:12:36 +0300 Subject: [PATCH 2/5] add BOX, HAMMING, MITCHELL filters to resample.c --- libImaging/Resample.c | 51 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 01c9a2fcd..9010f3239 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -10,6 +10,13 @@ struct filter { double support; }; +static inline double box_filter(double x) +{ + if (x >= -0.5 && x < 0.5) + return 1.0; + return 0.0; +} + static inline double bilinear_filter(double x) { if (x < 0.0) @@ -19,6 +26,16 @@ static inline double bilinear_filter(double x) return 0.0; } +static inline double hamming_filter(double x) +{ + if (x < 0.0) + x = -x; + if (x == 0.0) + return 1.0; + x = x * M_PI; + return sin(x) / x * (0.54f + 0.46f * cos(x)); +} + static inline double bicubic_filter(double x) { /* https://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm */ @@ -33,6 +50,27 @@ static inline double bicubic_filter(double x) #undef a } +static inline double mitchell_filter(double x) +{ + double fB = 1.f / 3.f; + double fC = 1.f / 3.f; + double fA1 = -fB - 6*fC; + double fB1 = 6*fB + 30*fC; + double fC1 = -12*fB - 48*fC; + double fD1 = 8*fB + 24*fC; + double fA2 = 12 - 9*fB - 6*fC; + double fB2 = -18 + 12*fB + 6*fC; + double fD2 = 6 - 2*fB; + + if (x < 0.0) + x = -x; + if (x < 1.0) + return ((fA2 * x + fB2) * x*x + fD2) * (1.f/6.f); + if (x < 2.0) + return (((fA1 * x + fB1) * x + fC1) * x + fD1) * (1.f/6.f); + return 0.0; +} + static inline double sinc_filter(double x) { if (x == 0.0) @@ -49,12 +87,14 @@ static inline double lanczos_filter(double x) return 0.0; } +static struct filter BOX = { bilinear_filter, 0.5 }; static struct filter BILINEAR = { bilinear_filter, 1.0 }; +static struct filter HAMMING = { hamming_filter, 1.0 }; static struct filter BICUBIC = { bicubic_filter, 2.0 }; +static struct filter MITCHELL = { mitchell_filter, 2.0 }; static struct filter LANCZOS = { lanczos_filter, 3.0 }; - /* 8 bits for result. Filter can have negative areas. In one cases the sum of the coefficients will be negative, in the other it will be more than 1.0. That is why we need @@ -523,12 +563,21 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter) /* check filter */ switch (filter) { + case IMAGING_TRANSFORM_BOX: + filterp = &BOX; + break; case IMAGING_TRANSFORM_BILINEAR: filterp = &BILINEAR; break; + case IMAGING_TRANSFORM_HAMMING: + filterp = &HAMMING; + break; case IMAGING_TRANSFORM_BICUBIC: filterp = &BICUBIC; break; + case IMAGING_TRANSFORM_MITCHELL: + filterp = &MITCHELL; + break; case IMAGING_TRANSFORM_LANCZOS: filterp = &LANCZOS; break; From a67e5453d5353c597a1e1d4483c371b1b7ca4f2f Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 16 Jun 2016 21:57:02 +0300 Subject: [PATCH 3/5] add tests, fix errors ) --- Tests/test_image_resample.py | 73 +++++++++++++++++++++++++++++++++++- Tests/test_image_resize.py | 9 +++-- libImaging/Resample.c | 2 +- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 68c4ce402..1483d15a1 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -10,7 +10,7 @@ class TestImagingResampleVulnerability(PillowTestCase): ysize = 1000 # unimportant try: # any resampling filter will do here - im.im.resize((xsize, ysize), Image.LINEAR) + im.im.resize((xsize, ysize), Image.BILINEAR) self.fail("Resize should raise MemoryError on invalid xsize") except MemoryError: self.assertTrue(True, "Should raise MemoryError") @@ -89,6 +89,15 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): for y in range(image.size[1]) ) + def test_reduce_box(self): + for mode in ['RGBX', 'RGB', 'La', 'L']: + case = self.make_case(mode, (8, 8), 0xe1) + case = case.resize((4, 4), Image.BOX) + data = ('e1 e1' + 'e1 e1') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + def test_reduce_bilinear(self): for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (8, 8), 0xe1) @@ -98,6 +107,15 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): for channel in case.split(): self.check_case(channel, self.make_sample(data, (4, 4))) + def test_reduce_hamming(self): + for mode in ['RGBX', 'RGB', 'La', 'L']: + case = self.make_case(mode, (8, 8), 0xe1) + case = case.resize((4, 4), Image.HAMMING) + data = ('e1 da' + 'da d3') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + def test_reduce_bicubic(self): for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (12, 12), 0xe1) @@ -108,6 +126,16 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): for channel in case.split(): self.check_case(channel, self.make_sample(data, (6, 6))) + def test_reduce_mitchell(self): + for mode in ['RGBX', 'RGB', 'La', 'L']: + case = self.make_case(mode, (12, 12), 0xe1) + case = case.resize((6, 6), Image.MITCHELL) + data = ('e1 e2 cc' + 'e2 e3 cd' + 'cc cd bb') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (6, 6))) + def test_reduce_lanczos(self): for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (16, 16), 0xe1) @@ -119,6 +147,15 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): for channel in case.split(): self.check_case(channel, self.make_sample(data, (8, 8))) + def test_enlarge_box(self): + for mode in ['RGBX', 'RGB', 'La', 'L']: + case = self.make_case(mode, (2, 2), 0xe1) + case = case.resize((4, 4), Image.BOX) + data = ('e1 e1' + 'e1 e1') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + def test_enlarge_bilinear(self): for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (2, 2), 0xe1) @@ -128,6 +165,17 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): for channel in case.split(): self.check_case(channel, self.make_sample(data, (4, 4))) + def test_enlarge_hamming(self): + for mode in ['RGBX', 'RGB', 'La', 'L']: + case = self.make_case(mode, (4, 4), 0xe1) + case = case.resize((8, 8), Image.HAMMING) + data = ('e1 e1 ea d1' + 'e1 e1 ea d1' + 'ea ea f4 d9' + 'd1 d1 d9 c4') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (8, 8))) + def test_enlarge_bicubic(self): for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (4, 4), 0xe1) @@ -139,6 +187,17 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): for channel in case.split(): self.check_case(channel, self.make_sample(data, (8, 8))) + def test_enlarge_mitchell(self): + for mode in ['RGBX', 'RGB', 'La', 'L']: + case = self.make_case(mode, (4, 4), 0xe1) + case = case.resize((8, 8), Image.MITCHELL) + data = ('e1 e4 e6 b2' + 'e4 e7 e9 b3' + 'e6 e9 eb b4' + 'b2 b3 b5 9a') + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (8, 8))) + def test_enlarge_lanczos(self): for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (6, 6), 0xe1) @@ -211,15 +270,21 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): @unittest.skip("current implementation isn't precise enough") def test_levels_rgba(self): case = self.make_levels_case('RGBA') + self.run_levels_case(case.resize((512, 32), Image.BOX)) self.run_levels_case(case.resize((512, 32), Image.BILINEAR)) + self.run_levels_case(case.resize((512, 32), Image.HAMMING)) self.run_levels_case(case.resize((512, 32), Image.BICUBIC)) + self.run_levels_case(case.resize((512, 32), Image.MITCHELL)) self.run_levels_case(case.resize((512, 32), Image.LANCZOS)) @unittest.skip("current implementation isn't precise enough") def test_levels_la(self): case = self.make_levels_case('LA') + self.run_levels_case(case.resize((512, 32), Image.BOX)) self.run_levels_case(case.resize((512, 32), Image.BILINEAR)) + self.run_levels_case(case.resize((512, 32), Image.HAMMING)) self.run_levels_case(case.resize((512, 32), Image.BICUBIC)) + self.run_levels_case(case.resize((512, 32), Image.MITCHELL)) self.run_levels_case(case.resize((512, 32), Image.LANCZOS)) def make_dity_case(self, mode, clean_pixel, dirty_pixel): @@ -243,14 +308,20 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): def test_dirty_pixels_rgba(self): case = self.make_dity_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0)) + self.run_dity_case(case.resize((20, 20), Image.BOX), (255, 255, 0)) self.run_dity_case(case.resize((20, 20), Image.BILINEAR), (255, 255, 0)) + self.run_dity_case(case.resize((20, 20), Image.HAMMING), (255, 255, 0)) self.run_dity_case(case.resize((20, 20), Image.BICUBIC), (255, 255, 0)) + self.run_dity_case(case.resize((20, 20), Image.MITCHELL), (255, 255, 0)) self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0)) def test_dirty_pixels_la(self): case = self.make_dity_case('LA', (255, 128), (0, 0)) + self.run_dity_case(case.resize((20, 20), Image.BOX), (255,)) self.run_dity_case(case.resize((20, 20), Image.BILINEAR), (255,)) + self.run_dity_case(case.resize((20, 20), Image.HAMMING), (255,)) self.run_dity_case(case.resize((20, 20), Image.BICUBIC), (255,)) + self.run_dity_case(case.resize((20, 20), Image.MITCHELL), (255,)) self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255,)) diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index d59f8ba14..567fd219a 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -39,13 +39,15 @@ class TestImagingCoreResize(PillowTestCase): self.assertEqual(r.im.bands, im.im.bands) def test_reduce_filters(self): - for f in [Image.LINEAR, Image.BILINEAR, Image.BICUBIC, Image.LANCZOS]: + for f in [Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING, + Image.BICUBIC, Image.LANCZOS]: r = self.resize(hopper("RGB"), (15, 12), f) self.assertEqual(r.mode, "RGB") self.assertEqual(r.size, (15, 12)) def test_enlarge_filters(self): - for f in [Image.LINEAR, Image.BILINEAR, Image.BICUBIC, Image.LANCZOS]: + for f in [Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING, + Image.BICUBIC, Image.LANCZOS]: r = self.resize(hopper("RGB"), (212, 195), f) self.assertEqual(r.mode, "RGB") self.assertEqual(r.size, (212, 195)) @@ -64,7 +66,8 @@ class TestImagingCoreResize(PillowTestCase): } samples['dirty'].putpixel((1, 1), 128) - for f in [Image.LINEAR, Image.BILINEAR, Image.BICUBIC, Image.LANCZOS]: + for f in [Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING, + Image.BICUBIC, Image.LANCZOS]: # samples resized with current filter references = dict( (name, self.resize(ch, (4, 4), f)) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 9010f3239..feecc7066 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -87,7 +87,7 @@ static inline double lanczos_filter(double x) return 0.0; } -static struct filter BOX = { bilinear_filter, 0.5 }; +static struct filter BOX = { box_filter, 0.5 }; static struct filter BILINEAR = { bilinear_filter, 1.0 }; static struct filter HAMMING = { hamming_filter, 1.0 }; static struct filter BICUBIC = { bicubic_filter, 2.0 }; From 66715246c1b38b2af068a323a657f47f401ee3fb Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 16 Jun 2016 23:23:03 +0300 Subject: [PATCH 4/5] remove mitchell filter. Has no advantages over bicubic --- PIL/Image.py | 7 ++----- Tests/test_image_resample.py | 25 ------------------------- libImaging/Imaging.h | 1 - libImaging/Resample.c | 25 ------------------------- 4 files changed, 2 insertions(+), 56 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index ff78c84b7..2f9c2666e 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -172,7 +172,6 @@ BOX = 4 BILINEAR = LINEAR = 2 HAMMING = 5 BICUBIC = CUBIC = 3 -MITCHELL = 6 LANCZOS = ANTIALIAS = 1 # dithers @@ -1522,8 +1521,7 @@ class Image(object): :param resample: An optional resampling filter. This can be one of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BOX`, :py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.HAMMING`, - :py:attr:`PIL.Image.BICUBIC`, :py:attr:`PIL.Image.MITCHELL` or - :py:attr:`PIL.Image.LANCZOS`. + :py:attr:`PIL.Image.BICUBIC` or :py:attr:`PIL.Image.LANCZOS`. If omitted, or if the image has mode "1" or "P", it is set :py:attr:`PIL.Image.NEAREST`. See: :ref:`concept-filters`. @@ -1531,8 +1529,7 @@ class Image(object): """ if resample not in ( - NEAREST, BILINEAR, BICUBIC, LANCZOS, - BOX, HAMMING, MITCHELL, + NEAREST, BILINEAR, BICUBIC, LANCZOS, BOX, HAMMING, ): raise ValueError("unknown resampling filter") diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 1483d15a1..464e127f8 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -126,16 +126,6 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): for channel in case.split(): self.check_case(channel, self.make_sample(data, (6, 6))) - def test_reduce_mitchell(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (12, 12), 0xe1) - case = case.resize((6, 6), Image.MITCHELL) - data = ('e1 e2 cc' - 'e2 e3 cd' - 'cc cd bb') - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (6, 6))) - def test_reduce_lanczos(self): for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (16, 16), 0xe1) @@ -187,17 +177,6 @@ class TestImagingCoreResampleAccuracy(PillowTestCase): for channel in case.split(): self.check_case(channel, self.make_sample(data, (8, 8))) - def test_enlarge_mitchell(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (4, 4), 0xe1) - case = case.resize((8, 8), Image.MITCHELL) - data = ('e1 e4 e6 b2' - 'e4 e7 e9 b3' - 'e6 e9 eb b4' - 'b2 b3 b5 9a') - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (8, 8))) - def test_enlarge_lanczos(self): for mode in ['RGBX', 'RGB', 'La', 'L']: case = self.make_case(mode, (6, 6), 0xe1) @@ -274,7 +253,6 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): self.run_levels_case(case.resize((512, 32), Image.BILINEAR)) self.run_levels_case(case.resize((512, 32), Image.HAMMING)) self.run_levels_case(case.resize((512, 32), Image.BICUBIC)) - self.run_levels_case(case.resize((512, 32), Image.MITCHELL)) self.run_levels_case(case.resize((512, 32), Image.LANCZOS)) @unittest.skip("current implementation isn't precise enough") @@ -284,7 +262,6 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): self.run_levels_case(case.resize((512, 32), Image.BILINEAR)) self.run_levels_case(case.resize((512, 32), Image.HAMMING)) self.run_levels_case(case.resize((512, 32), Image.BICUBIC)) - self.run_levels_case(case.resize((512, 32), Image.MITCHELL)) self.run_levels_case(case.resize((512, 32), Image.LANCZOS)) def make_dity_case(self, mode, clean_pixel, dirty_pixel): @@ -312,7 +289,6 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): self.run_dity_case(case.resize((20, 20), Image.BILINEAR), (255, 255, 0)) self.run_dity_case(case.resize((20, 20), Image.HAMMING), (255, 255, 0)) self.run_dity_case(case.resize((20, 20), Image.BICUBIC), (255, 255, 0)) - self.run_dity_case(case.resize((20, 20), Image.MITCHELL), (255, 255, 0)) self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0)) def test_dirty_pixels_la(self): @@ -321,7 +297,6 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): self.run_dity_case(case.resize((20, 20), Image.BILINEAR), (255,)) self.run_dity_case(case.resize((20, 20), Image.HAMMING), (255,)) self.run_dity_case(case.resize((20, 20), Image.BICUBIC), (255,)) - self.run_dity_case(case.resize((20, 20), Image.MITCHELL), (255,)) self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255,)) diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 7c0d847cd..bda10e64d 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -233,7 +233,6 @@ extern void ImagingError_Clear(void); #define IMAGING_TRANSFORM_BILINEAR 2 #define IMAGING_TRANSFORM_HAMMING 5 #define IMAGING_TRANSFORM_BICUBIC 3 -#define IMAGING_TRANSFORM_MITCHELL 6 #define IMAGING_TRANSFORM_LANCZOS 1 typedef int (*ImagingTransformMap)(double* X, double* Y, diff --git a/libImaging/Resample.c b/libImaging/Resample.c index feecc7066..2c1a38a74 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -50,27 +50,6 @@ static inline double bicubic_filter(double x) #undef a } -static inline double mitchell_filter(double x) -{ - double fB = 1.f / 3.f; - double fC = 1.f / 3.f; - double fA1 = -fB - 6*fC; - double fB1 = 6*fB + 30*fC; - double fC1 = -12*fB - 48*fC; - double fD1 = 8*fB + 24*fC; - double fA2 = 12 - 9*fB - 6*fC; - double fB2 = -18 + 12*fB + 6*fC; - double fD2 = 6 - 2*fB; - - if (x < 0.0) - x = -x; - if (x < 1.0) - return ((fA2 * x + fB2) * x*x + fD2) * (1.f/6.f); - if (x < 2.0) - return (((fA1 * x + fB1) * x + fC1) * x + fD1) * (1.f/6.f); - return 0.0; -} - static inline double sinc_filter(double x) { if (x == 0.0) @@ -91,7 +70,6 @@ static struct filter BOX = { box_filter, 0.5 }; static struct filter BILINEAR = { bilinear_filter, 1.0 }; static struct filter HAMMING = { hamming_filter, 1.0 }; static struct filter BICUBIC = { bicubic_filter, 2.0 }; -static struct filter MITCHELL = { mitchell_filter, 2.0 }; static struct filter LANCZOS = { lanczos_filter, 3.0 }; @@ -575,9 +553,6 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter) case IMAGING_TRANSFORM_BICUBIC: filterp = &BICUBIC; break; - case IMAGING_TRANSFORM_MITCHELL: - filterp = &MITCHELL; - break; case IMAGING_TRANSFORM_LANCZOS: filterp = &LANCZOS; break; From 41f03a05e544505d24e5b9036c0c055ab3465b44 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 17 Jun 2016 00:03:25 +0300 Subject: [PATCH 5/5] add filters to concepts --- docs/handbook/concepts.rst | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 3733387cf..8c5de15d6 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -96,7 +96,16 @@ For geometry operations that may map multiple input pixels to a single output pixel, the Python Imaging Library provides four different resampling *filters*. ``NEAREST`` - Pick the nearest pixel from the input image. Ignore all other input pixels. + Pick one nearest pixel from the input image. Ignore all other input pixels. + +``BOX`` + Each pixel of source image contributes to one pixel of the + destination image with identical weights. + For upscaling is equivalent of ``NEAREST``. + This filter can only be used with the :py:meth:`~PIL.Image.Image.resize` + and :py:meth:`~PIL.Image.Image.thumbnail` methods. + + .. versionadded:: 3.4.0 ``BILINEAR`` For resize calculate the output pixel value using linear interpolation @@ -104,6 +113,14 @@ pixel, the Python Imaging Library provides four different resampling *filters*. For other transformations linear interpolation over a 2x2 environment in the input image is used. +``HAMMING`` + Produces more sharp image than ``BILINEAR``, doesn't have dislocations + on local level like with ``BOX``. + This filter can only be used with the :py:meth:`~PIL.Image.Image.resize` + and :py:meth:`~PIL.Image.Image.thumbnail` methods. + + .. versionadded:: 3.4.0 + ``BICUBIC`` For resize calculate the output pixel value using cubic interpolation on all pixels that may contribute to the output value. @@ -128,8 +145,12 @@ Filters comparison table +============+=============+===========+=============+ |``NEAREST`` | | | ⭐⭐⭐⭐⭐ | +------------+-------------+-----------+-------------+ +|``BOX`` | ⭐ | | ⭐⭐⭐⭐ | ++------------+-------------+-----------+-------------+ |``BILINEAR``| ⭐ | ⭐ | ⭐⭐⭐ | +------------+-------------+-----------+-------------+ +|``HAMMING`` | ⭐⭐ | | ⭐⭐⭐ | ++------------+-------------+-----------+-------------+ |``BICUBIC`` | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | +------------+-------------+-----------+-------------+ |``LANCZOS`` | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐ |