mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	Merge pull request #1959 from uploadcare/new-filters
Add Box and Hamming filters for resampling
This commit is contained in:
		
						commit
						e8c123c833
					
				
							
								
								
									
										21
									
								
								PIL/Image.py
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								PIL/Image.py
									
									
									
									
									
								
							| 
						 | 
					@ -168,13 +168,14 @@ MESH = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# resampling filters
 | 
					# resampling filters
 | 
				
			||||||
NEAREST = NONE = 0
 | 
					NEAREST = NONE = 0
 | 
				
			||||||
LANCZOS = ANTIALIAS = 1
 | 
					BOX = 4
 | 
				
			||||||
BILINEAR = LINEAR = 2
 | 
					BILINEAR = LINEAR = 2
 | 
				
			||||||
 | 
					HAMMING = 5
 | 
				
			||||||
BICUBIC = CUBIC = 3
 | 
					BICUBIC = CUBIC = 3
 | 
				
			||||||
 | 
					LANCZOS = ANTIALIAS = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# dithers
 | 
					# dithers
 | 
				
			||||||
NONE = 0
 | 
					NEAREST = NONE = 0
 | 
				
			||||||
NEAREST = 0
 | 
					 | 
				
			||||||
ORDERED = 1  # Not yet implemented
 | 
					ORDERED = 1  # Not yet implemented
 | 
				
			||||||
RASTERIZE = 2  # Not yet implemented
 | 
					RASTERIZE = 2  # Not yet implemented
 | 
				
			||||||
FLOYDSTEINBERG = 3  # default
 | 
					FLOYDSTEINBERG = 3  # default
 | 
				
			||||||
| 
						 | 
					@ -1518,16 +1519,18 @@ class Image(object):
 | 
				
			||||||
        :param size: The requested size in pixels, as a 2-tuple:
 | 
					        :param size: The requested size in pixels, as a 2-tuple:
 | 
				
			||||||
           (width, height).
 | 
					           (width, height).
 | 
				
			||||||
        :param resample: An optional resampling filter.  This can be
 | 
					        :param resample: An optional resampling filter.  This can be
 | 
				
			||||||
           one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
 | 
					           one of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BOX`,
 | 
				
			||||||
           :py:attr:`PIL.Image.BILINEAR` (linear interpolation),
 | 
					           :py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.HAMMING`,
 | 
				
			||||||
           :py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation), or
 | 
					           :py:attr:`PIL.Image.BICUBIC` or :py:attr:`PIL.Image.LANCZOS`.
 | 
				
			||||||
           :py:attr:`PIL.Image.LANCZOS` (a high-quality downsampling filter).
 | 
					 | 
				
			||||||
           If omitted, or if the image has mode "1" or "P", it is
 | 
					           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`.
 | 
				
			||||||
        :returns: An :py:class:`~PIL.Image.Image` object.
 | 
					        :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,
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
            raise ValueError("unknown resampling filter")
 | 
					            raise ValueError("unknown resampling filter")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
| 
						 | 
					@ -1560,7 +1563,7 @@ class Image(object):
 | 
				
			||||||
           environment), or :py:attr:`PIL.Image.BICUBIC`
 | 
					           environment), or :py:attr:`PIL.Image.BICUBIC`
 | 
				
			||||||
           (cubic spline interpolation in a 4x4 environment).
 | 
					           (cubic spline interpolation in a 4x4 environment).
 | 
				
			||||||
           If omitted, or if the image has mode "1" or "P", it is
 | 
					           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
 | 
					        :param expand: Optional expansion flag.  If true, expands the output
 | 
				
			||||||
           image to make it large enough to hold the entire rotated image.
 | 
					           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
 | 
					           If false or omitted, make the output image the same size as the
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ class TestImagingResampleVulnerability(PillowTestCase):
 | 
				
			||||||
        ysize = 1000  # unimportant
 | 
					        ysize = 1000  # unimportant
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            # any resampling filter will do here
 | 
					            # 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")
 | 
					            self.fail("Resize should raise MemoryError on invalid xsize")
 | 
				
			||||||
        except MemoryError:
 | 
					        except MemoryError:
 | 
				
			||||||
            self.assertTrue(True, "Should raise MemoryError")
 | 
					            self.assertTrue(True, "Should raise MemoryError")
 | 
				
			||||||
| 
						 | 
					@ -89,6 +89,15 @@ class TestImagingCoreResampleAccuracy(PillowTestCase):
 | 
				
			||||||
            for y in range(image.size[1])
 | 
					            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):
 | 
					    def test_reduce_bilinear(self):
 | 
				
			||||||
        for mode in ['RGBX', 'RGB', 'La', 'L']:
 | 
					        for mode in ['RGBX', 'RGB', 'La', 'L']:
 | 
				
			||||||
            case = self.make_case(mode, (8, 8), 0xe1)
 | 
					            case = self.make_case(mode, (8, 8), 0xe1)
 | 
				
			||||||
| 
						 | 
					@ -98,6 +107,15 @@ class TestImagingCoreResampleAccuracy(PillowTestCase):
 | 
				
			||||||
            for channel in case.split():
 | 
					            for channel in case.split():
 | 
				
			||||||
                self.check_case(channel, self.make_sample(data, (4, 4)))
 | 
					                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):
 | 
					    def test_reduce_bicubic(self):
 | 
				
			||||||
        for mode in ['RGBX', 'RGB', 'La', 'L']:
 | 
					        for mode in ['RGBX', 'RGB', 'La', 'L']:
 | 
				
			||||||
            case = self.make_case(mode, (12, 12), 0xe1)
 | 
					            case = self.make_case(mode, (12, 12), 0xe1)
 | 
				
			||||||
| 
						 | 
					@ -119,6 +137,15 @@ class TestImagingCoreResampleAccuracy(PillowTestCase):
 | 
				
			||||||
            for channel in case.split():
 | 
					            for channel in case.split():
 | 
				
			||||||
                self.check_case(channel, self.make_sample(data, (8, 8)))
 | 
					                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):
 | 
					    def test_enlarge_bilinear(self):
 | 
				
			||||||
        for mode in ['RGBX', 'RGB', 'La', 'L']:
 | 
					        for mode in ['RGBX', 'RGB', 'La', 'L']:
 | 
				
			||||||
            case = self.make_case(mode, (2, 2), 0xe1)
 | 
					            case = self.make_case(mode, (2, 2), 0xe1)
 | 
				
			||||||
| 
						 | 
					@ -128,6 +155,17 @@ class TestImagingCoreResampleAccuracy(PillowTestCase):
 | 
				
			||||||
            for channel in case.split():
 | 
					            for channel in case.split():
 | 
				
			||||||
                self.check_case(channel, self.make_sample(data, (4, 4)))
 | 
					                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):
 | 
					    def test_enlarge_bicubic(self):
 | 
				
			||||||
        for mode in ['RGBX', 'RGB', 'La', 'L']:
 | 
					        for mode in ['RGBX', 'RGB', 'La', 'L']:
 | 
				
			||||||
            case = self.make_case(mode, (4, 4), 0xe1)
 | 
					            case = self.make_case(mode, (4, 4), 0xe1)
 | 
				
			||||||
| 
						 | 
					@ -211,14 +249,18 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
 | 
				
			||||||
    @unittest.skip("current implementation isn't precise enough")
 | 
					    @unittest.skip("current implementation isn't precise enough")
 | 
				
			||||||
    def test_levels_rgba(self):
 | 
					    def test_levels_rgba(self):
 | 
				
			||||||
        case = self.make_levels_case('RGBA')
 | 
					        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.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.BICUBIC))
 | 
				
			||||||
        self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
 | 
					        self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @unittest.skip("current implementation isn't precise enough")
 | 
					    @unittest.skip("current implementation isn't precise enough")
 | 
				
			||||||
    def test_levels_la(self):
 | 
					    def test_levels_la(self):
 | 
				
			||||||
        case = self.make_levels_case('LA')
 | 
					        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.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.BICUBIC))
 | 
				
			||||||
        self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
 | 
					        self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -243,13 +285,17 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_dirty_pixels_rgba(self):
 | 
					    def test_dirty_pixels_rgba(self):
 | 
				
			||||||
        case = self.make_dity_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0))
 | 
					        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.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.BICUBIC), (255, 255, 0))
 | 
				
			||||||
        self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0))
 | 
					        self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_dirty_pixels_la(self):
 | 
					    def test_dirty_pixels_la(self):
 | 
				
			||||||
        case = self.make_dity_case('LA', (255, 128), (0, 0))
 | 
					        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.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.BICUBIC), (255,))
 | 
				
			||||||
        self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255,))
 | 
					        self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,13 +39,15 @@ class TestImagingCoreResize(PillowTestCase):
 | 
				
			||||||
            self.assertEqual(r.im.bands, im.im.bands)
 | 
					            self.assertEqual(r.im.bands, im.im.bands)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_reduce_filters(self):
 | 
					    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)
 | 
					            r = self.resize(hopper("RGB"), (15, 12), f)
 | 
				
			||||||
            self.assertEqual(r.mode, "RGB")
 | 
					            self.assertEqual(r.mode, "RGB")
 | 
				
			||||||
            self.assertEqual(r.size, (15, 12))
 | 
					            self.assertEqual(r.size, (15, 12))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_enlarge_filters(self):
 | 
					    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)
 | 
					            r = self.resize(hopper("RGB"), (212, 195), f)
 | 
				
			||||||
            self.assertEqual(r.mode, "RGB")
 | 
					            self.assertEqual(r.mode, "RGB")
 | 
				
			||||||
            self.assertEqual(r.size, (212, 195))
 | 
					            self.assertEqual(r.size, (212, 195))
 | 
				
			||||||
| 
						 | 
					@ -64,7 +66,8 @@ class TestImagingCoreResize(PillowTestCase):
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        samples['dirty'].putpixel((1, 1), 128)
 | 
					        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
 | 
					            # samples resized with current filter
 | 
				
			||||||
            references = dict(
 | 
					            references = dict(
 | 
				
			||||||
                (name, self.resize(ch, (4, 4), f))
 | 
					                (name, self.resize(ch, (4, 4), f))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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*.
 | 
					pixel, the Python Imaging Library provides four different resampling *filters*.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
``NEAREST``
 | 
					``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``
 | 
					``BILINEAR``
 | 
				
			||||||
    For resize calculate the output pixel value using linear interpolation
 | 
					    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
 | 
					    For other transformations linear interpolation over a 2x2 environment
 | 
				
			||||||
    in the input image is used.
 | 
					    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``
 | 
					``BICUBIC``
 | 
				
			||||||
    For resize calculate the output pixel value using cubic interpolation
 | 
					    For resize calculate the output pixel value using cubic interpolation
 | 
				
			||||||
    on all pixels that may contribute to the output value.
 | 
					    on all pixels that may contribute to the output value.
 | 
				
			||||||
| 
						 | 
					@ -128,8 +145,12 @@ Filters comparison table
 | 
				
			||||||
+============+=============+===========+=============+
 | 
					+============+=============+===========+=============+
 | 
				
			||||||
|``NEAREST`` |             |           | ⭐⭐⭐⭐⭐       |
 | 
					|``NEAREST`` |             |           | ⭐⭐⭐⭐⭐       |
 | 
				
			||||||
+------------+-------------+-----------+-------------+
 | 
					+------------+-------------+-----------+-------------+
 | 
				
			||||||
 | 
					|``BOX``     | ⭐           |           | ⭐⭐⭐⭐        |
 | 
				
			||||||
 | 
					+------------+-------------+-----------+-------------+
 | 
				
			||||||
|``BILINEAR``| ⭐           | ⭐         | ⭐⭐⭐         |
 | 
					|``BILINEAR``| ⭐           | ⭐         | ⭐⭐⭐         |
 | 
				
			||||||
+------------+-------------+-----------+-------------+
 | 
					+------------+-------------+-----------+-------------+
 | 
				
			||||||
 | 
					|``HAMMING`` | ⭐⭐          |           | ⭐⭐⭐         |
 | 
				
			||||||
 | 
					+------------+-------------+-----------+-------------+
 | 
				
			||||||
|``BICUBIC`` | ⭐⭐⭐         | ⭐⭐⭐       | ⭐⭐          |
 | 
					|``BICUBIC`` | ⭐⭐⭐         | ⭐⭐⭐       | ⭐⭐          |
 | 
				
			||||||
+------------+-------------+-----------+-------------+
 | 
					+------------+-------------+-----------+-------------+
 | 
				
			||||||
|``LANCZOS`` | ⭐⭐⭐⭐        | ⭐⭐⭐⭐      | ⭐           |
 | 
					|``LANCZOS`` | ⭐⭐⭐⭐        | ⭐⭐⭐⭐      | ⭐           |
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -229,9 +229,11 @@ extern void ImagingError_Clear(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* standard filters */
 | 
					/* standard filters */
 | 
				
			||||||
#define IMAGING_TRANSFORM_NEAREST 0
 | 
					#define IMAGING_TRANSFORM_NEAREST 0
 | 
				
			||||||
#define IMAGING_TRANSFORM_LANCZOS 1
 | 
					#define IMAGING_TRANSFORM_BOX 4
 | 
				
			||||||
#define IMAGING_TRANSFORM_BILINEAR 2
 | 
					#define IMAGING_TRANSFORM_BILINEAR 2
 | 
				
			||||||
 | 
					#define IMAGING_TRANSFORM_HAMMING 5
 | 
				
			||||||
#define IMAGING_TRANSFORM_BICUBIC 3
 | 
					#define IMAGING_TRANSFORM_BICUBIC 3
 | 
				
			||||||
 | 
					#define IMAGING_TRANSFORM_LANCZOS 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef int (*ImagingTransformMap)(double* X, double* Y,
 | 
					typedef int (*ImagingTransformMap)(double* X, double* Y,
 | 
				
			||||||
                                   int x, int y, void* data);
 | 
					                                   int x, int y, void* data);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,12 +5,51 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ROUND_UP(f) ((int) ((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F))
 | 
					#define ROUND_UP(f) ((int) ((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
struct filter {
 | 
					struct filter {
 | 
				
			||||||
    double (*filter)(double x);
 | 
					    double (*filter)(double x);
 | 
				
			||||||
    double support;
 | 
					    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)
 | 
				
			||||||
 | 
					        x = -x;
 | 
				
			||||||
 | 
					    if (x < 1.0)
 | 
				
			||||||
 | 
					        return 1.0-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 */
 | 
				
			||||||
 | 
					#define a -0.5
 | 
				
			||||||
 | 
					    if (x < 0.0)
 | 
				
			||||||
 | 
					        x = -x;
 | 
				
			||||||
 | 
					    if (x < 1.0)
 | 
				
			||||||
 | 
					        return ((a + 2.0) * x - (a + 3.0)) * x*x + 1;
 | 
				
			||||||
 | 
					    if (x < 2.0)
 | 
				
			||||||
 | 
					        return (((x - 5) * x + 8) * x - 4) * a;
 | 
				
			||||||
 | 
					    return 0.0;
 | 
				
			||||||
 | 
					#undef a
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline double sinc_filter(double x)
 | 
					static inline double sinc_filter(double x)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (x == 0.0)
 | 
					    if (x == 0.0)
 | 
				
			||||||
| 
						 | 
					@ -27,33 +66,11 @@ static inline double lanczos_filter(double x)
 | 
				
			||||||
    return 0.0;
 | 
					    return 0.0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline double bilinear_filter(double x)
 | 
					static struct filter BOX = { box_filter, 0.5 };
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (x < 0.0)
 | 
					 | 
				
			||||||
        x = -x;
 | 
					 | 
				
			||||||
    if (x < 1.0)
 | 
					 | 
				
			||||||
        return 1.0-x;
 | 
					 | 
				
			||||||
    return 0.0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline double bicubic_filter(double x)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /* https://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm */
 | 
					 | 
				
			||||||
#define a -0.5
 | 
					 | 
				
			||||||
    if (x < 0.0)
 | 
					 | 
				
			||||||
        x = -x;
 | 
					 | 
				
			||||||
    if (x < 1.0)
 | 
					 | 
				
			||||||
        return ((a + 2.0) * x - (a + 3.0)) * x*x + 1;
 | 
					 | 
				
			||||||
    if (x < 2.0)
 | 
					 | 
				
			||||||
        return (((x - 5) * x + 8) * x - 4) * a;
 | 
					 | 
				
			||||||
    return 0.0;
 | 
					 | 
				
			||||||
#undef a
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct filter LANCZOS = { lanczos_filter, 3.0 };
 | 
					 | 
				
			||||||
static struct filter BILINEAR = { bilinear_filter, 1.0 };
 | 
					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 BICUBIC = { bicubic_filter, 2.0 };
 | 
				
			||||||
 | 
					static struct filter LANCZOS = { lanczos_filter, 3.0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* 8 bits for result. Filter can have negative areas.
 | 
					/* 8 bits for result. Filter can have negative areas.
 | 
				
			||||||
| 
						 | 
					@ -524,15 +541,21 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* check filter */
 | 
					    /* check filter */
 | 
				
			||||||
    switch (filter) {
 | 
					    switch (filter) {
 | 
				
			||||||
    case IMAGING_TRANSFORM_LANCZOS:
 | 
					    case IMAGING_TRANSFORM_BOX:
 | 
				
			||||||
        filterp = &LANCZOS;
 | 
					        filterp = &BOX;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case IMAGING_TRANSFORM_BILINEAR:
 | 
					    case IMAGING_TRANSFORM_BILINEAR:
 | 
				
			||||||
        filterp = &BILINEAR;
 | 
					        filterp = &BILINEAR;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					    case IMAGING_TRANSFORM_HAMMING:
 | 
				
			||||||
 | 
					        filterp = &HAMMING;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    case IMAGING_TRANSFORM_BICUBIC:
 | 
					    case IMAGING_TRANSFORM_BICUBIC:
 | 
				
			||||||
        filterp = &BICUBIC;
 | 
					        filterp = &BICUBIC;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					    case IMAGING_TRANSFORM_LANCZOS:
 | 
				
			||||||
 | 
					        filterp = &LANCZOS;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
        return (Imaging) ImagingError_ValueError(
 | 
					        return (Imaging) ImagingError_ValueError(
 | 
				
			||||||
            "unsupported resampling filter"
 | 
					            "unsupported resampling filter"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user