Merge pull request #2730 from uploadcare/fast-geometry

Faster Transposition
This commit is contained in:
wiredfool 2017-09-19 10:58:47 +01:00 committed by GitHub
commit 7541755aa5
5 changed files with 154 additions and 50 deletions

View File

@ -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.
"""

View File

@ -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__':

View File

@ -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);

View File

@ -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);

View File

@ -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);