mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-04-25 19:43:45 +03:00
Merge pull request #821 from wiredfool/pypy-performance
PyPy performance improvements
This commit is contained in:
commit
58c56e9aa4
|
@ -124,7 +124,8 @@ class PillowTestCase(unittest.TestCase):
|
||||||
self.assertTrue(found)
|
self.assertTrue(found)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def skipKnownBadTest(self, msg=None, platform=None, travis=None):
|
def skipKnownBadTest(self, msg=None, platform=None,
|
||||||
|
travis=None, interpreter=None):
|
||||||
# Skip if platform/travis matches, and
|
# Skip if platform/travis matches, and
|
||||||
# PILLOW_RUN_KNOWN_BAD is not true in the environment.
|
# PILLOW_RUN_KNOWN_BAD is not true in the environment.
|
||||||
if bool(os.environ.get('PILLOW_RUN_KNOWN_BAD', False)):
|
if bool(os.environ.get('PILLOW_RUN_KNOWN_BAD', False)):
|
||||||
|
@ -136,6 +137,8 @@ class PillowTestCase(unittest.TestCase):
|
||||||
skip = sys.platform.startswith(platform)
|
skip = sys.platform.startswith(platform)
|
||||||
if travis is not None:
|
if travis is not None:
|
||||||
skip = skip and (travis == bool(os.environ.get('TRAVIS', False)))
|
skip = skip and (travis == bool(os.environ.get('TRAVIS', False)))
|
||||||
|
if interpreter is not None:
|
||||||
|
skip = skip and (interpreter == 'pypy' and hasattr(sys, 'pypy_version_info'))
|
||||||
if skip:
|
if skip:
|
||||||
self.skipTest(msg or "Known Bad Test")
|
self.skipTest(msg or "Known Bad Test")
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,6 @@ import sys
|
||||||
|
|
||||||
class TestImagePoint(PillowTestCase):
|
class TestImagePoint(PillowTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
if hasattr(sys, 'pypy_version_info'):
|
|
||||||
# This takes _forever_ on PyPy. Open Bug,
|
|
||||||
# see https://github.com/python-pillow/Pillow/issues/484
|
|
||||||
self.skipTest("Too slow on PyPy")
|
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = lena()
|
im = lena()
|
||||||
|
|
||||||
|
@ -30,10 +24,23 @@ class TestImagePoint(PillowTestCase):
|
||||||
""" Tests for 16 bit -> 8 bit lut for converting I->L images
|
""" Tests for 16 bit -> 8 bit lut for converting I->L images
|
||||||
see https://github.com/python-pillow/Pillow/issues/440
|
see https://github.com/python-pillow/Pillow/issues/440
|
||||||
"""
|
"""
|
||||||
|
# This takes _forever_ on PyPy. Open Bug,
|
||||||
|
# see https://github.com/python-pillow/Pillow/issues/484
|
||||||
|
#self.skipKnownBadTest(msg="Too Slow on pypy", interpreter='pypy')
|
||||||
|
|
||||||
im = lena("I")
|
im = lena("I")
|
||||||
im.point(list(range(256))*256, 'L')
|
im.point(list(range(256))*256, 'L')
|
||||||
|
|
||||||
|
def test_f_lut(self):
|
||||||
|
""" Tests for floating point lut of 8bit gray image """
|
||||||
|
im = lena('L')
|
||||||
|
lut = [0.5 * float(x) for x in range(256)]
|
||||||
|
|
||||||
|
out = im.point(lut, 'F')
|
||||||
|
|
||||||
|
int_lut = [x//2 for x in range(256)]
|
||||||
|
self.assert_image_equal(out.convert('L'), im.point(int_lut, 'L'))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -42,6 +42,30 @@ class TestImagePutData(PillowTestCase):
|
||||||
self.assertEqual(put(sys.maxsize), (255, 255, 255, 127))
|
self.assertEqual(put(sys.maxsize), (255, 255, 255, 127))
|
||||||
|
|
||||||
|
|
||||||
|
def test_pypy_performance(self):
|
||||||
|
im = Image.new('L', (256,256))
|
||||||
|
im.putdata(list(range(256))*256)
|
||||||
|
|
||||||
|
def test_mode_i(self):
|
||||||
|
src = lena('L')
|
||||||
|
data = list(src.getdata())
|
||||||
|
im = Image.new('I', src.size, 0)
|
||||||
|
im.putdata(data, 2, 256)
|
||||||
|
|
||||||
|
target = [2* elt + 256 for elt in data]
|
||||||
|
self.assertEqual(list(im.getdata()), target)
|
||||||
|
|
||||||
|
def test_mode_F(self):
|
||||||
|
src = lena('L')
|
||||||
|
data = list(src.getdata())
|
||||||
|
im = Image.new('F', src.size, 0)
|
||||||
|
im.putdata(data, 2.0, 256.0)
|
||||||
|
|
||||||
|
target = [2.0* float(elt) + 256.0 for elt in data]
|
||||||
|
self.assertEqual(list(im.getdata()), target)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
|
|
|
@ -5,18 +5,22 @@ from PIL import Image
|
||||||
|
|
||||||
class TestModeI16(PillowTestCase):
|
class TestModeI16(PillowTestCase):
|
||||||
|
|
||||||
|
original = lena().resize((32,32)).convert('I')
|
||||||
|
|
||||||
def verify(self, im1):
|
def verify(self, im1):
|
||||||
im2 = lena("I")
|
im2 = self.original.copy()
|
||||||
self.assertEqual(im1.size, im2.size)
|
self.assertEqual(im1.size, im2.size)
|
||||||
pix1 = im1.load()
|
pix1 = im1.load()
|
||||||
pix2 = im2.load()
|
pix2 = im2.load()
|
||||||
for y in range(im1.size[1]):
|
for y in range(im1.size[1]):
|
||||||
for x in range(im1.size[0]):
|
for x in range(im1.size[0]):
|
||||||
xy = x, y
|
xy = x, y
|
||||||
|
p1 = pix1[xy]
|
||||||
|
p2 = pix2[xy]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
pix1[xy], pix2[xy],
|
p1, p2,
|
||||||
("got %r from mode %s at %s, expected %r" %
|
("got %r from mode %s at %s, expected %r" %
|
||||||
(pix1[xy], im1.mode, xy, pix2[xy])))
|
(p1, im1.mode, xy, p2)))
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
# PIL 1.1 has limited support for 16-bit image data. Check that
|
# PIL 1.1 has limited support for 16-bit image data. Check that
|
||||||
|
@ -24,7 +28,7 @@ class TestModeI16(PillowTestCase):
|
||||||
|
|
||||||
def basic(mode):
|
def basic(mode):
|
||||||
|
|
||||||
imIn = lena("I").convert(mode)
|
imIn = self.original.convert(mode)
|
||||||
self.verify(imIn)
|
self.verify(imIn)
|
||||||
|
|
||||||
w, h = imIn.size
|
w, h = imIn.size
|
||||||
|
@ -92,7 +96,7 @@ class TestModeI16(PillowTestCase):
|
||||||
|
|
||||||
def test_convert(self):
|
def test_convert(self):
|
||||||
|
|
||||||
im = lena("I")
|
im = self.original.copy()
|
||||||
|
|
||||||
self.verify(im.convert("I;16"))
|
self.verify(im.convert("I;16"))
|
||||||
self.verify(im.convert("I;16").convert("L"))
|
self.verify(im.convert("I;16").convert("L"))
|
||||||
|
|
140
_imaging.c
140
_imaging.c
|
@ -365,8 +365,11 @@ getbands(const char* mode)
|
||||||
static void*
|
static void*
|
||||||
getlist(PyObject* arg, int* length, const char* wrong_length, int type)
|
getlist(PyObject* arg, int* length, const char* wrong_length, int type)
|
||||||
{
|
{
|
||||||
int i, n;
|
int i, n, itemp;
|
||||||
|
double dtemp;
|
||||||
void* list;
|
void* list;
|
||||||
|
PyObject* seq;
|
||||||
|
PyObject* op;
|
||||||
|
|
||||||
if (!PySequence_Check(arg)) {
|
if (!PySequence_Check(arg)) {
|
||||||
PyErr_SetString(PyExc_TypeError, must_be_sequence);
|
PyErr_SetString(PyExc_TypeError, must_be_sequence);
|
||||||
|
@ -383,72 +386,36 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type)
|
||||||
if (!list)
|
if (!list)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
|
||||||
|
seq = PySequence_Fast(arg, must_be_sequence);
|
||||||
|
if (!seq) {
|
||||||
|
free(list);
|
||||||
|
PyErr_SetString(PyExc_TypeError, must_be_sequence);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
op = PySequence_Fast_GET_ITEM(seq, i);
|
||||||
|
// DRY, branch prediction is going to work _really_ well
|
||||||
|
// on this switch. And 3 fewer loops to copy/paste.
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TYPE_UINT8:
|
case TYPE_UINT8:
|
||||||
if (PyList_Check(arg)) {
|
itemp = PyInt_AsLong(op);
|
||||||
for (i = 0; i < n; i++) {
|
((UINT8*)list)[i] = CLIP(itemp);
|
||||||
PyObject *op = PyList_GET_ITEM(arg, i);
|
|
||||||
int temp = PyInt_AsLong(op);
|
|
||||||
((UINT8*)list)[i] = CLIP(temp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
PyObject *op = PySequence_GetItem(arg, i);
|
|
||||||
int temp = PyInt_AsLong(op);
|
|
||||||
Py_XDECREF(op);
|
|
||||||
((UINT8*)list)[i] = CLIP(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case TYPE_INT32:
|
case TYPE_INT32:
|
||||||
if (PyList_Check(arg)) {
|
itemp = PyInt_AsLong(op);
|
||||||
for (i = 0; i < n; i++) {
|
((INT32*)list)[i] = itemp;
|
||||||
PyObject *op = PyList_GET_ITEM(arg, i);
|
|
||||||
int temp = PyInt_AsLong(op);
|
|
||||||
((INT32*)list)[i] = temp;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
PyObject *op = PySequence_GetItem(arg, i);
|
|
||||||
int temp = PyInt_AsLong(op);
|
|
||||||
Py_XDECREF(op);
|
|
||||||
((INT32*)list)[i] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case TYPE_FLOAT32:
|
case TYPE_FLOAT32:
|
||||||
if (PyList_Check(arg)) {
|
dtemp = PyFloat_AsDouble(op);
|
||||||
for (i = 0; i < n; i++) {
|
((FLOAT32*)list)[i] = (FLOAT32) dtemp;
|
||||||
PyObject *op = PyList_GET_ITEM(arg, i);
|
|
||||||
double temp = PyFloat_AsDouble(op);
|
|
||||||
((FLOAT32*)list)[i] = (FLOAT32) temp;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
PyObject *op = PySequence_GetItem(arg, i);
|
|
||||||
double temp = PyFloat_AsDouble(op);
|
|
||||||
Py_XDECREF(op);
|
|
||||||
((FLOAT32*)list)[i] = (FLOAT32) temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case TYPE_DOUBLE:
|
case TYPE_DOUBLE:
|
||||||
if (PyList_Check(arg)) {
|
dtemp = PyFloat_AsDouble(op);
|
||||||
for (i = 0; i < n; i++) {
|
((double*)list)[i] = (double) dtemp;
|
||||||
PyObject *op = PyList_GET_ITEM(arg, i);
|
|
||||||
double temp = PyFloat_AsDouble(op);
|
|
||||||
((double*)list)[i] = temp;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
PyObject *op = PySequence_GetItem(arg, i);
|
|
||||||
double temp = PyFloat_AsDouble(op);
|
|
||||||
Py_XDECREF(op);
|
|
||||||
((double*)list)[i] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (length)
|
if (length)
|
||||||
*length = n;
|
*length = n;
|
||||||
|
@ -1253,6 +1220,8 @@ _putdata(ImagingObject* self, PyObject* args)
|
||||||
Py_ssize_t n, i, x, y;
|
Py_ssize_t n, i, x, y;
|
||||||
|
|
||||||
PyObject* data;
|
PyObject* data;
|
||||||
|
PyObject* seq;
|
||||||
|
PyObject* op;
|
||||||
double scale = 1.0;
|
double scale = 1.0;
|
||||||
double offset = 0.0;
|
double offset = 0.0;
|
||||||
|
|
||||||
|
@ -1292,41 +1261,28 @@ _putdata(ImagingObject* self, PyObject* args)
|
||||||
x = 0, y++;
|
x = 0, y++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
seq = PySequence_Fast(data, must_be_sequence);
|
||||||
|
if (!seq) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, must_be_sequence);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (scale == 1.0 && offset == 0.0) {
|
if (scale == 1.0 && offset == 0.0) {
|
||||||
/* Clipped data */
|
/* Clipped data */
|
||||||
if (PyList_Check(data)) {
|
|
||||||
for (i = x = y = 0; i < n; i++) {
|
for (i = x = y = 0; i < n; i++) {
|
||||||
PyObject *op = PyList_GET_ITEM(data, i);
|
op = PySequence_Fast_GET_ITEM(data, i);
|
||||||
image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op));
|
image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op));
|
||||||
if (++x >= (int) image->xsize)
|
if (++x >= (int) image->xsize){
|
||||||
x = 0, y++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = x = y = 0; i < n; i++) {
|
|
||||||
PyObject *op = PySequence_GetItem(data, i);
|
|
||||||
image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op));
|
|
||||||
Py_XDECREF(op);
|
|
||||||
if (++x >= (int) image->xsize)
|
|
||||||
x = 0, y++;
|
x = 0, y++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (PyList_Check(data)) {
|
|
||||||
/* Scaled and clipped data */
|
/* Scaled and clipped data */
|
||||||
for (i = x = y = 0; i < n; i++) {
|
for (i = x = y = 0; i < n; i++) {
|
||||||
PyObject *op = PyList_GET_ITEM(data, i);
|
PyObject *op = PySequence_Fast_GET_ITEM(data, i);
|
||||||
image->image8[y][x] = CLIP(
|
image->image8[y][x] = CLIP(
|
||||||
(int) (PyFloat_AsDouble(op) * scale + offset));
|
(int) (PyFloat_AsDouble(op) * scale + offset));
|
||||||
if (++x >= (int) image->xsize)
|
if (++x >= (int) image->xsize){
|
||||||
x = 0, y++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = x = y = 0; i < n; i++) {
|
|
||||||
PyObject *op = PySequence_GetItem(data, i);
|
|
||||||
image->image8[y][x] = CLIP(
|
|
||||||
(int) (PyFloat_AsDouble(op) * scale + offset));
|
|
||||||
Py_XDECREF(op);
|
|
||||||
if (++x >= (int) image->xsize)
|
|
||||||
x = 0, y++;
|
x = 0, y++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1335,27 +1291,32 @@ _putdata(ImagingObject* self, PyObject* args)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* 32-bit images */
|
/* 32-bit images */
|
||||||
|
seq = PySequence_Fast(data, must_be_sequence);
|
||||||
|
if (!seq) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, must_be_sequence);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
switch (image->type) {
|
switch (image->type) {
|
||||||
case IMAGING_TYPE_INT32:
|
case IMAGING_TYPE_INT32:
|
||||||
for (i = x = y = 0; i < n; i++) {
|
for (i = x = y = 0; i < n; i++) {
|
||||||
PyObject *op = PySequence_GetItem(data, i);
|
op = PySequence_Fast_GET_ITEM(data, i);
|
||||||
IMAGING_PIXEL_INT32(image, x, y) =
|
IMAGING_PIXEL_INT32(image, x, y) =
|
||||||
(INT32) (PyFloat_AsDouble(op) * scale + offset);
|
(INT32) (PyFloat_AsDouble(op) * scale + offset);
|
||||||
Py_XDECREF(op);
|
if (++x >= (int) image->xsize){
|
||||||
if (++x >= (int) image->xsize)
|
|
||||||
x = 0, y++;
|
x = 0, y++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
PyErr_Clear(); /* Avoid weird exceptions */
|
PyErr_Clear(); /* Avoid weird exceptions */
|
||||||
break;
|
break;
|
||||||
case IMAGING_TYPE_FLOAT32:
|
case IMAGING_TYPE_FLOAT32:
|
||||||
for (i = x = y = 0; i < n; i++) {
|
for (i = x = y = 0; i < n; i++) {
|
||||||
PyObject *op = PySequence_GetItem(data, i);
|
op = PySequence_Fast_GET_ITEM(data, i);
|
||||||
IMAGING_PIXEL_FLOAT32(image, x, y) =
|
IMAGING_PIXEL_FLOAT32(image, x, y) =
|
||||||
(FLOAT32) (PyFloat_AsDouble(op) * scale + offset);
|
(FLOAT32) (PyFloat_AsDouble(op) * scale + offset);
|
||||||
Py_XDECREF(op);
|
if (++x >= (int) image->xsize){
|
||||||
if (++x >= (int) image->xsize)
|
|
||||||
x = 0, y++;
|
x = 0, y++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
PyErr_Clear(); /* Avoid weird exceptions */
|
PyErr_Clear(); /* Avoid weird exceptions */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1365,17 +1326,16 @@ _putdata(ImagingObject* self, PyObject* args)
|
||||||
INT32 inkint;
|
INT32 inkint;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
PyObject *op = PySequence_GetItem(data, i);
|
op = PySequence_Fast_GET_ITEM(data, i);
|
||||||
if (!op || !getink(op, image, u.ink)) {
|
if (!op || !getink(op, image, u.ink)) {
|
||||||
Py_DECREF(op);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* FIXME: what about scale and offset? */
|
/* FIXME: what about scale and offset? */
|
||||||
image->image32[y][x] = u.inkint;
|
image->image32[y][x] = u.inkint;
|
||||||
Py_XDECREF(op);
|
if (++x >= (int) image->xsize){
|
||||||
if (++x >= (int) image->xsize)
|
|
||||||
x = 0, y++;
|
x = 0, y++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
PyErr_Clear(); /* Avoid weird exceptions */
|
PyErr_Clear(); /* Avoid weird exceptions */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,5 @@ if len(sys.argv) == 1:
|
||||||
if ('--no-path-adjustment' not in sys.argv) and ('-P' not in sys.argv):
|
if ('--no-path-adjustment' not in sys.argv) and ('-P' not in sys.argv):
|
||||||
sys.argv.insert(1, '--no-path-adjustment')
|
sys.argv.insert(1, '--no-path-adjustment')
|
||||||
|
|
||||||
if 'NOSE_PROCESSES' not in os.environ:
|
|
||||||
for arg in sys.argv:
|
|
||||||
if '--processes' in arg:
|
|
||||||
break
|
|
||||||
else: # for
|
|
||||||
sys.argv.insert(1, '--processes=-1') # -1 == number of cores
|
|
||||||
sys.argv.insert(1, '--process-timeout=30')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
profile.run("nose.main()", sort=2)
|
profile.run("nose.main()", sort=2)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user