mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 09:14:27 +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