mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 09:26:16 +03:00
commit
e16ee15f2c
|
@ -24,7 +24,7 @@ Changelog (Pillow)
|
||||||
- Use PyQt4 if it has already been imported, otherwise prefer PyQt5. #1003
|
- Use PyQt4 if it has already been imported, otherwise prefer PyQt5. #1003
|
||||||
[AurelienBallier]
|
[AurelienBallier]
|
||||||
|
|
||||||
- Speedup stretch implementation up to 2.5 times. #977
|
- Speedup resample implementation up to 2.5 times. #977
|
||||||
[homm]
|
[homm]
|
||||||
|
|
||||||
- Speed up rotation by using cache aware loops, added transpose to rotations. #994
|
- Speed up rotation by using cache aware loops, added transpose to rotations. #994
|
||||||
|
|
37
PIL/Image.py
37
PIL/Image.py
|
@ -1523,9 +1523,8 @@ class Image:
|
||||||
(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` (use nearest neighbour),
|
||||||
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
|
:py:attr:`PIL.Image.BILINEAR` (linear interpolation),
|
||||||
environment), :py:attr:`PIL.Image.BICUBIC` (cubic spline
|
:py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation), or
|
||||||
interpolation in a 4x4 environment), or
|
|
||||||
:py:attr:`PIL.Image.ANTIALIAS` (a high-quality downsampling filter).
|
:py:attr:`PIL.Image.ANTIALIAS` (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`.
|
||||||
|
@ -1547,16 +1546,7 @@ class Image:
|
||||||
if self.mode == 'RGBA':
|
if self.mode == 'RGBA':
|
||||||
return self.convert('RGBa').resize(size, resample).convert('RGBA')
|
return self.convert('RGBa').resize(size, resample).convert('RGBA')
|
||||||
|
|
||||||
if resample == ANTIALIAS:
|
return self._new(self.im.resize(size, resample))
|
||||||
# requires stretch support (imToolkit & PIL 1.1.3)
|
|
||||||
try:
|
|
||||||
im = self.im.stretch(size, resample)
|
|
||||||
except AttributeError:
|
|
||||||
raise ValueError("unsupported resampling filter")
|
|
||||||
else:
|
|
||||||
im = self.im.resize(size, resample)
|
|
||||||
|
|
||||||
return self._new(im)
|
|
||||||
|
|
||||||
def rotate(self, angle, resample=NEAREST, expand=0):
|
def rotate(self, angle, resample=NEAREST, expand=0):
|
||||||
"""
|
"""
|
||||||
|
@ -1772,12 +1762,7 @@ class Image:
|
||||||
:py:meth:`~PIL.Image.Image.draft` method to configure the file reader
|
:py:meth:`~PIL.Image.Image.draft` method to configure the file reader
|
||||||
(where applicable), and finally resizes the image.
|
(where applicable), and finally resizes the image.
|
||||||
|
|
||||||
Note that the bilinear and bicubic filters in the current
|
Note that this function modifies the :py:class:`~PIL.Image.Image`
|
||||||
version of PIL are not well-suited for thumbnail generation.
|
|
||||||
You should use :py:attr:`PIL.Image.ANTIALIAS` unless speed is much more
|
|
||||||
important than quality.
|
|
||||||
|
|
||||||
Also note that this function modifies the :py:class:`~PIL.Image.Image`
|
|
||||||
object in place. If you need to use the full resolution image as well,
|
object in place. If you need to use the full resolution image as well,
|
||||||
apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original
|
apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original
|
||||||
image.
|
image.
|
||||||
|
@ -1785,10 +1770,9 @@ class Image:
|
||||||
:param size: Requested size.
|
:param size: Requested size.
|
||||||
:param resample: Optional resampling filter. This can be one
|
:param resample: Optional resampling filter. This can be one
|
||||||
of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`,
|
of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`,
|
||||||
:py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS`
|
:py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS`.
|
||||||
(best quality). If omitted, it defaults to
|
If omitted, it defaults to :py:attr:`PIL.Image.ANTIALIAS`.
|
||||||
:py:attr:`PIL.Image.ANTIALIAS`. (was :py:attr:`PIL.Image.NEAREST`
|
(was :py:attr:`PIL.Image.NEAREST` prior to version 2.5.0)
|
||||||
prior to version 2.5.0)
|
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1807,14 +1791,7 @@ class Image:
|
||||||
|
|
||||||
self.draft(None, size)
|
self.draft(None, size)
|
||||||
|
|
||||||
self.load()
|
|
||||||
|
|
||||||
try:
|
|
||||||
im = self.resize(size, resample)
|
im = self.resize(size, resample)
|
||||||
except ValueError:
|
|
||||||
if resample != ANTIALIAS:
|
|
||||||
raise
|
|
||||||
im = self.resize(size, NEAREST) # fallback
|
|
||||||
|
|
||||||
self.im = im.im
|
self.im = im.im
|
||||||
self.mode = im.mode
|
self.mode = im.mode
|
||||||
|
|
|
@ -1,5 +1,91 @@
|
||||||
|
"""
|
||||||
|
Tests for resize functionality.
|
||||||
|
"""
|
||||||
|
from itertools import permutations
|
||||||
|
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, hopper
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
class TestImagingCoreResize(PillowTestCase):
|
||||||
|
|
||||||
|
def resize(self, im, size, f):
|
||||||
|
# Image class independent version of resize.
|
||||||
|
im.load()
|
||||||
|
return im._new(im.im.resize(size, f))
|
||||||
|
|
||||||
|
def test_nearest_mode(self):
|
||||||
|
for mode in ["1", "P", "L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr",
|
||||||
|
"I;16"]: # exotic mode
|
||||||
|
im = hopper(mode)
|
||||||
|
r = self.resize(im, (15, 12), Image.NEAREST)
|
||||||
|
self.assertEqual(r.mode, mode)
|
||||||
|
self.assertEqual(r.size, (15, 12) )
|
||||||
|
self.assertEqual(r.im.bands, im.im.bands)
|
||||||
|
|
||||||
|
def test_convolution_modes(self):
|
||||||
|
self.assertRaises(ValueError, self.resize, hopper("1"),
|
||||||
|
(15, 12), Image.BILINEAR)
|
||||||
|
self.assertRaises(ValueError, self.resize, hopper("P"),
|
||||||
|
(15, 12), Image.BILINEAR)
|
||||||
|
self.assertRaises(ValueError, self.resize, hopper("I;16"),
|
||||||
|
(15, 12), Image.BILINEAR)
|
||||||
|
for mode in ["L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr"]:
|
||||||
|
im = hopper(mode)
|
||||||
|
r = self.resize(im, (15, 12), Image.BILINEAR)
|
||||||
|
self.assertEqual(r.mode, mode)
|
||||||
|
self.assertEqual(r.size, (15, 12) )
|
||||||
|
self.assertEqual(r.im.bands, im.im.bands)
|
||||||
|
|
||||||
|
def test_reduce_filters(self):
|
||||||
|
for f in [Image.LINEAR, Image.BILINEAR, Image.BICUBIC, Image.ANTIALIAS]:
|
||||||
|
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.ANTIALIAS]:
|
||||||
|
r = self.resize(hopper("RGB"), (212, 195), f)
|
||||||
|
self.assertEqual(r.mode, "RGB")
|
||||||
|
self.assertEqual(r.size, (212, 195))
|
||||||
|
|
||||||
|
def test_endianness(self):
|
||||||
|
# Make an image with one colored pixel, in one channel.
|
||||||
|
# When resized, that channel should be the same as a GS image.
|
||||||
|
# Other channels should be unaffected.
|
||||||
|
# The R and A channels should not swap, which is indicitive of
|
||||||
|
# an endianness issues.
|
||||||
|
|
||||||
|
samples = {
|
||||||
|
'blank': Image.new('L', (2, 2), 0),
|
||||||
|
'filled': Image.new('L', (2, 2), 255),
|
||||||
|
'dirty': Image.new('L', (2, 2), 0),
|
||||||
|
}
|
||||||
|
samples['dirty'].putpixel((1, 1), 128)
|
||||||
|
|
||||||
|
for f in [Image.LINEAR, Image.BILINEAR, Image.BICUBIC, Image.ANTIALIAS]:
|
||||||
|
# samples resized with current filter
|
||||||
|
references = dict(
|
||||||
|
(name, self.resize(ch, (4, 4), f))
|
||||||
|
for name, ch in samples.items()
|
||||||
|
)
|
||||||
|
|
||||||
|
for mode, channels_set in [
|
||||||
|
('RGB', ('blank', 'filled', 'dirty')),
|
||||||
|
('RGBA', ('blank', 'blank', 'filled', 'dirty')),
|
||||||
|
('LA', ('filled', 'dirty')),
|
||||||
|
]:
|
||||||
|
for channels in set(permutations(channels_set)):
|
||||||
|
# compile image from different channels permutations
|
||||||
|
im = Image.merge(mode, [samples[ch] for ch in channels])
|
||||||
|
resized = self.resize(im, (4, 4), f)
|
||||||
|
|
||||||
|
for i, ch in enumerate(resized.split()):
|
||||||
|
# check what resized channel in image is the same
|
||||||
|
# as separately resized channel
|
||||||
|
self.assert_image_equal(ch, references[channels[i]])
|
||||||
|
|
||||||
|
|
||||||
class TestImageResize(PillowTestCase):
|
class TestImageResize(PillowTestCase):
|
||||||
|
|
||||||
|
@ -9,8 +95,8 @@ class TestImageResize(PillowTestCase):
|
||||||
self.assertEqual(out.mode, mode)
|
self.assertEqual(out.mode, mode)
|
||||||
self.assertEqual(out.size, size)
|
self.assertEqual(out.size, size)
|
||||||
for mode in "1", "P", "L", "RGB", "I", "F":
|
for mode in "1", "P", "L", "RGB", "I", "F":
|
||||||
resize(mode, (100, 100))
|
resize(mode, (112, 103))
|
||||||
resize(mode, (200, 200))
|
resize(mode, (188, 214))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -44,7 +44,9 @@ class TestImageTransform(PillowTestCase):
|
||||||
w//2, h//2, w//2, 0),
|
w//2, h//2, w//2, 0),
|
||||||
Image.BILINEAR)
|
Image.BILINEAR)
|
||||||
|
|
||||||
scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h))
|
scaled = im.transform((w, h), Image.AFFINE,
|
||||||
|
(.5, 0, 0, 0, .5, 0),
|
||||||
|
Image.BILINEAR)
|
||||||
|
|
||||||
self.assert_image_equal(transformed, scaled)
|
self.assert_image_equal(transformed, scaled)
|
||||||
|
|
||||||
|
@ -61,9 +63,9 @@ class TestImageTransform(PillowTestCase):
|
||||||
w, h, w, 0))], # ul -> ccw around quad
|
w, h, w, 0))], # ul -> ccw around quad
|
||||||
Image.BILINEAR)
|
Image.BILINEAR)
|
||||||
|
|
||||||
# transformed.save('transformed.png')
|
scaled = im.transform((w//2, h//2), Image.AFFINE,
|
||||||
|
(2, 0, 0, 0, 2, 0),
|
||||||
scaled = im.resize((w//2, h//2), Image.BILINEAR)
|
Image.BILINEAR)
|
||||||
|
|
||||||
checker = Image.new('RGBA', im.size)
|
checker = Image.new('RGBA', im.size)
|
||||||
checker.paste(scaled, (0, 0))
|
checker.paste(scaled, (0, 0))
|
||||||
|
@ -128,7 +130,8 @@ class TestImageTransform(PillowTestCase):
|
||||||
|
|
||||||
foo = [
|
foo = [
|
||||||
Image.new('RGBA', (1024, 1024), (a, a, a, a))
|
Image.new('RGBA', (1024, 1024), (a, a, a, a))
|
||||||
for a in range(1, 65)]
|
for a in range(1, 65)
|
||||||
|
]
|
||||||
|
|
||||||
# Yeah. Watch some JIT optimize this out.
|
# Yeah. Watch some JIT optimize this out.
|
||||||
foo = None
|
foo = None
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
"""
|
|
||||||
Tests for ImagingCore.stretch functionality.
|
|
||||||
"""
|
|
||||||
from itertools import permutations
|
|
||||||
|
|
||||||
from helper import unittest, PillowTestCase
|
|
||||||
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.ppm").copy()
|
|
||||||
|
|
||||||
|
|
||||||
class TestImagingStretch(PillowTestCase):
|
|
||||||
|
|
||||||
def stretch(self, im, size, f):
|
|
||||||
return im._new(im.im.stretch(size, f))
|
|
||||||
|
|
||||||
def test_modes(self):
|
|
||||||
self.assertRaises(ValueError, im.convert("1").im.stretch,
|
|
||||||
(15, 12), Image.ANTIALIAS)
|
|
||||||
self.assertRaises(ValueError, im.convert("P").im.stretch,
|
|
||||||
(15, 12), Image.ANTIALIAS)
|
|
||||||
for mode in ["L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr"]:
|
|
||||||
s = im.convert(mode).im
|
|
||||||
r = s.stretch((15, 12), Image.ANTIALIAS)
|
|
||||||
self.assertEqual(r.mode, mode)
|
|
||||||
self.assertEqual(r.size, (15, 12))
|
|
||||||
self.assertEqual(r.bands, s.bands)
|
|
||||||
|
|
||||||
def test_reduce_filters(self):
|
|
||||||
# There is no Image.NEAREST because im.stretch implementation
|
|
||||||
# is not NEAREST for reduction. It should be removed
|
|
||||||
# or renamed to supersampling.
|
|
||||||
for f in [Image.BILINEAR, Image.BICUBIC, Image.ANTIALIAS]:
|
|
||||||
r = im.im.stretch((15, 12), f)
|
|
||||||
self.assertEqual(r.mode, "RGB")
|
|
||||||
self.assertEqual(r.size, (15, 12))
|
|
||||||
|
|
||||||
def test_enlarge_filters(self):
|
|
||||||
for f in [Image.BILINEAR, Image.BICUBIC, Image.ANTIALIAS]:
|
|
||||||
r = im.im.stretch((764, 414), f)
|
|
||||||
self.assertEqual(r.mode, "RGB")
|
|
||||||
self.assertEqual(r.size, (764, 414))
|
|
||||||
|
|
||||||
def test_endianness(self):
|
|
||||||
# Make an image with one colored pixel, in one channel.
|
|
||||||
# When stretched, that channel should be the same as a GS image.
|
|
||||||
# Other channels should be unaffected.
|
|
||||||
# The R and A channels should not swap, which is indicitive of
|
|
||||||
# an endianness issues.
|
|
||||||
|
|
||||||
samples = {
|
|
||||||
'blank': Image.new('L', (2, 2), 0),
|
|
||||||
'filled': Image.new('L', (2, 2), 255),
|
|
||||||
'dirty': Image.new('L', (2, 2), 0),
|
|
||||||
}
|
|
||||||
samples['dirty'].putpixel((1, 1), 128)
|
|
||||||
|
|
||||||
for f in [Image.BILINEAR, Image.BICUBIC, Image.ANTIALIAS]:
|
|
||||||
# samples resized with current filter
|
|
||||||
resized = dict(
|
|
||||||
(name, self.stretch(ch, (4, 4), f))
|
|
||||||
for name, ch in samples.items()
|
|
||||||
)
|
|
||||||
|
|
||||||
for mode, channels_set in [
|
|
||||||
('RGB', ('blank', 'filled', 'dirty')),
|
|
||||||
('RGBA', ('blank', 'blank', 'filled', 'dirty')),
|
|
||||||
('LA', ('filled', 'dirty')),
|
|
||||||
]:
|
|
||||||
for channels in set(permutations(channels_set)):
|
|
||||||
# compile image from different channels permutations
|
|
||||||
im = Image.merge(mode, [samples[ch] for ch in channels])
|
|
||||||
stretched = self.stretch(im, (4, 4), f)
|
|
||||||
|
|
||||||
for i, ch in enumerate(stretched.split()):
|
|
||||||
# check what resized channel in image is the same
|
|
||||||
# as separately resized channel
|
|
||||||
self.assert_image_equal(ch, resized[channels[i]])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
||||||
|
|
||||||
# End of file
|
|
44
_imaging.c
44
_imaging.c
|
@ -1514,9 +1514,26 @@ _resize(ImagingObject* self, PyObject* args)
|
||||||
|
|
||||||
imIn = self->image;
|
imIn = self->image;
|
||||||
|
|
||||||
|
if (imIn->xsize == xsize && imIn->ysize == ysize) {
|
||||||
|
imOut = ImagingCopy(imIn);
|
||||||
|
}
|
||||||
|
else if ( ! filter) {
|
||||||
|
double a[6];
|
||||||
|
|
||||||
|
memset(a, 0, sizeof a);
|
||||||
|
a[1] = (double) imIn->xsize / xsize;
|
||||||
|
a[5] = (double) imIn->ysize / ysize;
|
||||||
|
|
||||||
imOut = ImagingNew(imIn->mode, xsize, ysize);
|
imOut = ImagingNew(imIn->mode, xsize, ysize);
|
||||||
if (imOut)
|
|
||||||
(void) ImagingResize(imOut, imIn, filter);
|
imOut = ImagingTransformAffine(
|
||||||
|
imOut, imIn,
|
||||||
|
0, 0, xsize, ysize,
|
||||||
|
a, filter, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
imOut = ImagingResample(imIn, xsize, ysize, filter);
|
||||||
|
}
|
||||||
|
|
||||||
return PyImagingNew(imOut);
|
return PyImagingNew(imOut);
|
||||||
}
|
}
|
||||||
|
@ -1610,25 +1627,6 @@ im_setmode(ImagingObject* self, PyObject* args)
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
_stretch(ImagingObject* self, PyObject* args)
|
|
||||||
{
|
|
||||||
Imaging imIn, imOut;
|
|
||||||
|
|
||||||
int xsize, ysize;
|
|
||||||
int filter = IMAGING_TRANSFORM_NEAREST;
|
|
||||||
if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
imIn = self->image;
|
|
||||||
|
|
||||||
imOut = ImagingStretch(imIn, xsize, ysize, filter);
|
|
||||||
if ( ! imOut) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PyImagingNew(imOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_transform2(ImagingObject* self, PyObject* args)
|
_transform2(ImagingObject* self, PyObject* args)
|
||||||
|
@ -3031,8 +3029,10 @@ static struct PyMethodDef methods[] = {
|
||||||
{"rankfilter", (PyCFunction)_rankfilter, 1},
|
{"rankfilter", (PyCFunction)_rankfilter, 1},
|
||||||
#endif
|
#endif
|
||||||
{"resize", (PyCFunction)_resize, 1},
|
{"resize", (PyCFunction)_resize, 1},
|
||||||
|
// There were two methods for image resize before.
|
||||||
|
// Starting from Pillow 2.7.0 stretch is depreciated.
|
||||||
|
{"stretch", (PyCFunction)_resize, 1},
|
||||||
{"rotate", (PyCFunction)_rotate, 1},
|
{"rotate", (PyCFunction)_rotate, 1},
|
||||||
{"stretch", (PyCFunction)_stretch, 1},
|
|
||||||
{"transpose", (PyCFunction)_transpose, 1},
|
{"transpose", (PyCFunction)_transpose, 1},
|
||||||
{"transform2", (PyCFunction)_transform2, 1},
|
{"transform2", (PyCFunction)_transform2, 1},
|
||||||
|
|
||||||
|
|
|
@ -89,25 +89,21 @@ pixel, the Python Imaging Library provides four different resampling *filters*.
|
||||||
Pick the nearest pixel from the input image. Ignore all other input pixels.
|
Pick the nearest pixel from the input image. Ignore all other input pixels.
|
||||||
|
|
||||||
``BILINEAR``
|
``BILINEAR``
|
||||||
Use linear interpolation over a 2x2 environment in the input image. Note
|
For resize calculate the output pixel value using linear interpolation
|
||||||
that in the current version of PIL, this filter uses a fixed input
|
on all pixels that may contribute to the output value.
|
||||||
environment when downsampling.
|
For other transformations linear interpolation over a 2x2 environment
|
||||||
|
in the input image is used.
|
||||||
|
|
||||||
``BICUBIC``
|
``BICUBIC``
|
||||||
Use cubic interpolation over a 4x4 environment in the input image. Note
|
For resize calculate the output pixel value using cubic interpolation
|
||||||
that in the current version of PIL, this filter uses a fixed input
|
on all pixels that may contribute to the output value.
|
||||||
environment when downsampling.
|
For other transformations cubic interpolation over a 4x4 environment
|
||||||
|
in the input image is used.
|
||||||
|
|
||||||
``ANTIALIAS``
|
``ANTIALIAS``
|
||||||
Calculate the output pixel value using a high-quality resampling filter (a
|
Calculate the output pixel value using a high-quality Lanczos filter (a
|
||||||
truncated sinc) on all pixels that may contribute to the output value. In
|
truncated sinc) on all pixels that may contribute to the output value. In
|
||||||
the current version of PIL, this filter can only be used with the resize
|
the current version of PIL, this filter can only be used with the resize
|
||||||
and thumbnail methods.
|
and thumbnail methods.
|
||||||
|
|
||||||
.. versionadded:: 1.1.3
|
.. versionadded:: 1.1.3
|
||||||
|
|
||||||
Note that in the current version of PIL, the ``ANTIALIAS`` filter is the only
|
|
||||||
filter that behaves properly when downsampling (that is, when converting a
|
|
||||||
large image to a small one). The ``BILINEAR`` and ``BICUBIC`` filters use a
|
|
||||||
fixed input environment, and are best used for scale-preserving geometric
|
|
||||||
transforms and upsamping.
|
|
||||||
|
|
|
@ -979,30 +979,6 @@ ImagingTransformQuad(Imaging imOut, Imaging imIn,
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* Convenience functions */
|
/* Convenience functions */
|
||||||
|
|
||||||
Imaging
|
|
||||||
ImagingResize(Imaging imOut, Imaging imIn, int filterid)
|
|
||||||
{
|
|
||||||
double a[6];
|
|
||||||
|
|
||||||
if (imOut->xsize == imIn->xsize && imOut->ysize == imIn->ysize)
|
|
||||||
return ImagingCopy2(imOut, imIn);
|
|
||||||
|
|
||||||
memset(a, 0, sizeof a);
|
|
||||||
a[1] = (double) imIn->xsize / imOut->xsize;
|
|
||||||
a[5] = (double) imIn->ysize / imOut->ysize;
|
|
||||||
|
|
||||||
if (!filterid && imIn->type != IMAGING_TYPE_SPECIAL)
|
|
||||||
return ImagingScaleAffine(
|
|
||||||
imOut, imIn,
|
|
||||||
0, 0, imOut->xsize, imOut->ysize,
|
|
||||||
a, 1);
|
|
||||||
|
|
||||||
return ImagingTransformAffine(
|
|
||||||
imOut, imIn,
|
|
||||||
0, 0, imOut->xsize, imOut->ysize,
|
|
||||||
a, filterid, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Imaging
|
Imaging
|
||||||
ImagingRotate(Imaging imOut, Imaging imIn, double theta, int filterid)
|
ImagingRotate(Imaging imOut, Imaging imIn, double theta, int filterid)
|
||||||
{
|
{
|
||||||
|
|
|
@ -286,13 +286,12 @@ extern Imaging ImagingPointTransform(
|
||||||
Imaging imIn, double scale, double offset);
|
Imaging imIn, double scale, double offset);
|
||||||
extern Imaging ImagingPutBand(Imaging im, Imaging imIn, int band);
|
extern Imaging ImagingPutBand(Imaging im, Imaging imIn, int band);
|
||||||
extern Imaging ImagingRankFilter(Imaging im, int size, int rank);
|
extern Imaging ImagingRankFilter(Imaging im, int size, int rank);
|
||||||
extern Imaging ImagingResize(Imaging imOut, Imaging imIn, int filter);
|
|
||||||
extern Imaging ImagingRotate(
|
extern Imaging ImagingRotate(
|
||||||
Imaging imOut, Imaging imIn, double theta, int filter);
|
Imaging imOut, Imaging imIn, double theta, int filter);
|
||||||
extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn);
|
extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn);
|
||||||
extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn);
|
extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn);
|
||||||
extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn);
|
extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn);
|
||||||
extern Imaging ImagingStretch(Imaging imIn, int xsize, int ysize, int filter);
|
extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter);
|
||||||
extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn);
|
extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn);
|
||||||
extern Imaging ImagingTransposeToNew(Imaging imIn);
|
extern Imaging ImagingTransposeToNew(Imaging imIn);
|
||||||
extern Imaging ImagingTransformPerspective(
|
extern Imaging ImagingTransformPerspective(
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* The Python Imaging Library
|
* The Python Imaging Library
|
||||||
* $Id$
|
* $Id$
|
||||||
*
|
*
|
||||||
* pilopen antialiasing support
|
* Pillow image resamling support
|
||||||
*
|
*
|
||||||
* history:
|
* history:
|
||||||
* 2002-03-09 fl Created (for PIL 1.1.3)
|
* 2002-03-09 fl Created (for PIL 1.1.3)
|
||||||
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
/* resampling filters (from antialias.py) */
|
|
||||||
|
|
||||||
struct filter {
|
struct filter {
|
||||||
float (*filter)(float x);
|
float (*filter)(float x);
|
||||||
float support;
|
float support;
|
||||||
|
@ -42,15 +40,6 @@ static inline float antialias_filter(float x)
|
||||||
|
|
||||||
static struct filter ANTIALIAS = { antialias_filter, 3.0 };
|
static struct filter ANTIALIAS = { antialias_filter, 3.0 };
|
||||||
|
|
||||||
static inline float nearest_filter(float x)
|
|
||||||
{
|
|
||||||
if (-0.5 <= x && x < 0.5)
|
|
||||||
return 1.0;
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct filter NEAREST = { nearest_filter, 0.5 };
|
|
||||||
|
|
||||||
static inline float bilinear_filter(float x)
|
static inline float bilinear_filter(float x)
|
||||||
{
|
{
|
||||||
if (x < 0.0)
|
if (x < 0.0)
|
||||||
|
@ -106,7 +95,7 @@ static float inline i2f(int v) { return (float) v; }
|
||||||
|
|
||||||
|
|
||||||
Imaging
|
Imaging
|
||||||
ImagingStretchHorizontal(Imaging imIn, int xsize, int filter)
|
ImagingResampleHorizontal(Imaging imIn, int xsize, int filter)
|
||||||
{
|
{
|
||||||
ImagingSectionCookie cookie;
|
ImagingSectionCookie cookie;
|
||||||
Imaging imOut;
|
Imaging imOut;
|
||||||
|
@ -119,9 +108,6 @@ ImagingStretchHorizontal(Imaging imIn, int xsize, int filter)
|
||||||
|
|
||||||
/* check filter */
|
/* check filter */
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
case IMAGING_TRANSFORM_NEAREST:
|
|
||||||
filterp = &NEAREST;
|
|
||||||
break;
|
|
||||||
case IMAGING_TRANSFORM_ANTIALIAS:
|
case IMAGING_TRANSFORM_ANTIALIAS:
|
||||||
filterp = &ANTIALIAS;
|
filterp = &ANTIALIAS;
|
||||||
break;
|
break;
|
||||||
|
@ -152,7 +138,7 @@ ImagingStretchHorizontal(Imaging imIn, int xsize, int filter)
|
||||||
/* maximum number of coofs */
|
/* maximum number of coofs */
|
||||||
kmax = (int) ceil(support) * 2 + 1;
|
kmax = (int) ceil(support) * 2 + 1;
|
||||||
|
|
||||||
/* coefficient buffer (with rounding safety margin) */
|
/* coefficient buffer */
|
||||||
kk = malloc(xsize * kmax * sizeof(float));
|
kk = malloc(xsize * kmax * sizeof(float));
|
||||||
if ( ! kk)
|
if ( ! kk)
|
||||||
return (Imaging) ImagingError_MemoryError();
|
return (Imaging) ImagingError_MemoryError();
|
||||||
|
@ -208,7 +194,7 @@ ImagingStretchHorizontal(Imaging imIn, int xsize, int filter)
|
||||||
ss += i2f(imIn->image8[yy][x]) * k[x - xmin];
|
ss += i2f(imIn->image8[yy][x]) * k[x - xmin];
|
||||||
imOut->image8[yy][xx] = clip8(ss);
|
imOut->image8[yy][xx] = clip8(ss);
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
switch(imIn->type) {
|
switch(imIn->type) {
|
||||||
case IMAGING_TYPE_UINT8:
|
case IMAGING_TYPE_UINT8:
|
||||||
/* n-bit grayscale */
|
/* n-bit grayscale */
|
||||||
|
@ -283,12 +269,7 @@ ImagingStretchHorizontal(Imaging imIn, int xsize, int filter)
|
||||||
IMAGING_PIXEL_F(imOut, xx, yy) = ss;
|
IMAGING_PIXEL_F(imOut, xx, yy) = ss;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
ImagingSectionLeave(&cookie);
|
|
||||||
ImagingDelete(imOut);
|
|
||||||
free(kk);
|
|
||||||
free(xbounds);
|
|
||||||
return (Imaging) ImagingError_ModeError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImagingSectionLeave(&cookie);
|
ImagingSectionLeave(&cookie);
|
||||||
|
@ -299,7 +280,7 @@ ImagingStretchHorizontal(Imaging imIn, int xsize, int filter)
|
||||||
|
|
||||||
|
|
||||||
Imaging
|
Imaging
|
||||||
ImagingStretch(Imaging imIn, int xsize, int ysize, int filter)
|
ImagingResample(Imaging imIn, int xsize, int ysize, int filter)
|
||||||
{
|
{
|
||||||
Imaging imTemp1, imTemp2, imTemp3;
|
Imaging imTemp1, imTemp2, imTemp3;
|
||||||
Imaging imOut;
|
Imaging imOut;
|
||||||
|
@ -307,8 +288,11 @@ ImagingStretch(Imaging imIn, int xsize, int ysize, int filter)
|
||||||
if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0)
|
if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0)
|
||||||
return (Imaging) ImagingError_ModeError();
|
return (Imaging) ImagingError_ModeError();
|
||||||
|
|
||||||
|
if (imIn->type == IMAGING_TYPE_SPECIAL)
|
||||||
|
return (Imaging) ImagingError_ModeError();
|
||||||
|
|
||||||
/* two-pass resize, first pass */
|
/* two-pass resize, first pass */
|
||||||
imTemp1 = ImagingStretchHorizontal(imIn, xsize, filter);
|
imTemp1 = ImagingResampleHorizontal(imIn, xsize, filter);
|
||||||
if ( ! imTemp1)
|
if ( ! imTemp1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -319,7 +303,7 @@ ImagingStretch(Imaging imIn, int xsize, int ysize, int filter)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* second pass */
|
/* second pass */
|
||||||
imTemp3 = ImagingStretchHorizontal(imTemp2, ysize, filter);
|
imTemp3 = ImagingResampleHorizontal(imTemp2, ysize, filter);
|
||||||
ImagingDelete(imTemp2);
|
ImagingDelete(imTemp2);
|
||||||
if ( ! imTemp3)
|
if ( ! imTemp3)
|
||||||
return NULL;
|
return NULL;
|
2
setup.py
2
setup.py
|
@ -26,7 +26,7 @@ _IMAGING = (
|
||||||
"decode", "encode", "map", "display", "outline", "path")
|
"decode", "encode", "map", "display", "outline", "path")
|
||||||
|
|
||||||
_LIB_IMAGING = (
|
_LIB_IMAGING = (
|
||||||
"Access", "AlphaComposite", "Antialias", "Bands", "BitDecode", "Blend",
|
"Access", "AlphaComposite", "Resample", "Bands", "BitDecode", "Blend",
|
||||||
"Chops", "Convert", "ConvertYCbCr", "Copy", "Crc32", "Crop", "Dib", "Draw",
|
"Chops", "Convert", "ConvertYCbCr", "Copy", "Crc32", "Crop", "Dib", "Draw",
|
||||||
"Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode",
|
"Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode",
|
||||||
"Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode",
|
"Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user