mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-02-03 13:14:27 +03:00
Merge pull request #2730 from uploadcare/fast-geometry
Faster Transposition
This commit is contained in:
commit
7541755aa5
|
@ -150,6 +150,7 @@ ROTATE_90 = 2
|
|||
ROTATE_180 = 3
|
||||
ROTATE_270 = 4
|
||||
TRANSPOSE = 5
|
||||
TRANSVERSE = 6
|
||||
|
||||
# transforms
|
||||
AFFINE = 0
|
||||
|
@ -2173,8 +2174,8 @@ class Image(object):
|
|||
|
||||
:param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`,
|
||||
:py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`,
|
||||
:py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270` or
|
||||
:py:attr:`PIL.Image.TRANSPOSE`.
|
||||
:py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270`,
|
||||
:py:attr:`PIL.Image.TRANSPOSE` or :py:attr:`PIL.Image.TRANSVERSE`.
|
||||
:returns: Returns a flipped or rotated copy of this image.
|
||||
"""
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import helper
|
|||
from helper import unittest, PillowTestCase
|
||||
|
||||
from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180,
|
||||
ROTATE_270, TRANSPOSE)
|
||||
ROTATE_270, TRANSPOSE, TRANSVERSE)
|
||||
|
||||
|
||||
class TestImageTranspose(PillowTestCase):
|
||||
|
@ -108,6 +108,22 @@ class TestImageTranspose(PillowTestCase):
|
|||
for mode in ("L", "RGB"):
|
||||
transpose(mode)
|
||||
|
||||
def test_tranverse(self):
|
||||
def transpose(mode):
|
||||
im = self.hopper[mode]
|
||||
out = im.transpose(TRANSVERSE)
|
||||
self.assertEqual(out.mode, mode)
|
||||
self.assertEqual(out.size, im.size[::-1])
|
||||
|
||||
x, y = im.size
|
||||
self.assertEqual(im.getpixel((1, 1)), out.getpixel((y-2, x-2)))
|
||||
self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((y-2, 1)))
|
||||
self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, x-2)))
|
||||
self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1)))
|
||||
|
||||
for mode in ("L", "RGB"):
|
||||
transpose(mode)
|
||||
|
||||
def test_roundtrip(self):
|
||||
im = self.hopper['L']
|
||||
|
||||
|
@ -124,6 +140,12 @@ class TestImageTranspose(PillowTestCase):
|
|||
im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM))
|
||||
self.assert_image_equal(
|
||||
im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT))
|
||||
self.assert_image_equal(
|
||||
im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT))
|
||||
self.assert_image_equal(
|
||||
im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM))
|
||||
self.assert_image_equal(
|
||||
im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1678,6 +1678,7 @@ _transpose(ImagingObject* self, PyObject* args)
|
|||
case 2: /* rotate 90 */
|
||||
case 4: /* rotate 270 */
|
||||
case 5: /* transpose */
|
||||
case 6: /* transverse */
|
||||
imOut = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize);
|
||||
break;
|
||||
default:
|
||||
|
@ -1705,6 +1706,9 @@ _transpose(ImagingObject* self, PyObject* args)
|
|||
case 5:
|
||||
(void) ImagingTranspose(imOut, imIn);
|
||||
break;
|
||||
case 6:
|
||||
(void) ImagingTransverse(imOut, imIn);
|
||||
break;
|
||||
}
|
||||
|
||||
return PyImagingNew(imOut);
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
Rotating in chunks that fit in the cache can speed up rotation
|
||||
8x on a modern CPU. A chunk size of 128 requires only 65k and is large enough
|
||||
that the overhead from the extra loops are not apparent. */
|
||||
#define ROTATE_CHUNK 128
|
||||
#define ROTATE_CHUNK 512
|
||||
#define ROTATE_SMALL_CHUNK 8
|
||||
|
||||
#define COORD(v) ((v) < 0.0 ? -1 : ((int)(v)))
|
||||
#define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v)))
|
||||
|
@ -26,30 +27,27 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
|
|||
|
||||
ImagingCopyInfo(imOut, imIn);
|
||||
|
||||
#define FLIP_LEFT_RIGHT(INT, image) \
|
||||
for (y = 0; y < imIn->ysize; y++) { \
|
||||
INT* in = imIn->image[y]; \
|
||||
INT* out = imOut->image[y]; \
|
||||
xr = imIn->xsize-1; \
|
||||
for (x = 0; x < imIn->xsize; x++, xr--) \
|
||||
out[xr] = in[x]; \
|
||||
}
|
||||
|
||||
ImagingSectionEnter(&cookie);
|
||||
|
||||
if (imIn->image8) {
|
||||
for (y = 0; y < imIn->ysize; y++) {
|
||||
UINT8* in = (UINT8*) imIn->image8[y];
|
||||
UINT8* out = (UINT8*) imOut->image8[y];
|
||||
x = 0;
|
||||
xr = imIn->xsize-1;
|
||||
for (; x < imIn->xsize; x++, xr--)
|
||||
out[xr] = in[x];
|
||||
}
|
||||
FLIP_LEFT_RIGHT(UINT8, image8)
|
||||
} else {
|
||||
for (y = 0; y < imIn->ysize; y++) {
|
||||
UINT32* in = (UINT32*) imIn->image32[y];
|
||||
UINT32* out = (UINT32*) imOut->image32[y];
|
||||
x = 0;
|
||||
xr = imIn->xsize-1;
|
||||
for (; x < imIn->xsize; x++, xr--)
|
||||
out[xr] = in[x];
|
||||
}
|
||||
FLIP_LEFT_RIGHT(INT32, image32)
|
||||
}
|
||||
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
||||
#undef FLIP_LEFT_RIGHT
|
||||
|
||||
return imOut;
|
||||
}
|
||||
|
||||
|
@ -84,6 +82,7 @@ ImagingRotate90(Imaging imOut, Imaging imIn)
|
|||
{
|
||||
ImagingSectionCookie cookie;
|
||||
int x, y, xx, yy, xr, xxsize, yysize;
|
||||
int xxx, yyy, xxxsize, yyysize;
|
||||
|
||||
if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
|
||||
return (Imaging) ImagingError_ModeError();
|
||||
|
@ -92,15 +91,22 @@ ImagingRotate90(Imaging imOut, Imaging imIn)
|
|||
|
||||
ImagingCopyInfo(imOut, imIn);
|
||||
|
||||
#define ROTATE_90(image) \
|
||||
#define ROTATE_90(INT, image) \
|
||||
for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
|
||||
for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
|
||||
yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
|
||||
xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
|
||||
for (yy = y; yy < yysize; yy++) { \
|
||||
xr = imIn->xsize - 1 - x; \
|
||||
for (xx = x; xx < xxsize; xx++, xr--) { \
|
||||
imOut->image[xr][yy] = imIn->image[yy][xx]; \
|
||||
for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
|
||||
for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
|
||||
yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \
|
||||
xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \
|
||||
for (yyy = yy; yyy < yyysize; yyy++) { \
|
||||
INT* in = imIn->image[yyy]; \
|
||||
xr = imIn->xsize - 1 - xx; \
|
||||
for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
|
||||
imOut->image[xr][yyy] = in[xxx]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
|
@ -109,9 +115,9 @@ ImagingRotate90(Imaging imOut, Imaging imIn)
|
|||
ImagingSectionEnter(&cookie);
|
||||
|
||||
if (imIn->image8)
|
||||
ROTATE_90(image8)
|
||||
ROTATE_90(UINT8, image8)
|
||||
else
|
||||
ROTATE_90(image32)
|
||||
ROTATE_90(INT32, image32)
|
||||
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
||||
|
@ -126,6 +132,7 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
|
|||
{
|
||||
ImagingSectionCookie cookie;
|
||||
int x, y, xx, yy, xxsize, yysize;
|
||||
int xxx, yyy, xxxsize, yyysize;
|
||||
|
||||
if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
|
||||
return (Imaging) ImagingError_ModeError();
|
||||
|
@ -134,14 +141,21 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
|
|||
|
||||
ImagingCopyInfo(imOut, imIn);
|
||||
|
||||
#define TRANSPOSE(image) \
|
||||
#define TRANSPOSE(INT, image) \
|
||||
for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
|
||||
for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
|
||||
yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
|
||||
xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
|
||||
for (yy = y; yy < yysize; yy++) { \
|
||||
for (xx = x; xx < xxsize; xx++) { \
|
||||
imOut->image[xx][yy] = imIn->image[yy][xx]; \
|
||||
for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
|
||||
for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
|
||||
yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \
|
||||
xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \
|
||||
for (yyy = yy; yyy < yyysize; yyy++) { \
|
||||
INT* in = imIn->image[yyy]; \
|
||||
for (xxx = xx; xxx < xxxsize; xxx++) { \
|
||||
imOut->image[xxx][yyy] = in[xxx]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
|
@ -150,9 +164,9 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
|
|||
ImagingSectionEnter(&cookie);
|
||||
|
||||
if (imIn->image8)
|
||||
TRANSPOSE(image8)
|
||||
TRANSPOSE(UINT8, image8)
|
||||
else
|
||||
TRANSPOSE(image32)
|
||||
TRANSPOSE(INT32, image32)
|
||||
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
||||
|
@ -162,6 +176,57 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
|
|||
}
|
||||
|
||||
|
||||
Imaging
|
||||
ImagingTransverse(Imaging imOut, Imaging imIn)
|
||||
{
|
||||
ImagingSectionCookie cookie;
|
||||
int x, y, xr, yr, xx, yy, xxsize, yysize;
|
||||
int xxx, yyy, xxxsize, yyysize;
|
||||
|
||||
if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
|
||||
return (Imaging) ImagingError_ModeError();
|
||||
if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize)
|
||||
return (Imaging) ImagingError_Mismatch();
|
||||
|
||||
ImagingCopyInfo(imOut, imIn);
|
||||
|
||||
#define TRANSVERSE(INT, image) \
|
||||
for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
|
||||
for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
|
||||
yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
|
||||
xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
|
||||
for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
|
||||
for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
|
||||
yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \
|
||||
xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \
|
||||
yr = imIn->ysize - 1 - yy; \
|
||||
for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
|
||||
INT* in = imIn->image[yyy]; \
|
||||
xr = imIn->xsize - 1 - xx; \
|
||||
for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
|
||||
imOut->image[xr][yr] = in[xxx]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
ImagingSectionEnter(&cookie);
|
||||
|
||||
if (imIn->image8)
|
||||
TRANSVERSE(UINT8, image8)
|
||||
else
|
||||
TRANSVERSE(INT32, image32)
|
||||
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
||||
#undef TRANSVERSE
|
||||
|
||||
return imOut;
|
||||
}
|
||||
|
||||
|
||||
Imaging
|
||||
ImagingRotate180(Imaging imOut, Imaging imIn)
|
||||
{
|
||||
|
@ -175,20 +240,23 @@ ImagingRotate180(Imaging imOut, Imaging imIn)
|
|||
|
||||
ImagingCopyInfo(imOut, imIn);
|
||||
|
||||
#define ROTATE_180(image)\
|
||||
for (y = 0; y < imIn->ysize; y++, yr--) {\
|
||||
xr = imIn->xsize-1;\
|
||||
for (x = 0; x < imIn->xsize; x++, xr--)\
|
||||
imOut->image[y][x] = imIn->image[yr][xr];\
|
||||
#define ROTATE_180(INT, image) \
|
||||
for (y = 0; y < imIn->ysize; y++, yr--) { \
|
||||
INT* in = imIn->image[y]; \
|
||||
INT* out = imOut->image[yr]; \
|
||||
xr = imIn->xsize-1; \
|
||||
for (x = 0; x < imIn->xsize; x++, xr--) \
|
||||
out[xr] = in[x]; \
|
||||
}
|
||||
|
||||
ImagingSectionEnter(&cookie);
|
||||
|
||||
yr = imIn->ysize-1;
|
||||
if (imIn->image8)
|
||||
ROTATE_180(image8)
|
||||
else
|
||||
ROTATE_180(image32)
|
||||
if (imIn->image8) {
|
||||
ROTATE_180(UINT8, image8)
|
||||
} else {
|
||||
ROTATE_180(INT32, image32)
|
||||
}
|
||||
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
||||
|
@ -203,6 +271,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
|
|||
{
|
||||
ImagingSectionCookie cookie;
|
||||
int x, y, xx, yy, yr, xxsize, yysize;
|
||||
int xxx, yyy, xxxsize, yyysize;
|
||||
|
||||
if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
|
||||
return (Imaging) ImagingError_ModeError();
|
||||
|
@ -211,15 +280,22 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
|
|||
|
||||
ImagingCopyInfo(imOut, imIn);
|
||||
|
||||
#define ROTATE_270(image) \
|
||||
#define ROTATE_270(INT, image) \
|
||||
for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
|
||||
for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
|
||||
yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
|
||||
xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
|
||||
yr = imIn->ysize - 1 - y; \
|
||||
for (yy = y; yy < yysize; yy++, yr--) { \
|
||||
for (xx = x; xx < xxsize; xx++) { \
|
||||
imOut->image[xx][yr] = imIn->image[yy][xx]; \
|
||||
for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
|
||||
for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
|
||||
yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \
|
||||
xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \
|
||||
yr = imIn->ysize - 1 - yy; \
|
||||
for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
|
||||
INT* in = imIn->image[yyy]; \
|
||||
for (xxx = xx; xxx < xxxsize; xxx++) { \
|
||||
imOut->image[xxx][yr] = in[xxx]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
|
@ -228,9 +304,9 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
|
|||
ImagingSectionEnter(&cookie);
|
||||
|
||||
if (imIn->image8)
|
||||
ROTATE_270(image8)
|
||||
ROTATE_270(UINT8, image8)
|
||||
else
|
||||
ROTATE_270(image32)
|
||||
ROTATE_270(INT32, image32)
|
||||
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
||||
|
|
|
@ -289,8 +289,9 @@ extern Imaging ImagingRankFilter(Imaging im, int size, int rank);
|
|||
extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn);
|
||||
extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn);
|
||||
extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn);
|
||||
extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]);
|
||||
extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn);
|
||||
extern Imaging ImagingTransverse(Imaging imOut, Imaging imIn);
|
||||
extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]);
|
||||
extern Imaging ImagingTransform(
|
||||
Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1,
|
||||
double *a, int filter, int fill);
|
||||
|
|
Loading…
Reference in New Issue
Block a user