From 0c90ce250493b6cc9e17f887d407d3eef921b827 Mon Sep 17 00:00:00 2001 From: homm Date: Sat, 25 Oct 2014 04:12:24 +0400 Subject: [PATCH 1/8] Add new fast ImagingTranspose function --- libImaging/Geometry.c | 38 ++++++++++++++++++++++++++++++++++++++ libImaging/Imaging.h | 1 + 2 files changed, 39 insertions(+) diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index 0f59ee0d5..20b0ef353 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -127,6 +127,44 @@ ImagingRotate90(Imaging imOut, Imaging imIn) } +Imaging +ImagingTranspose(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int x, y, xx, yy, xxsize, yysize; + int size = 64; + + 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(); + +#define TRANSPOSE(image) \ + for (y = 0; y < imIn->ysize; y += size) { \ + for (x = 0; x < imIn->xsize; x += size) { \ + yysize = size < (imIn->ysize - y) ? size : (imIn->ysize - y); \ + xxsize = size < (imIn->xsize - x) ? size : (imIn->xsize - x); \ + for (yy = 0; yy < yysize; yy++) { \ + for (xx = 0; xx < xxsize; xx++) { \ + imOut->image[x + xx][y + yy] = imIn->image[y + yy][x + xx]; \ + } \ + } \ + } \ + } + + ImagingCopyInfo(imOut, imIn); + + ImagingSectionEnter(&cookie); + if (imIn->image8) + TRANSPOSE(image8) + else + TRANSPOSE(image32) + ImagingSectionLeave(&cookie); + + return imOut; +} + + Imaging ImagingRotate180(Imaging imOut, Imaging imIn) { diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index d958387c9..f014bc0aa 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -292,6 +292,7 @@ extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn); extern Imaging ImagingStretch(Imaging imOut, Imaging imIn, int filter); +extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn); extern Imaging ImagingTransformPerspective( Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[8], int filter, int fill); From d41bc4fbfcbc2ade7ecbf554b8c8273ee51f3e5f Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 7 Nov 2014 03:21:38 +0300 Subject: [PATCH 2/8] convert tabs into spaces --- libImaging/Geometry.c | 196 +++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index 20b0ef353..9cd5d683f 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -34,7 +34,7 @@ #define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v))) /* -------------------------------------------------------------------- */ -/* Transpose operations */ +/* Transpose operations */ Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn) @@ -43,25 +43,25 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) int x, y, xr; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); -#define FLIP_HORIZ(image)\ +#define FLIP_HORIZ(image)\ for (y = 0; y < imIn->ysize; y++) {\ - xr = imIn->xsize-1;\ - for (x = 0; x < imIn->xsize; x++, xr--)\ - imOut->image[y][x] = imIn->image[y][xr];\ + xr = imIn->xsize-1;\ + for (x = 0; x < imIn->xsize; x++, xr--)\ + imOut->image[y][x] = imIn->image[y][xr];\ } ImagingSectionEnter(&cookie); if (imIn->image8) - FLIP_HORIZ(image8) + FLIP_HORIZ(image8) else - FLIP_HORIZ(image32) + FLIP_HORIZ(image32) ImagingSectionLeave(&cookie); @@ -76,9 +76,9 @@ ImagingFlipTopBottom(Imaging imOut, Imaging imIn) int y, yr; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); @@ -86,7 +86,7 @@ ImagingFlipTopBottom(Imaging imOut, Imaging imIn) yr = imIn->ysize-1; for (y = 0; y < imIn->ysize; y++, yr--) - memcpy(imOut->image[yr], imIn->image[y], imIn->linesize); + memcpy(imOut->image[yr], imIn->image[y], imIn->linesize); ImagingSectionLeave(&cookie); @@ -101,25 +101,25 @@ ImagingRotate90(Imaging imOut, Imaging imIn) int x, y, xr; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); -#define ROTATE_90(image)\ +#define ROTATE_90(image)\ for (y = 0; y < imIn->ysize; y++) {\ - xr = imIn->xsize-1;\ - for (x = 0; x < imIn->xsize; x++, xr--)\ - imOut->image[xr][y] = imIn->image[y][x];\ + xr = imIn->xsize-1;\ + for (x = 0; x < imIn->xsize; x++, xr--)\ + imOut->image[xr][y] = imIn->image[y][x];\ } ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_90(image8) + ROTATE_90(image8) else - ROTATE_90(image32) + ROTATE_90(image32) ImagingSectionLeave(&cookie); @@ -172,27 +172,27 @@ ImagingRotate180(Imaging imOut, Imaging imIn) int x, y, xr, yr; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); yr = imIn->ysize-1; -#define ROTATE_180(image)\ +#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];\ + xr = imIn->xsize-1;\ + for (x = 0; x < imIn->xsize; x++, xr--)\ + imOut->image[y][x] = imIn->image[yr][xr];\ } ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_180(image8) + ROTATE_180(image8) else - ROTATE_180(image32) + ROTATE_180(image32) ImagingSectionLeave(&cookie); @@ -207,25 +207,25 @@ ImagingRotate270(Imaging imOut, Imaging imIn) int x, y, yr; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) - return (Imaging) ImagingError_Mismatch(); + return (Imaging) ImagingError_Mismatch(); ImagingCopyInfo(imOut, imIn); yr = imIn->ysize - 1; -#define ROTATE_270(image)\ +#define ROTATE_270(image)\ for (y = 0; y < imIn->ysize; y++, yr--)\ - for (x = 0; x < imIn->xsize; x++)\ - imOut->image[x][y] = imIn->image[yr][x]; + for (x = 0; x < imIn->xsize; x++)\ + imOut->image[x][y] = imIn->image[yr][x]; ImagingSectionEnter(&cookie); if (imIn->image8) - ROTATE_270(image8) + ROTATE_270(image8) else - ROTATE_270(image32) + ROTATE_270(image32) ImagingSectionLeave(&cookie); @@ -234,7 +234,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn) /* -------------------------------------------------------------------- */ -/* Transforms */ +/* Transforms */ /* transform primitives (ImagingTransformMap) */ @@ -635,7 +635,7 @@ ImagingTransform( double xx, yy; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); ImagingCopyInfo(imOut, imIn); @@ -651,15 +651,15 @@ ImagingTransform( y1 = imOut->ysize; for (y = y0; y < y1; y++) { - out = imOut->image[y] + x0*imOut->pixelsize; - for (x = x0; x < x1; x++) { - if (!transform(&xx, &yy, x-x0, y-y0, transform_data) || + out = imOut->image[y] + x0*imOut->pixelsize; + for (x = x0; x < x1; x++) { + if (!transform(&xx, &yy, x-x0, y-y0, transform_data) || !filter(out, imIn, xx, yy, filter_data)) { if (fill) memset(out, 0, imOut->pixelsize); } out += imOut->pixelsize; - } + } } ImagingSectionLeave(&cookie); @@ -682,7 +682,7 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, int *xintab; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); ImagingCopyInfo(imOut, imIn); @@ -697,8 +697,8 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, xintab = (int*) malloc(imOut->xsize * sizeof(int)); if (!xintab) { - ImagingDelete(imOut); - return (Imaging) ImagingError_MemoryError(); + ImagingDelete(imOut); + return (Imaging) ImagingError_MemoryError(); } xo = a[0]; @@ -709,29 +709,29 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, /* Pretabulate horizontal pixel positions */ for (x = x0; x < x1; x++) { - xin = COORD(xo); - if (xin >= 0 && xin < (int) imIn->xsize) { - xmax = x+1; - if (x < xmin) - xmin = x; - xintab[x] = xin; - } - xo += a[1]; + xin = COORD(xo); + if (xin >= 0 && xin < (int) imIn->xsize) { + xmax = x+1; + if (x < xmin) + xmin = x; + xintab[x] = xin; + } + xo += a[1]; } -#define AFFINE_SCALE(pixel, image)\ +#define AFFINE_SCALE(pixel, image)\ for (y = y0; y < y1; y++) {\ - int yi = COORD(yo);\ - pixel *in, *out;\ - out = imOut->image[y];\ + int yi = COORD(yo);\ + pixel *in, *out;\ + out = imOut->image[y];\ if (fill && x1 > x0)\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ - if (yi >= 0 && yi < imIn->ysize) {\ - in = imIn->image[yi];\ - for (x = xmin; x < xmax; x++)\ - out[x] = in[xintab[x]];\ - }\ - yo += a[5];\ + if (yi >= 0 && yi < imIn->ysize) {\ + in = imIn->image[yi];\ + for (x = xmin; x < xmax; x++)\ + out[x] = in[xintab[x]];\ + }\ + yo += a[5];\ } ImagingSectionEnter(&cookie); @@ -781,32 +781,32 @@ affine_fixed(Imaging imOut, Imaging imIn, a0 = FIX(a[0]); a1 = FIX(a[1]); a2 = FIX(a[2]); a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]); -#define AFFINE_TRANSFORM_FIXED(pixel, image)\ +#define AFFINE_TRANSFORM_FIXED(pixel, image)\ for (y = y0; y < y1; y++) {\ - pixel *out;\ - xx = a0;\ - yy = a3;\ - out = imOut->image[y];\ + pixel *out;\ + xx = a0;\ + yy = a3;\ + out = imOut->image[y];\ if (fill && x1 > x0)\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ for (x = x0; x < x1; x++, out++) {\ - xin = xx >> 16;\ - if (xin >= 0 && xin < xsize) {\ - yin = yy >> 16;\ - if (yin >= 0 && yin < ysize)\ + xin = xx >> 16;\ + if (xin >= 0 && xin < xsize) {\ + yin = yy >> 16;\ + if (yin >= 0 && yin < ysize)\ *out = imIn->image[yin][xin];\ }\ - xx += a1;\ - yy += a4;\ - }\ - a0 += a2;\ - a3 += a5;\ + xx += a1;\ + yy += a4;\ + }\ + a0 += a2;\ + a3 += a5;\ } if (imIn->image8) - AFFINE_TRANSFORM_FIXED(UINT8, image8) + AFFINE_TRANSFORM_FIXED(UINT8, image8) else - AFFINE_TRANSFORM_FIXED(INT32, image32) + AFFINE_TRANSFORM_FIXED(INT32, image32) return imOut; } @@ -839,11 +839,11 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn, } if (a[2] == 0 && a[4] == 0) - /* Scaling */ - return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill); + /* Scaling */ + return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill); if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (x0 < 0) x0 = 0; @@ -873,34 +873,34 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn, xo = a[0]; yo = a[3]; -#define AFFINE_TRANSFORM(pixel, image)\ +#define AFFINE_TRANSFORM(pixel, image)\ for (y = y0; y < y1; y++) {\ - pixel *out;\ - xx = xo;\ - yy = yo;\ - out = imOut->image[y];\ + pixel *out;\ + xx = xo;\ + yy = yo;\ + out = imOut->image[y];\ if (fill && x1 > x0)\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ for (x = x0; x < x1; x++, out++) {\ - xin = COORD(xx);\ - if (xin >= 0 && xin < xsize) {\ - yin = COORD(yy);\ - if (yin >= 0 && yin < ysize)\ + xin = COORD(xx);\ + if (xin >= 0 && xin < xsize) {\ + yin = COORD(yy);\ + if (yin >= 0 && yin < ysize)\ *out = imIn->image[yin][xin];\ }\ - xx += a[1];\ - yy += a[4];\ - }\ - xo += a[2];\ - yo += a[5];\ + xx += a[1];\ + yy += a[4];\ + }\ + xo += a[2];\ + yo += a[5];\ } ImagingSectionEnter(&cookie); if (imIn->image8) - AFFINE_TRANSFORM(UINT8, image8) + AFFINE_TRANSFORM(UINT8, image8) else - AFFINE_TRANSFORM(INT32, image32) + AFFINE_TRANSFORM(INT32, image32) ImagingSectionLeave(&cookie); @@ -950,7 +950,7 @@ ImagingResize(Imaging imOut, Imaging imIn, int filterid) double a[6]; if (imOut->xsize == imIn->xsize && imOut->ysize == imIn->ysize) - return ImagingCopy2(imOut, imIn); + return ImagingCopy2(imOut, imIn); memset(a, 0, sizeof a); a[1] = (double) imIn->xsize / imOut->xsize; From 32079b1dcc8dca39a016f9332424ee41c812c56a Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 7 Nov 2014 03:37:12 +0300 Subject: [PATCH 3/8] make transpose part of public api --- PIL/Image.py | 7 ++++--- _imaging.c | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 99ab6327b..de2876f37 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -150,6 +150,7 @@ FLIP_TOP_BOTTOM = 1 ROTATE_90 = 2 ROTATE_180 = 3 ROTATE_270 = 4 +TRANSPOSE = 5 # transforms AFFINE = 0 @@ -1921,13 +1922,13 @@ class Image: :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`, or :py:attr:`PIL.Image.ROTATE_270`. + :py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270` or + :py:attr:`PIL.Image.TRANSPOSE`. :returns: Returns a flipped or rotated copy of this image. """ self.load() - im = self.im.transpose(method) - return self._new(im) + return self._new(self.im.transpose(method)) def effect_spread(self, distance): """ diff --git a/_imaging.c b/_imaging.c index 1759d4c8d..4eb878cf7 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1750,6 +1750,7 @@ _transpose(ImagingObject* self, PyObject* args) break; case 2: /* rotate 90 */ case 4: /* rotate 270 */ + case 5: /* transpose */ imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize); break; default: @@ -1774,6 +1775,9 @@ _transpose(ImagingObject* self, PyObject* args) case 4: (void) ImagingRotate270(imOut, imIn); break; + case 5: + (void) ImagingTranspose(imOut, imIn); + break; } return PyImagingNew(imOut); From a960d440db22a1e95e4aae726a9433ce2cd01a7c Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 7 Nov 2014 03:57:00 +0300 Subject: [PATCH 4/8] less operations in loop --- libImaging/Geometry.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index 9cd5d683f..4adb228f5 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -132,7 +132,7 @@ ImagingTranspose(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, xxsize, yysize; - int size = 64; + int size = 128; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -142,11 +142,11 @@ ImagingTranspose(Imaging imOut, Imaging imIn) #define TRANSPOSE(image) \ for (y = 0; y < imIn->ysize; y += size) { \ for (x = 0; x < imIn->xsize; x += size) { \ - yysize = size < (imIn->ysize - y) ? size : (imIn->ysize - y); \ - xxsize = size < (imIn->xsize - x) ? size : (imIn->xsize - x); \ - for (yy = 0; yy < yysize; yy++) { \ - for (xx = 0; xx < xxsize; xx++) { \ - imOut->image[x + xx][y + yy] = imIn->image[y + yy][x + xx]; \ + yysize = y + size < imIn->ysize ? y + size : imIn->ysize; \ + xxsize = x + size < imIn->xsize ? x + size : imIn->xsize; \ + for (yy = y; yy < yysize; yy++) { \ + for (xx = x; xx < xxsize; xx++) { \ + imOut->image[xx][yy] = imIn->image[yy][xx]; \ } \ } \ } \ From cfa20f1004428026b6be01b2807ece30908609f0 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 7 Nov 2014 04:15:18 +0300 Subject: [PATCH 5/8] rotate 90 and 270 by chunks --- libImaging/Geometry.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index 4adb228f5..4d26e644b 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -98,7 +98,8 @@ Imaging ImagingRotate90(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; - int x, y, xr; + int x, y, xx, yy, xr, xxsize, yysize; + int size = 128; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -107,11 +108,18 @@ ImagingRotate90(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); -#define ROTATE_90(image)\ - for (y = 0; y < imIn->ysize; y++) {\ - xr = imIn->xsize-1;\ - for (x = 0; x < imIn->xsize; x++, xr--)\ - imOut->image[xr][y] = imIn->image[y][x];\ +#define ROTATE_90(image) \ + for (y = 0; y < imIn->ysize; y += size) { \ + for (x = 0; x < imIn->xsize; x += size) { \ + yysize = y + size < imIn->ysize ? y + size : imIn->ysize; \ + xxsize = x + size < imIn->xsize ? x + size : 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]; \ + } \ + } \ + } \ } ImagingSectionEnter(&cookie); @@ -204,7 +212,8 @@ Imaging ImagingRotate270(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; - int x, y, yr; + int x, y, xx, yy, yr, xxsize, yysize; + int size = 128; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -213,12 +222,19 @@ ImagingRotate270(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); - yr = imIn->ysize - 1; - -#define ROTATE_270(image)\ - for (y = 0; y < imIn->ysize; y++, yr--)\ - for (x = 0; x < imIn->xsize; x++)\ - imOut->image[x][y] = imIn->image[yr][x]; +#define ROTATE_270(image) \ + for (y = 0; y < imIn->ysize; y += size) { \ + for (x = 0; x < imIn->xsize; x += size) { \ + yysize = y + size < imIn->ysize ? y + size : imIn->ysize; \ + xxsize = x + size < imIn->xsize ? x + size : 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]; \ + } \ + } \ + } \ + } ImagingSectionEnter(&cookie); From a51b17664499a347f7c0daeac778a0828a131d9f Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 7 Nov 2014 04:46:16 +0300 Subject: [PATCH 6/8] make ROTATE_CHUNK constant add comment --- libImaging/Geometry.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index 4d26e644b..aca183112 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -30,6 +30,13 @@ /* Undef if you don't need resampling filters */ #define WITH_FILTERS +/* Large images rotation is inefficient operation in terms of CPU cache. + One row in source image affects each column in destination. + Rotating in small chunks can speed up up to 8 times on modern CPU. + Chunk size of 128 requires only 65k of CPU cache and high enougth + for overhead from extra loop have not been manifested. */ +#define ROTATE_CHUNK 128 + #define COORD(v) ((v) < 0.0 ? -1 : ((int)(v))) #define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v))) @@ -99,7 +106,6 @@ ImagingRotate90(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, xr, xxsize, yysize; - int size = 128; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -109,10 +115,10 @@ ImagingRotate90(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); #define ROTATE_90(image) \ - for (y = 0; y < imIn->ysize; y += size) { \ - for (x = 0; x < imIn->xsize; x += size) { \ - yysize = y + size < imIn->ysize ? y + size : imIn->ysize; \ - xxsize = x + size < imIn->xsize ? x + size : imIn->xsize; \ + 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--) { \ @@ -140,7 +146,6 @@ ImagingTranspose(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, xxsize, yysize; - int size = 128; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -148,10 +153,10 @@ ImagingTranspose(Imaging imOut, Imaging imIn) return (Imaging) ImagingError_Mismatch(); #define TRANSPOSE(image) \ - for (y = 0; y < imIn->ysize; y += size) { \ - for (x = 0; x < imIn->xsize; x += size) { \ - yysize = y + size < imIn->ysize ? y + size : imIn->ysize; \ - xxsize = x + size < imIn->xsize ? x + size : imIn->xsize; \ + 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]; \ @@ -213,7 +218,6 @@ ImagingRotate270(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int x, y, xx, yy, yr, xxsize, yysize; - int size = 128; if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) return (Imaging) ImagingError_ModeError(); @@ -223,10 +227,10 @@ ImagingRotate270(Imaging imOut, Imaging imIn) ImagingCopyInfo(imOut, imIn); #define ROTATE_270(image) \ - for (y = 0; y < imIn->ysize; y += size) { \ - for (x = 0; x < imIn->xsize; x += size) { \ - yysize = y + size < imIn->ysize ? y + size : imIn->ysize; \ - xxsize = x + size < imIn->xsize ? x + size : imIn->xsize; \ + 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++) { \ From 4cf2f158b674d14693ef1728888e660174384aba Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 7 Nov 2014 11:48:47 +0300 Subject: [PATCH 7/8] add test update comment --- Tests/test_image_transpose.py | 116 ++++++++++++++++++++++++++++------ libImaging/Geometry.c | 10 +-- 2 files changed, 103 insertions(+), 23 deletions(-) diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 3e4257bc0..eb61c0c04 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,29 +1,108 @@ from helper import unittest, PillowTestCase, hopper -from PIL import Image - -FLIP_LEFT_RIGHT = Image.FLIP_LEFT_RIGHT -FLIP_TOP_BOTTOM = Image.FLIP_TOP_BOTTOM -ROTATE_90 = Image.ROTATE_90 -ROTATE_180 = Image.ROTATE_180 -ROTATE_270 = Image.ROTATE_270 +from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, + ROTATE_270, TRANSPOSE) class TestImageTranspose(PillowTestCase): - def test_sanity(self): + def test_flip_left_right(self): + def transpose(mode): + im = hopper(mode) + out = im.transpose(FLIP_LEFT_RIGHT) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) - im = hopper() + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((x-2, 1))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, y-2))) - im.transpose(FLIP_LEFT_RIGHT) - im.transpose(FLIP_TOP_BOTTOM) + for mode in ("L", "RGB"): + transpose(mode) - im.transpose(ROTATE_90) - im.transpose(ROTATE_180) - im.transpose(ROTATE_270) + def test_flip_top_bottom(self): + def transpose(mode): + im = hopper(mode) + out = im.transpose(FLIP_TOP_BOTTOM) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, 1))) + + for mode in ("L", "RGB"): + transpose(mode) + + def test_rotate_90(self): + def transpose(mode): + im = hopper(mode) + out = im.transpose(ROTATE_90) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size[::-1]) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, 1))) + + for mode in ("L", "RGB"): + transpose(mode) + + def test_rotate_180(self): + def transpose(mode): + im = hopper(mode) + out = im.transpose(ROTATE_180) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1))) + + for mode in ("L", "RGB"): + transpose(mode) + + def test_rotate_270(self): + def transpose(mode): + im = hopper(mode) + out = im.transpose(ROTATE_270) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size[::-1]) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((x-2, 1))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, y-2))) + + for mode in ("L", "RGB"): + transpose(mode) + + def test_transpose(self): + def transpose(mode): + im = hopper(mode) + out = im.transpose(TRANSPOSE) + self.assertEqual(out.mode, mode) + self.assertEqual(out.size, im.size[::-1]) + + x, y = im.size + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, y-2))) + + for mode in ("L", "RGB"): + transpose(mode) def test_roundtrip(self): - im = hopper() def transpose(first, second): @@ -33,12 +112,13 @@ class TestImageTranspose(PillowTestCase): im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) self.assert_image_equal( im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) - self.assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) self.assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) + self.assert_image_equal( + im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM)) + self.assert_image_equal( + im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT)) if __name__ == '__main__': unittest.main() - -# End of file diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index aca183112..afd162dad 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -30,11 +30,11 @@ /* Undef if you don't need resampling filters */ #define WITH_FILTERS -/* Large images rotation is inefficient operation in terms of CPU cache. - One row in source image affects each column in destination. - Rotating in small chunks can speed up up to 8 times on modern CPU. - Chunk size of 128 requires only 65k of CPU cache and high enougth - for overhead from extra loop have not been manifested. */ +/* For large images rotation is an inefficient operation in terms of CPU cache. + One row in the source image affects each column in destination. + 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 COORD(v) ((v) < 0.0 ? -1 : ((int)(v))) From 9e5ee5a5bffa6b4ce7954cfb2f908132a3a0f5d7 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 7 Nov 2014 13:15:51 +0300 Subject: [PATCH 8/8] use not square image for test, fix tests --- Tests/test_image_transpose.py | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index eb61c0c04..3183ceadd 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -6,9 +6,14 @@ from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, class TestImageTranspose(PillowTestCase): + hopper = { + 'L': hopper('L').crop((0, 0, 121, 127)).copy(), + 'RGB': hopper('RGB').crop((0, 0, 121, 127)).copy(), + } + def test_flip_left_right(self): def transpose(mode): - im = hopper(mode) + im = self.hopper[mode] out = im.transpose(FLIP_LEFT_RIGHT) self.assertEqual(out.mode, mode) self.assertEqual(out.size, im.size) @@ -24,7 +29,7 @@ class TestImageTranspose(PillowTestCase): def test_flip_top_bottom(self): def transpose(mode): - im = hopper(mode) + im = self.hopper[mode] out = im.transpose(FLIP_TOP_BOTTOM) self.assertEqual(out.mode, mode) self.assertEqual(out.size, im.size) @@ -40,23 +45,23 @@ class TestImageTranspose(PillowTestCase): def test_rotate_90(self): def transpose(mode): - im = hopper(mode) + im = self.hopper[mode] out = im.transpose(ROTATE_90) self.assertEqual(out.mode, mode) self.assertEqual(out.size, im.size[::-1]) x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, x-2))) self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, y-2))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, 1))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, x-2))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, 1))) for mode in ("L", "RGB"): transpose(mode) def test_rotate_180(self): def transpose(mode): - im = hopper(mode) + im = self.hopper[mode] out = im.transpose(ROTATE_180) self.assertEqual(out.mode, mode) self.assertEqual(out.size, im.size) @@ -72,38 +77,38 @@ class TestImageTranspose(PillowTestCase): def test_rotate_270(self): def transpose(mode): - im = hopper(mode) + im = self.hopper[mode] out = im.transpose(ROTATE_270) self.assertEqual(out.mode, mode) self.assertEqual(out.size, im.size[::-1]) x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((x-2, 1))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((1, 1)), out.getpixel((y-2, 1))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((y-2, x-2))) self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, x-2))) for mode in ("L", "RGB"): transpose(mode) def test_transpose(self): def transpose(mode): - im = hopper(mode) + im = self.hopper[mode] out = im.transpose(TRANSPOSE) self.assertEqual(out.mode, mode) self.assertEqual(out.size, im.size[::-1]) x, y = im.size self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, y-2))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, 1))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, y-2))) + self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, x-2))) + self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, 1))) + self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, x-2))) for mode in ("L", "RGB"): transpose(mode) def test_roundtrip(self): - im = hopper() + im = self.hopper['L'] def transpose(first, second): return im.transpose(first).transpose(second)