From e7569a1ee9d7a67964f1d6d984530f83e9f56778 Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 24 Nov 2016 04:33:46 +0300 Subject: [PATCH 01/28] remove deprecated since Pillow 2.7 Image.im.stretch() method --- _imaging.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/_imaging.c b/_imaging.c index e2320d862..b72aa552a 100644 --- a/_imaging.c +++ b/_imaging.c @@ -2977,9 +2977,6 @@ static struct PyMethodDef methods[] = { {"rankfilter", (PyCFunction)_rankfilter, 1}, #endif {"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}, {"transpose", (PyCFunction)_transpose, 1}, {"transform2", (PyCFunction)_transform2, 1}, From f828416752d4922b2fdd2e9172e9f3e185f72e24 Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 24 Nov 2016 04:40:54 +0300 Subject: [PATCH 02/28] add roi argument to Image.resize() method constraints check --- PIL/Image.py | 7 +++++-- Tests/test_image_resample.py | 31 +++++++++++++++++++++++++++++++ _imaging.c | 23 ++++++++++++++++++++--- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 3f8a49e6f..2434d54a7 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1519,7 +1519,7 @@ class Image(object): return self.pyaccess.putpixel(xy, value) return self.im.putpixel(xy, value) - def resize(self, size, resample=NEAREST): + def resize(self, size, resample=NEAREST, roi=None): """ Returns a resized copy of this image. @@ -1555,7 +1555,10 @@ class Image(object): if self.mode == 'RGBA': return self.convert('RGBa').resize(size, resample).convert('RGBA') - return self._new(self.im.resize(size, resample)) + if roi is None: + roi = (0, 0) + self.size + + return self._new(self.im.resize(size, resample, roi)) def rotate(self, angle, resample=NEAREST, expand=0): """ diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index ebc1ac6e4..dd990c5a9 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -348,5 +348,36 @@ class CoreResampleCoefficientsTest(PillowTestCase): self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) # fourth channel +class CoreResampleRoiTest(PillowTestCase): + def test_wrong_arguments(self): + im = hopper() + for resample in (Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING, + Image.BICUBIC, Image.LANCZOS): + im.resize((32, 32), resample, (0, 0, im.width, im.height)) + im.resize((32, 32), resample, (20, 20, im.width, im.height)) + + with self.assertRaisesRegexp(TypeError, "must be sequence of length 4"): + im.resize((32, 32), resample, (im.width, im.height)) + + with self.assertRaisesRegexp(ValueError, "can't be negative"): + im.resize((32, 32), resample, (-20, 20, 100, 100)) + with self.assertRaisesRegexp(ValueError, "can't be negative"): + im.resize((32, 32), resample, (20, -20, 100, 100)) + + with self.assertRaisesRegexp(ValueError, "can't be empty"): + im.resize((32, 32), resample, (20, 20, 20, 100)) + with self.assertRaisesRegexp(ValueError, "can't be empty"): + im.resize((32, 32), resample, (100, 20, 20, 100)) + with self.assertRaisesRegexp(ValueError, "can't be empty"): + im.resize((32, 32), resample, (20, 20, 100, 20)) + with self.assertRaisesRegexp(ValueError, "can't be empty"): + im.resize((32, 32), resample, (20, 100, 100, 20)) + + with self.assertRaisesRegexp(ValueError, "can't exceed"): + im.resize((32, 32), resample, (0, 0, im.width + 1, im.height)) + with self.assertRaisesRegexp(ValueError, "can't exceed"): + im.resize((32, 32), resample, (0, 0, im.width, im.height + 1)) + + if __name__ == '__main__': unittest.main() diff --git a/_imaging.c b/_imaging.c index b72aa552a..881c6585d 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1534,15 +1534,32 @@ _resize(ImagingObject* self, PyObject* args) int xsize, ysize; int filter = IMAGING_TRANSFORM_NEAREST; - if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter)) - return NULL; - + float roi[4] = {0, 0, 0, 0}; + imIn = self->image; + roi[2] = imIn->xsize; + roi[3] = imIn->ysize; + + if (!PyArg_ParseTuple(args, "(ii)|i(ffff)", &xsize, &ysize, &filter, + &roi[0], &roi[1], &roi[2], &roi[3])) + return NULL; if (xsize < 1 || ysize < 1) { return ImagingError_ValueError("height and width must be > 0"); } + if (roi[0] < 0 || roi[1] < 0) { + return ImagingError_ValueError("region of interest offset can't be negative"); + } + + if (roi[2] > imIn->xsize || roi[3] > imIn->ysize) { + return ImagingError_ValueError("region of interest can't exceed original image size"); + } + + if (roi[2] - roi[0] <= 0 || roi[3] - roi[1] <= 0) { + return ImagingError_ValueError("region of interest can't be empty"); + } + if (imIn->xsize == xsize && imIn->ysize == ysize) { imOut = ImagingCopy(imIn); } From 338610b112befc2ba65308cb72bc0bb192808ff5 Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 24 Nov 2016 05:12:41 +0300 Subject: [PATCH 03/28] nearest and copy ROI --- Tests/test_image_resample.py | 19 +++++++++++++++++++ _imaging.c | 8 +++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index dd990c5a9..2589ca9a4 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -378,6 +378,25 @@ class CoreResampleRoiTest(PillowTestCase): with self.assertRaisesRegexp(ValueError, "can't exceed"): im.resize((32, 32), resample, (0, 0, im.width, im.height + 1)) + def test_tiles(self): + im = hopper() + # should not be fractional + size = (28, 14) + sc = (3, 4) # scale + o = (5, 10) # offset + # fixed size divisible by scale + im = im.resize((im.width // sc[0] * sc[0], + im.height // sc[1] * sc[1])) + + for resample in (Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING, + Image.BICUBIC, Image.LANCZOS): + roi = (o[0] * sc[0], o[1] * sc[1], + (o[0] + size[0]) * sc[0], (o[1] + size[1]) * sc[1]) + tile1 = im.resize(size, resample, roi) + big_size = (im.width // sc[0], im.height // sc[1]) + tile2 = im.resize(big_size, resample)\ + .crop(o + (o[0] + size[0], o[1] + size[1])) + if __name__ == '__main__': unittest.main() diff --git a/_imaging.c b/_imaging.c index 881c6585d..f95c71aa7 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1560,15 +1560,17 @@ _resize(ImagingObject* self, PyObject* args) return ImagingError_ValueError("region of interest can't be empty"); } - if (imIn->xsize == xsize && imIn->ysize == ysize) { + if (roi[0] == 0 && roi[1] == 0 && roi[2] == xsize && roi[3] == ysize) { imOut = ImagingCopy(imIn); } else if (filter == IMAGING_TRANSFORM_NEAREST) { double a[6]; memset(a, 0, sizeof a); - a[0] = (double) imIn->xsize / xsize; - a[4] = (double) imIn->ysize / ysize; + a[0] = (double) (roi[2] - roi[0]) / xsize; + a[4] = (double) (roi[3] - roi[1]) / ysize; + a[2] = roi[0]; + a[5] = roi[1]; imOut = ImagingNew(imIn->mode, xsize, ysize); From a1fedc0f8ac13e23f4b1f35cd917556b1747c33e Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 24 Nov 2016 06:05:20 +0300 Subject: [PATCH 04/28] expose roi to precompute_coeffs (still noop) --- Tests/test_image_resample.py | 4 ++-- _imaging.c | 2 +- libImaging/Imaging.h | 2 +- libImaging/Resample.c | 46 +++++++++++++++++++++++------------- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 2589ca9a4..fa862932d 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -351,7 +351,7 @@ class CoreResampleCoefficientsTest(PillowTestCase): class CoreResampleRoiTest(PillowTestCase): def test_wrong_arguments(self): im = hopper() - for resample in (Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING, + for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, Image.BICUBIC, Image.LANCZOS): im.resize((32, 32), resample, (0, 0, im.width, im.height)) im.resize((32, 32), resample, (20, 20, im.width, im.height)) @@ -388,7 +388,7 @@ class CoreResampleRoiTest(PillowTestCase): im = im.resize((im.width // sc[0] * sc[0], im.height // sc[1] * sc[1])) - for resample in (Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING, + for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, Image.BICUBIC, Image.LANCZOS): roi = (o[0] * sc[0], o[1] * sc[1], (o[0] + size[0]) * sc[0], (o[1] + size[1]) * sc[1]) diff --git a/_imaging.c b/_imaging.c index f95c71aa7..4da764d01 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1580,7 +1580,7 @@ _resize(ImagingObject* self, PyObject* args) a, filter, 1); } else { - imOut = ImagingResample(imIn, xsize, ysize, filter); + imOut = ImagingResample(imIn, xsize, ysize, filter, roi); } return PyImagingNew(imOut); diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 2030821f2..dcdcb85b9 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -290,7 +290,7 @@ 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); +extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float roi[4]); extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn); extern Imaging ImagingTransform( Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1, diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 770f5f611..28faa7416 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -125,8 +125,8 @@ static inline UINT8 clip8(int in) int -precompute_coeffs(int inSize, int outSize, struct filter *filterp, - int **xboundsp, double **kkp) { +precompute_coeffs(int inSize, int in0, int in1, int outSize, + struct filter *filterp, int **xboundsp, double **kkp) { double support, scale, filterscale; double center, ww, ss; int xx, x, kmax, xmin, xmax; @@ -238,7 +238,8 @@ normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp) Imaging -ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) +ImagingResampleHorizontal_8bpc(Imaging imIn, int x0, int x1, int xsize, + struct filter *filterp) { ImagingSectionCookie cookie; Imaging imOut; @@ -248,7 +249,8 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) INT32 *k, *kk; double *prekk; - kmax = precompute_coeffs(imIn->xsize, xsize, filterp, &xbounds, &prekk); + kmax = precompute_coeffs(imIn->xsize, x0, x1, xsize, filterp, + &xbounds, &prekk); if ( ! kmax) { return (Imaging) ImagingError_MemoryError(); } @@ -343,7 +345,8 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) Imaging -ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) +ImagingResampleVertical_8bpc(Imaging imIn, int y0, int y1, int ysize, + struct filter *filterp) { ImagingSectionCookie cookie; Imaging imOut; @@ -353,7 +356,8 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) INT32 *k, *kk; double *prekk; - kmax = precompute_coeffs(imIn->ysize, ysize, filterp, &xbounds, &prekk); + kmax = precompute_coeffs(imIn->ysize, y0, y1, ysize, filterp, + &xbounds, &prekk); if ( ! kmax) { return (Imaging) ImagingError_MemoryError(); } @@ -448,7 +452,8 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) Imaging -ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp) +ImagingResampleHorizontal_32bpc(Imaging imIn, int x0, int x1, int xsize, + struct filter *filterp) { ImagingSectionCookie cookie; Imaging imOut; @@ -457,7 +462,8 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp) int *xbounds; double *k, *kk; - kmax = precompute_coeffs(imIn->xsize, xsize, filterp, &xbounds, &kk); + kmax = precompute_coeffs(imIn->xsize, x0, x1, xsize, filterp, + &xbounds, &kk); if ( ! kmax) { return (Imaging) ImagingError_MemoryError(); } @@ -508,7 +514,8 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp) Imaging -ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp) +ImagingResampleVertical_32bpc(Imaging imIn, int y0, int y1, int ysize, + struct filter *filterp) { ImagingSectionCookie cookie; Imaging imOut; @@ -517,7 +524,8 @@ ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp) int *xbounds; double *k, *kk; - kmax = precompute_coeffs(imIn->ysize, ysize, filterp, &xbounds, &kk); + kmax = precompute_coeffs(imIn->ysize, y0, y1, ysize, filterp, + &xbounds, &kk); if ( ! kmax) { return (Imaging) ImagingError_MemoryError(); } @@ -567,14 +575,18 @@ ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp) } +typedef Imaging (*ResampleFunction)(Imaging imIn, int x0, int x1, int xsize, + struct filter *filterp); + + Imaging -ImagingResample(Imaging imIn, int xsize, int ysize, int filter) +ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float roi[4]) { Imaging imTemp = NULL; Imaging imOut = NULL; struct filter *filterp; - Imaging (*ResampleHorizontal)(Imaging imIn, int xsize, struct filter *filterp); - Imaging (*ResampleVertical)(Imaging imIn, int xsize, struct filter *filterp); + ResampleFunction ResampleHorizontal; + ResampleFunction ResampleVertical; if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0) return (Imaging) ImagingError_ModeError(); @@ -624,17 +636,17 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter) } /* two-pass resize, first pass */ - if (imIn->xsize != xsize) { - imTemp = ResampleHorizontal(imIn, xsize, filterp); + if (roi[0] || roi[2] != xsize) { + imTemp = ResampleHorizontal(imIn, roi[0], roi[2], xsize, filterp); if ( ! imTemp) return NULL; imOut = imIn = imTemp; } /* second pass */ - if (imIn->ysize != ysize) { + if (roi[1] || roi[3] != ysize) { /* imIn can be the original image or horizontally resampled one */ - imOut = ResampleVertical(imIn, ysize, filterp); + imOut = ResampleVertical(imIn, roi[1], roi[3], ysize, filterp); /* it's safe to call ImagingDelete with empty value if there was no previous step. */ ImagingDelete(imTemp); From ef787bc3f537327dab0ebea21132d2397b9f50ac Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 24 Nov 2016 06:11:36 +0300 Subject: [PATCH 05/28] use ROI in precompute_coeffs --- Tests/test_image_resample.py | 2 ++ libImaging/Resample.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index fa862932d..680f0ba73 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -397,6 +397,8 @@ class CoreResampleRoiTest(PillowTestCase): tile2 = im.resize(big_size, resample)\ .crop(o + (o[0] + size[0], o[1] + size[1])) + self.assert_image_equal(tile1, tile2) + if __name__ == '__main__': unittest.main() diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 28faa7416..fd29a1a93 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -134,7 +134,7 @@ precompute_coeffs(int inSize, int in0, int in1, int outSize, double *kk, *k; /* prepare for horizontal stretch */ - filterscale = scale = (double) inSize / outSize; + filterscale = scale = (double) (in1 - in0) / outSize; if (filterscale < 1.0) { filterscale = 1.0; } @@ -163,7 +163,7 @@ precompute_coeffs(int inSize, int in0, int in1, int outSize, } for (xx = 0; xx < outSize; xx++) { - center = (xx + 0.5) * scale; + center = in0 + (xx + 0.5) * scale; ww = 0.0; ss = 1.0 / filterscale; xmin = (int) floor(center - support); From 44e443fa7588299c66569825d8896e68cd1f5a2f Mon Sep 17 00:00:00 2001 From: homm Date: Sat, 26 Nov 2016 01:38:55 +0300 Subject: [PATCH 06/28] fix int boundaries --- Tests/test_image_resample.py | 12 ++++++++++++ libImaging/Resample.c | 12 ++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 680f0ba73..680633f2c 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -399,6 +399,18 @@ class CoreResampleRoiTest(PillowTestCase): self.assert_image_equal(tile1, tile2) + def test_subsample(self): + im = hopper() + reference = im.crop((0, 0, 125, 125)).resize((26, 26), Image.BICUBIC) + supersampled = im.resize((32, 32), Image.BOX) + without_roi = supersampled.resize((26, 26), Image.BICUBIC) + with_roi = supersampled.resize((26, 26), Image.BICUBIC, (0, 0, 31.25, 31.25)) + + self.assert_image_similar(reference, with_roi, 12) + + with self.assertRaisesRegexp(AssertionError, "difference 3\d\."): + self.assert_image_similar(reference, without_roi, 10) + if __name__ == '__main__': unittest.main() diff --git a/libImaging/Resample.c b/libImaging/Resample.c index fd29a1a93..a3c12c881 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -125,7 +125,7 @@ static inline UINT8 clip8(int in) int -precompute_coeffs(int inSize, int in0, int in1, int outSize, +precompute_coeffs(int inSize, float in0, float in1, int outSize, struct filter *filterp, int **xboundsp, double **kkp) { double support, scale, filterscale; double center, ww, ss; @@ -238,7 +238,7 @@ normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp) Imaging -ImagingResampleHorizontal_8bpc(Imaging imIn, int x0, int x1, int xsize, +ImagingResampleHorizontal_8bpc(Imaging imIn, float x0, float x1, int xsize, struct filter *filterp) { ImagingSectionCookie cookie; @@ -345,7 +345,7 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int x0, int x1, int xsize, Imaging -ImagingResampleVertical_8bpc(Imaging imIn, int y0, int y1, int ysize, +ImagingResampleVertical_8bpc(Imaging imIn, float y0, float y1, int ysize, struct filter *filterp) { ImagingSectionCookie cookie; @@ -452,7 +452,7 @@ ImagingResampleVertical_8bpc(Imaging imIn, int y0, int y1, int ysize, Imaging -ImagingResampleHorizontal_32bpc(Imaging imIn, int x0, int x1, int xsize, +ImagingResampleHorizontal_32bpc(Imaging imIn, float x0, float x1, int xsize, struct filter *filterp) { ImagingSectionCookie cookie; @@ -514,7 +514,7 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, int x0, int x1, int xsize, Imaging -ImagingResampleVertical_32bpc(Imaging imIn, int y0, int y1, int ysize, +ImagingResampleVertical_32bpc(Imaging imIn, float y0, float y1, int ysize, struct filter *filterp) { ImagingSectionCookie cookie; @@ -575,7 +575,7 @@ ImagingResampleVertical_32bpc(Imaging imIn, int y0, int y1, int ysize, } -typedef Imaging (*ResampleFunction)(Imaging imIn, int x0, int x1, int xsize, +typedef Imaging (*ResampleFunction)(Imaging imIn, float x0, float x1, int xsize, struct filter *filterp); From 4079c7f192fb73856360d2ae6b8ccb79a2021056 Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 30 Nov 2016 05:27:20 +0300 Subject: [PATCH 07/28] allocate new image outside of ResampleHorizontal/ResampleVertical --- libImaging/Resample.c | 87 ++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 54 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index a3c12c881..8abb63fb4 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -238,11 +238,10 @@ normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp) Imaging -ImagingResampleHorizontal_8bpc(Imaging imIn, float x0, float x1, int xsize, - struct filter *filterp) +ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, + int xsize, struct filter *filterp) { ImagingSectionCookie cookie; - Imaging imOut; int ss0, ss1, ss2, ss3; int xx, yy, x, kmax, xmin, xmax; int *xbounds; @@ -262,13 +261,6 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, float x0, float x1, int xsize, return (Imaging) ImagingError_MemoryError(); } - imOut = ImagingNew(imIn->mode, xsize, imIn->ysize); - if ( ! imOut) { - free(kk); - free(xbounds); - return NULL; - } - ImagingSectionEnter(&cookie); if (imIn->image8) { for (yy = 0; yy < imOut->ysize; yy++) { @@ -336,8 +328,8 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, float x0, float x1, int xsize, } } } - ImagingSectionLeave(&cookie); + free(kk); free(xbounds); return imOut; @@ -345,11 +337,10 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, float x0, float x1, int xsize, Imaging -ImagingResampleVertical_8bpc(Imaging imIn, float y0, float y1, int ysize, - struct filter *filterp) +ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, + int ysize, struct filter *filterp) { ImagingSectionCookie cookie; - Imaging imOut; int ss0, ss1, ss2, ss3; int xx, yy, y, kmax, ymin, ymax; int *xbounds; @@ -369,13 +360,6 @@ ImagingResampleVertical_8bpc(Imaging imIn, float y0, float y1, int ysize, return (Imaging) ImagingError_MemoryError(); } - imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); - if ( ! imOut) { - free(kk); - free(xbounds); - return NULL; - } - ImagingSectionEnter(&cookie); if (imIn->image8) { for (yy = 0; yy < ysize; yy++) { @@ -443,8 +427,8 @@ ImagingResampleVertical_8bpc(Imaging imIn, float y0, float y1, int ysize, } } } - ImagingSectionLeave(&cookie); + free(kk); free(xbounds); return imOut; @@ -452,11 +436,10 @@ ImagingResampleVertical_8bpc(Imaging imIn, float y0, float y1, int ysize, Imaging -ImagingResampleHorizontal_32bpc(Imaging imIn, float x0, float x1, int xsize, - struct filter *filterp) +ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, float x0, float x1, + int xsize, struct filter *filterp) { ImagingSectionCookie cookie; - Imaging imOut; double ss; int xx, yy, x, kmax, xmin, xmax; int *xbounds; @@ -468,13 +451,6 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, float x0, float x1, int xsize, return (Imaging) ImagingError_MemoryError(); } - imOut = ImagingNew(imIn->mode, xsize, imIn->ysize); - if ( ! imOut) { - free(kk); - free(xbounds); - return NULL; - } - ImagingSectionEnter(&cookie); switch(imIn->type) { case IMAGING_TYPE_INT32: @@ -505,8 +481,8 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, float x0, float x1, int xsize, } break; } - ImagingSectionLeave(&cookie); + free(kk); free(xbounds); return imOut; @@ -514,11 +490,10 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, float x0, float x1, int xsize, Imaging -ImagingResampleVertical_32bpc(Imaging imIn, float y0, float y1, int ysize, - struct filter *filterp) +ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, float y0, float y1, + int ysize, struct filter *filterp) { ImagingSectionCookie cookie; - Imaging imOut; double ss; int xx, yy, y, kmax, ymin, ymax; int *xbounds; @@ -530,13 +505,6 @@ ImagingResampleVertical_32bpc(Imaging imIn, float y0, float y1, int ysize, return (Imaging) ImagingError_MemoryError(); } - imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); - if ( ! imOut) { - free(kk); - free(xbounds); - return NULL; - } - ImagingSectionEnter(&cookie); switch(imIn->type) { case IMAGING_TYPE_INT32: @@ -567,16 +535,16 @@ ImagingResampleVertical_32bpc(Imaging imIn, float y0, float y1, int ysize, } break; } - ImagingSectionLeave(&cookie); + free(kk); free(xbounds); return imOut; } -typedef Imaging (*ResampleFunction)(Imaging imIn, float x0, float x1, int xsize, - struct filter *filterp); +typedef Imaging (*ResampleFunction)(Imaging imOut, Imaging imIn, + float x0, float x1, int xsize, struct filter *filterp); Imaging @@ -637,21 +605,32 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float roi[4]) /* two-pass resize, first pass */ if (roi[0] || roi[2] != xsize) { - imTemp = ResampleHorizontal(imIn, roi[0], roi[2], xsize, filterp); - if ( ! imTemp) + imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); + if ( ! imTemp) { return NULL; + } + if ( ! ResampleHorizontal(imTemp, imIn, roi[0], roi[2], xsize, filterp)) { + ImagingDelete(imTemp); + return NULL; + } imOut = imIn = imTemp; } /* second pass */ if (roi[1] || roi[3] != ysize) { - /* imIn can be the original image or horizontally resampled one */ - imOut = ResampleVertical(imIn, roi[1], roi[3], ysize, filterp); - /* it's safe to call ImagingDelete with empty value - if there was no previous step. */ - ImagingDelete(imTemp); - if ( ! imOut) + imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); + if ( ! imOut) { return NULL; + } + /* imIn can be the original image or horizontally resampled one */ + if ( ! ResampleVertical(imOut, imIn, roi[1], roi[3], ysize, filterp)) { + ImagingDelete(imTemp); + ImagingDelete(imOut); + return NULL; + } + /* it's safe to call ImagingDelete with empty value + if previous step was not performed. */ + ImagingDelete(imTemp); } /* none of the previous steps are performed, copying */ From 276fdbc78db9557af8e7ef644cb6b824936d741f Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 30 Nov 2016 19:56:55 +0300 Subject: [PATCH 08/28] Revert "remove deprecated since Pillow 2.7 Image.im.stretch() method" This reverts commit e7569a1ee9d7a67964f1d6d984530f83e9f56778. --- _imaging.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_imaging.c b/_imaging.c index 4da764d01..40159c6a2 100644 --- a/_imaging.c +++ b/_imaging.c @@ -2996,6 +2996,9 @@ static struct PyMethodDef methods[] = { {"rankfilter", (PyCFunction)_rankfilter, 1}, #endif {"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}, {"transpose", (PyCFunction)_transpose, 1}, {"transform2", (PyCFunction)_transform2, 1}, From 6def4bfc7337017a7c9e18149ca5de8fd1589ddf Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 30 Nov 2016 20:01:28 +0300 Subject: [PATCH 09/28] =?UTF-8?q?roi=20=E2=86=92=20box?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PIL/Image.py | 8 ++++---- Tests/test_image_resample.py | 12 ++++++------ _imaging.c | 26 +++++++++++++------------- libImaging/Imaging.h | 2 +- libImaging/Resample.c | 10 +++++----- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 2434d54a7..9814a7392 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1519,7 +1519,7 @@ class Image(object): return self.pyaccess.putpixel(xy, value) return self.im.putpixel(xy, value) - def resize(self, size, resample=NEAREST, roi=None): + def resize(self, size, resample=NEAREST, box=None): """ Returns a resized copy of this image. @@ -1555,10 +1555,10 @@ class Image(object): if self.mode == 'RGBA': return self.convert('RGBa').resize(size, resample).convert('RGBA') - if roi is None: - roi = (0, 0) + self.size + if box is None: + box = (0, 0) + self.size - return self._new(self.im.resize(size, resample, roi)) + return self._new(self.im.resize(size, resample, box)) def rotate(self, angle, resample=NEAREST, expand=0): """ diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 680633f2c..3d37bddde 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -390,9 +390,9 @@ class CoreResampleRoiTest(PillowTestCase): for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, Image.BICUBIC, Image.LANCZOS): - roi = (o[0] * sc[0], o[1] * sc[1], + box = (o[0] * sc[0], o[1] * sc[1], (o[0] + size[0]) * sc[0], (o[1] + size[1]) * sc[1]) - tile1 = im.resize(size, resample, roi) + tile1 = im.resize(size, resample, box) big_size = (im.width // sc[0], im.height // sc[1]) tile2 = im.resize(big_size, resample)\ .crop(o + (o[0] + size[0], o[1] + size[1])) @@ -403,13 +403,13 @@ class CoreResampleRoiTest(PillowTestCase): im = hopper() reference = im.crop((0, 0, 125, 125)).resize((26, 26), Image.BICUBIC) supersampled = im.resize((32, 32), Image.BOX) - without_roi = supersampled.resize((26, 26), Image.BICUBIC) - with_roi = supersampled.resize((26, 26), Image.BICUBIC, (0, 0, 31.25, 31.25)) + without_box = supersampled.resize((26, 26), Image.BICUBIC) + with_box = supersampled.resize((26, 26), Image.BICUBIC, (0, 0, 31.25, 31.25)) - self.assert_image_similar(reference, with_roi, 12) + self.assert_image_similar(reference, with_box, 12) with self.assertRaisesRegexp(AssertionError, "difference 3\d\."): - self.assert_image_similar(reference, without_roi, 10) + self.assert_image_similar(reference, without_box, 10) if __name__ == '__main__': diff --git a/_imaging.c b/_imaging.c index 40159c6a2..818826f37 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1534,43 +1534,43 @@ _resize(ImagingObject* self, PyObject* args) int xsize, ysize; int filter = IMAGING_TRANSFORM_NEAREST; - float roi[4] = {0, 0, 0, 0}; + float box[4] = {0, 0, 0, 0}; imIn = self->image; - roi[2] = imIn->xsize; - roi[3] = imIn->ysize; + box[2] = imIn->xsize; + box[3] = imIn->ysize; if (!PyArg_ParseTuple(args, "(ii)|i(ffff)", &xsize, &ysize, &filter, - &roi[0], &roi[1], &roi[2], &roi[3])) + &box[0], &box[1], &box[2], &box[3])) return NULL; if (xsize < 1 || ysize < 1) { return ImagingError_ValueError("height and width must be > 0"); } - if (roi[0] < 0 || roi[1] < 0) { + if (box[0] < 0 || box[1] < 0) { return ImagingError_ValueError("region of interest offset can't be negative"); } - if (roi[2] > imIn->xsize || roi[3] > imIn->ysize) { + if (box[2] > imIn->xsize || box[3] > imIn->ysize) { return ImagingError_ValueError("region of interest can't exceed original image size"); } - if (roi[2] - roi[0] <= 0 || roi[3] - roi[1] <= 0) { + if (box[2] - box[0] <= 0 || box[3] - box[1] <= 0) { return ImagingError_ValueError("region of interest can't be empty"); } - if (roi[0] == 0 && roi[1] == 0 && roi[2] == xsize && roi[3] == ysize) { + if (box[0] == 0 && box[1] == 0 && box[2] == xsize && box[3] == ysize) { imOut = ImagingCopy(imIn); } else if (filter == IMAGING_TRANSFORM_NEAREST) { double a[6]; memset(a, 0, sizeof a); - a[0] = (double) (roi[2] - roi[0]) / xsize; - a[4] = (double) (roi[3] - roi[1]) / ysize; - a[2] = roi[0]; - a[5] = roi[1]; + a[0] = (double) (box[2] - box[0]) / xsize; + a[4] = (double) (box[3] - box[1]) / ysize; + a[2] = box[0]; + a[5] = box[1]; imOut = ImagingNew(imIn->mode, xsize, ysize); @@ -1580,7 +1580,7 @@ _resize(ImagingObject* self, PyObject* args) a, filter, 1); } else { - imOut = ImagingResample(imIn, xsize, ysize, filter, roi); + imOut = ImagingResample(imIn, xsize, ysize, filter, box); } return PyImagingNew(imOut); diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index dcdcb85b9..686bb82ec 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -290,7 +290,7 @@ 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 roi[4]); +extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]); extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn); extern Imaging ImagingTransform( Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1, diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 8abb63fb4..9b055ed17 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -548,7 +548,7 @@ typedef Imaging (*ResampleFunction)(Imaging imOut, Imaging imIn, Imaging -ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float roi[4]) +ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) { Imaging imTemp = NULL; Imaging imOut = NULL; @@ -604,12 +604,12 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float roi[4]) } /* two-pass resize, first pass */ - if (roi[0] || roi[2] != xsize) { + if (box[0] || box[2] != xsize) { imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); if ( ! imTemp) { return NULL; } - if ( ! ResampleHorizontal(imTemp, imIn, roi[0], roi[2], xsize, filterp)) { + if ( ! ResampleHorizontal(imTemp, imIn, box[0], box[2], xsize, filterp)) { ImagingDelete(imTemp); return NULL; } @@ -617,13 +617,13 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float roi[4]) } /* second pass */ - if (roi[1] || roi[3] != ysize) { + if (box[1] || box[3] != ysize) { imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); if ( ! imOut) { return NULL; } /* imIn can be the original image or horizontally resampled one */ - if ( ! ResampleVertical(imOut, imIn, roi[1], roi[3], ysize, filterp)) { + if ( ! ResampleVertical(imOut, imIn, box[1], box[3], ysize, filterp)) { ImagingDelete(imTemp); ImagingDelete(imOut); return NULL; From 9842505a60abe1247d804ef12295c18abbc88cba Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 30 Nov 2016 20:43:54 +0300 Subject: [PATCH 10/28] remove fifth argument from ResampleFunction --- libImaging/Resample.c | 50 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 9b055ed17..0691d7c56 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -239,7 +239,7 @@ normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp) Imaging ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, - int xsize, struct filter *filterp) + struct filter *filterp) { ImagingSectionCookie cookie; int ss0, ss1, ss2, ss3; @@ -248,13 +248,13 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, INT32 *k, *kk; double *prekk; - kmax = precompute_coeffs(imIn->xsize, x0, x1, xsize, filterp, + kmax = precompute_coeffs(imIn->xsize, x0, x1, imOut->xsize, filterp, &xbounds, &prekk); if ( ! kmax) { return (Imaging) ImagingError_MemoryError(); } - kmax = normalize_coeffs_8bpc(xsize, kmax, prekk, &kk); + kmax = normalize_coeffs_8bpc(imOut->xsize, kmax, prekk, &kk); free(prekk); if ( ! kmax) { free(xbounds); @@ -264,7 +264,7 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, ImagingSectionEnter(&cookie); if (imIn->image8) { for (yy = 0; yy < imOut->ysize; yy++) { - for (xx = 0; xx < xsize; xx++) { + for (xx = 0; xx < imOut->xsize; xx++) { xmin = xbounds[xx * 2 + 0]; xmax = xbounds[xx * 2 + 1]; k = &kk[xx * kmax]; @@ -277,7 +277,7 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, } else if (imIn->type == IMAGING_TYPE_UINT8) { if (imIn->bands == 2) { for (yy = 0; yy < imOut->ysize; yy++) { - for (xx = 0; xx < xsize; xx++) { + for (xx = 0; xx < imOut->xsize; xx++) { xmin = xbounds[xx * 2 + 0]; xmax = xbounds[xx * 2 + 1]; k = &kk[xx * kmax]; @@ -292,7 +292,7 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, } } else if (imIn->bands == 3) { for (yy = 0; yy < imOut->ysize; yy++) { - for (xx = 0; xx < xsize; xx++) { + for (xx = 0; xx < imOut->xsize; xx++) { xmin = xbounds[xx * 2 + 0]; xmax = xbounds[xx * 2 + 1]; k = &kk[xx * kmax]; @@ -309,7 +309,7 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, } } else { for (yy = 0; yy < imOut->ysize; yy++) { - for (xx = 0; xx < xsize; xx++) { + for (xx = 0; xx < imOut->xsize; xx++) { xmin = xbounds[xx * 2 + 0]; xmax = xbounds[xx * 2 + 1]; k = &kk[xx * kmax]; @@ -338,7 +338,7 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, Imaging ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, - int ysize, struct filter *filterp) + struct filter *filterp) { ImagingSectionCookie cookie; int ss0, ss1, ss2, ss3; @@ -347,13 +347,13 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, INT32 *k, *kk; double *prekk; - kmax = precompute_coeffs(imIn->ysize, y0, y1, ysize, filterp, + kmax = precompute_coeffs(imIn->ysize, y0, y1, imOut->ysize, filterp, &xbounds, &prekk); if ( ! kmax) { return (Imaging) ImagingError_MemoryError(); } - kmax = normalize_coeffs_8bpc(ysize, kmax, prekk, &kk); + kmax = normalize_coeffs_8bpc(imOut->ysize, kmax, prekk, &kk); free(prekk); if ( ! kmax) { free(xbounds); @@ -362,7 +362,7 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, ImagingSectionEnter(&cookie); if (imIn->image8) { - for (yy = 0; yy < ysize; yy++) { + for (yy = 0; yy < imOut->ysize; yy++) { k = &kk[yy * kmax]; ymin = xbounds[yy * 2 + 0]; ymax = xbounds[yy * 2 + 1]; @@ -375,7 +375,7 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, } } else if (imIn->type == IMAGING_TYPE_UINT8) { if (imIn->bands == 2) { - for (yy = 0; yy < ysize; yy++) { + for (yy = 0; yy < imOut->ysize; yy++) { k = &kk[yy * kmax]; ymin = xbounds[yy * 2 + 0]; ymax = xbounds[yy * 2 + 1]; @@ -390,7 +390,7 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, } } } else if (imIn->bands == 3) { - for (yy = 0; yy < ysize; yy++) { + for (yy = 0; yy < imOut->ysize; yy++) { k = &kk[yy * kmax]; ymin = xbounds[yy * 2 + 0]; ymax = xbounds[yy * 2 + 1]; @@ -407,7 +407,7 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, } } } else { - for (yy = 0; yy < ysize; yy++) { + for (yy = 0; yy < imOut->ysize; yy++) { k = &kk[yy * kmax]; ymin = xbounds[yy * 2 + 0]; ymax = xbounds[yy * 2 + 1]; @@ -437,7 +437,7 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, Imaging ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, float x0, float x1, - int xsize, struct filter *filterp) + struct filter *filterp) { ImagingSectionCookie cookie; double ss; @@ -445,7 +445,7 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, float x0, float x1, int *xbounds; double *k, *kk; - kmax = precompute_coeffs(imIn->xsize, x0, x1, xsize, filterp, + kmax = precompute_coeffs(imIn->xsize, x0, x1, imOut->xsize, filterp, &xbounds, &kk); if ( ! kmax) { return (Imaging) ImagingError_MemoryError(); @@ -455,7 +455,7 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, float x0, float x1, switch(imIn->type) { case IMAGING_TYPE_INT32: for (yy = 0; yy < imOut->ysize; yy++) { - for (xx = 0; xx < xsize; xx++) { + for (xx = 0; xx < imOut->xsize; xx++) { xmin = xbounds[xx * 2 + 0]; xmax = xbounds[xx * 2 + 1]; k = &kk[xx * kmax]; @@ -469,7 +469,7 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, float x0, float x1, case IMAGING_TYPE_FLOAT32: for (yy = 0; yy < imOut->ysize; yy++) { - for (xx = 0; xx < xsize; xx++) { + for (xx = 0; xx < imOut->xsize; xx++) { xmin = xbounds[xx * 2 + 0]; xmax = xbounds[xx * 2 + 1]; k = &kk[xx * kmax]; @@ -491,7 +491,7 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, float x0, float x1, Imaging ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, float y0, float y1, - int ysize, struct filter *filterp) + struct filter *filterp) { ImagingSectionCookie cookie; double ss; @@ -499,7 +499,7 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, float y0, float y1, int *xbounds; double *k, *kk; - kmax = precompute_coeffs(imIn->ysize, y0, y1, ysize, filterp, + kmax = precompute_coeffs(imIn->ysize, y0, y1, imOut->ysize, filterp, &xbounds, &kk); if ( ! kmax) { return (Imaging) ImagingError_MemoryError(); @@ -508,7 +508,7 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, float y0, float y1, ImagingSectionEnter(&cookie); switch(imIn->type) { case IMAGING_TYPE_INT32: - for (yy = 0; yy < ysize; yy++) { + for (yy = 0; yy < imOut->ysize; yy++) { ymin = xbounds[yy * 2 + 0]; ymax = xbounds[yy * 2 + 1]; k = &kk[yy * kmax]; @@ -522,7 +522,7 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, float y0, float y1, break; case IMAGING_TYPE_FLOAT32: - for (yy = 0; yy < ysize; yy++) { + for (yy = 0; yy < imOut->ysize; yy++) { ymin = xbounds[yy * 2 + 0]; ymax = xbounds[yy * 2 + 1]; k = &kk[yy * kmax]; @@ -544,7 +544,7 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, float y0, float y1, typedef Imaging (*ResampleFunction)(Imaging imOut, Imaging imIn, - float x0, float x1, int xsize, struct filter *filterp); + float x0, float x1, struct filter *filterp); Imaging @@ -609,7 +609,7 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) if ( ! imTemp) { return NULL; } - if ( ! ResampleHorizontal(imTemp, imIn, box[0], box[2], xsize, filterp)) { + if ( ! ResampleHorizontal(imTemp, imIn, box[0], box[2], filterp)) { ImagingDelete(imTemp); return NULL; } @@ -623,7 +623,7 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) return NULL; } /* imIn can be the original image or horizontally resampled one */ - if ( ! ResampleVertical(imOut, imIn, box[1], box[3], ysize, filterp)) { + if ( ! ResampleVertical(imOut, imIn, box[1], box[3], filterp)) { ImagingDelete(imTemp); ImagingDelete(imOut); return NULL; From 2c2bdd80efea2a8ab1ae6e2dd7a6d3ead7c59f8f Mon Sep 17 00:00:00 2001 From: homm Date: Wed, 30 Nov 2016 21:59:21 +0300 Subject: [PATCH 11/28] split out ImagingResampleInner from ImagingResample --- libImaging/Resample.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 0691d7c56..39c7d857f 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -547,11 +547,16 @@ typedef Imaging (*ResampleFunction)(Imaging imOut, Imaging imIn, float x0, float x1, struct filter *filterp); +Imaging +ImagingResampleInner(Imaging imIn, int xsize, int ysize, + struct filter *filterp, float box[4], + ResampleFunction ResampleHorizontal, + ResampleFunction ResampleVertical); + + Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) { - Imaging imTemp = NULL; - Imaging imOut = NULL; struct filter *filterp; ResampleFunction ResampleHorizontal; ResampleFunction ResampleVertical; @@ -603,6 +608,20 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) ); } + return ImagingResampleInner(imIn, xsize, ysize, filterp, box, + ResampleHorizontal, ResampleVertical); +} + + +Imaging +ImagingResampleInner(Imaging imIn, int xsize, int ysize, + struct filter *filterp, float box[4], + ResampleFunction ResampleHorizontal, + ResampleFunction ResampleVertical) +{ + Imaging imTemp = NULL; + Imaging imOut = NULL; + /* two-pass resize, first pass */ if (box[0] || box[2] != xsize) { imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); From 02e1ae3d8f43422cc502207d60022b2f17c98fe8 Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 1 Dec 2016 03:26:10 +0300 Subject: [PATCH 12/28] move precompute_coeffs to ImagingResampleInner --- libImaging/Resample.c | 102 +++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 57 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 39c7d857f..f3a52bbf1 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -238,26 +238,16 @@ normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp) Imaging -ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, - struct filter *filterp) +ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, + int kmax, int *xbounds, double *prekk) { ImagingSectionCookie cookie; int ss0, ss1, ss2, ss3; - int xx, yy, x, kmax, xmin, xmax; - int *xbounds; + int xx, yy, x, xmin, xmax; INT32 *k, *kk; - double *prekk; - - kmax = precompute_coeffs(imIn->xsize, x0, x1, imOut->xsize, filterp, - &xbounds, &prekk); - if ( ! kmax) { - return (Imaging) ImagingError_MemoryError(); - } kmax = normalize_coeffs_8bpc(imOut->xsize, kmax, prekk, &kk); - free(prekk); if ( ! kmax) { - free(xbounds); return (Imaging) ImagingError_MemoryError(); } @@ -331,32 +321,21 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, float x0, float x1, ImagingSectionLeave(&cookie); free(kk); - free(xbounds); return imOut; } Imaging -ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, - struct filter *filterp) +ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, + int kmax, int *xbounds, double *prekk) { ImagingSectionCookie cookie; int ss0, ss1, ss2, ss3; - int xx, yy, y, kmax, ymin, ymax; - int *xbounds; + int xx, yy, y, ymin, ymax; INT32 *k, *kk; - double *prekk; - - kmax = precompute_coeffs(imIn->ysize, y0, y1, imOut->ysize, filterp, - &xbounds, &prekk); - if ( ! kmax) { - return (Imaging) ImagingError_MemoryError(); - } kmax = normalize_coeffs_8bpc(imOut->ysize, kmax, prekk, &kk); - free(prekk); if ( ! kmax) { - free(xbounds); return (Imaging) ImagingError_MemoryError(); } @@ -430,26 +409,18 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, float y0, float y1, ImagingSectionLeave(&cookie); free(kk); - free(xbounds); return imOut; } Imaging -ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, float x0, float x1, - struct filter *filterp) +ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, + int kmax, int *xbounds, double *kk) { ImagingSectionCookie cookie; double ss; - int xx, yy, x, kmax, xmin, xmax; - int *xbounds; - double *k, *kk; - - kmax = precompute_coeffs(imIn->xsize, x0, x1, imOut->xsize, filterp, - &xbounds, &kk); - if ( ! kmax) { - return (Imaging) ImagingError_MemoryError(); - } + int xx, yy, x, xmin, xmax; + double *k; ImagingSectionEnter(&cookie); switch(imIn->type) { @@ -483,27 +454,18 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, float x0, float x1, } ImagingSectionLeave(&cookie); - free(kk); - free(xbounds); return imOut; } Imaging -ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, float y0, float y1, - struct filter *filterp) +ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, + int kmax, int *xbounds, double *kk) { ImagingSectionCookie cookie; double ss; - int xx, yy, y, kmax, ymin, ymax; - int *xbounds; - double *k, *kk; - - kmax = precompute_coeffs(imIn->ysize, y0, y1, imOut->ysize, filterp, - &xbounds, &kk); - if ( ! kmax) { - return (Imaging) ImagingError_MemoryError(); - } + int xx, yy, y, ymin, ymax; + double *k; ImagingSectionEnter(&cookie); switch(imIn->type) { @@ -537,14 +499,12 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, float y0, float y1, } ImagingSectionLeave(&cookie); - free(kk); - free(xbounds); return imOut; } typedef Imaging (*ResampleFunction)(Imaging imOut, Imaging imIn, - float x0, float x1, struct filter *filterp); + int kmax, int *xbounds, double *kk); Imaging @@ -622,34 +582,62 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, Imaging imTemp = NULL; Imaging imOut = NULL; + int kmax; + int *xbounds; + double *kk; + /* two-pass resize, first pass */ if (box[0] || box[2] != xsize) { + kmax = precompute_coeffs(imIn->xsize, box[0], box[2], xsize, filterp, + &xbounds, &kk); + if ( ! kmax) { + return (Imaging) ImagingError_MemoryError(); + } + imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); if ( ! imTemp) { + free(xbounds); + free(kk); return NULL; } - if ( ! ResampleHorizontal(imTemp, imIn, box[0], box[2], filterp)) { + if ( ! ResampleHorizontal(imTemp, imIn, kmax, xbounds, kk)) { ImagingDelete(imTemp); + free(xbounds); + free(kk); return NULL; } + free(xbounds); + free(kk); imOut = imIn = imTemp; } /* second pass */ if (box[1] || box[3] != ysize) { + kmax = precompute_coeffs(imIn->ysize, box[1], box[3], ysize, filterp, + &xbounds, &kk); + if ( ! kmax) { + return (Imaging) ImagingError_MemoryError(); + } + imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); if ( ! imOut) { + free(xbounds); + free(kk); return NULL; } /* imIn can be the original image or horizontally resampled one */ - if ( ! ResampleVertical(imOut, imIn, box[1], box[3], filterp)) { + if ( ! ResampleVertical(imOut, imIn, kmax, xbounds, kk)) { ImagingDelete(imTemp); ImagingDelete(imOut); + free(xbounds); + free(kk); return NULL; } /* it's safe to call ImagingDelete with empty value if previous step was not performed. */ ImagingDelete(imTemp); + free(xbounds); + free(kk); } /* none of the previous steps are performed, copying */ From 8d7355e733582023c72777f7934dfdb4ec3eeb0e Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 1 Dec 2016 04:02:22 +0300 Subject: [PATCH 13/28] do not allocate memory in normalize_coeffs_8bpc --- libImaging/Resample.c | 62 +++++++++++-------------------------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index f3a52bbf1..9977c5838 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -211,17 +211,14 @@ precompute_coeffs(int inSize, float in0, float in1, int outSize, } -int -normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp) +void +normalize_coeffs_8bpc(int outSize, int kmax, double *prekk) { int x; INT32 *kk; - /* malloc check ok, overflow checked in precompute_coeffs */ - kk = malloc(outSize * kmax * sizeof(INT32)); - if ( ! kk) { - return 0; - } + // We are using the same buffer for normalized coefficients + kk = (INT32 *)prekk; for (x = 0; x < outSize * kmax; x++) { if (prekk[x] < 0) { @@ -230,14 +227,11 @@ normalize_coeffs_8bpc(int outSize, int kmax, double *prekk, INT32 **kkp) kk[x] = (int) (0.5 + prekk[x] * (1 << PRECISION_BITS)); } } - - *kkp = kk; - return kmax; } -Imaging +void ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, int kmax, int *xbounds, double *prekk) { @@ -246,10 +240,8 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, int xx, yy, x, xmin, xmax; INT32 *k, *kk; - kmax = normalize_coeffs_8bpc(imOut->xsize, kmax, prekk, &kk); - if ( ! kmax) { - return (Imaging) ImagingError_MemoryError(); - } + kk = (INT32 *) prekk; + normalize_coeffs_8bpc(imOut->xsize, kmax, prekk); ImagingSectionEnter(&cookie); if (imIn->image8) { @@ -319,13 +311,10 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, } } ImagingSectionLeave(&cookie); - - free(kk); - return imOut; } -Imaging +void ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, int kmax, int *xbounds, double *prekk) { @@ -334,10 +323,8 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, int xx, yy, y, ymin, ymax; INT32 *k, *kk; - kmax = normalize_coeffs_8bpc(imOut->ysize, kmax, prekk, &kk); - if ( ! kmax) { - return (Imaging) ImagingError_MemoryError(); - } + kk = (INT32 *) prekk; + normalize_coeffs_8bpc(imOut->ysize, kmax, prekk); ImagingSectionEnter(&cookie); if (imIn->image8) { @@ -407,13 +394,10 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, } } ImagingSectionLeave(&cookie); - - free(kk); - return imOut; } -Imaging +void ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, int kmax, int *xbounds, double *kk) { @@ -453,12 +437,10 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, break; } ImagingSectionLeave(&cookie); - - return imOut; } -Imaging +void ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, int kmax, int *xbounds, double *kk) { @@ -498,12 +480,10 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, break; } ImagingSectionLeave(&cookie); - - return imOut; } -typedef Imaging (*ResampleFunction)(Imaging imOut, Imaging imIn, +typedef void (*ResampleFunction)(Imaging imOut, Imaging imIn, int kmax, int *xbounds, double *kk); @@ -600,12 +580,7 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, free(kk); return NULL; } - if ( ! ResampleHorizontal(imTemp, imIn, kmax, xbounds, kk)) { - ImagingDelete(imTemp); - free(xbounds); - free(kk); - return NULL; - } + ResampleHorizontal(imTemp, imIn, kmax, xbounds, kk); free(xbounds); free(kk); imOut = imIn = imTemp; @@ -616,6 +591,7 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, kmax = precompute_coeffs(imIn->ysize, box[1], box[3], ysize, filterp, &xbounds, &kk); if ( ! kmax) { + ImagingDelete(imTemp); return (Imaging) ImagingError_MemoryError(); } @@ -626,13 +602,7 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, return NULL; } /* imIn can be the original image or horizontally resampled one */ - if ( ! ResampleVertical(imOut, imIn, kmax, xbounds, kk)) { - ImagingDelete(imTemp); - ImagingDelete(imOut); - free(xbounds); - free(kk); - return NULL; - } + ResampleVertical(imOut, imIn, kmax, xbounds, kk); /* it's safe to call ImagingDelete with empty value if previous step was not performed. */ ImagingDelete(imTemp); From c7161af9248f7124044c03496a255bc7c3a45b74 Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 1 Dec 2016 04:10:58 +0300 Subject: [PATCH 14/28] set error type when it happens --- libImaging/Resample.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 9977c5838..3f4db64d2 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -146,19 +146,24 @@ precompute_coeffs(int inSize, float in0, float in1, int outSize, kmax = (int) ceil(support) * 2 + 1; // check for overflow - if (outSize > INT_MAX / (kmax * sizeof(double))) + if (outSize > INT_MAX / (kmax * sizeof(double))) { + ImagingError_MemoryError(); return 0; + } /* coefficient buffer */ /* malloc check ok, overflow checked above */ kk = malloc(outSize * kmax * sizeof(double)); - if ( ! kk) + if ( ! kk) { + ImagingError_MemoryError(); return 0; + } /* malloc check ok, kmax*sizeof(double) > 2*sizeof(int) */ xbounds = malloc(outSize * 2 * sizeof(int)); if ( ! xbounds) { free(kk); + ImagingError_MemoryError(); return 0; } @@ -571,7 +576,7 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, kmax = precompute_coeffs(imIn->xsize, box[0], box[2], xsize, filterp, &xbounds, &kk); if ( ! kmax) { - return (Imaging) ImagingError_MemoryError(); + return NULL; } imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); @@ -592,7 +597,7 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, &xbounds, &kk); if ( ! kmax) { ImagingDelete(imTemp); - return (Imaging) ImagingError_MemoryError(); + return NULL; } imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); From 4ff7afbe163801d776448f0f4777e6a9e79531dd Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 1 Dec 2016 13:46:12 +0300 Subject: [PATCH 15/28] =?UTF-8?q?xbounds=20=E2=86=92=20bounds=20kmax=20?= =?UTF-8?q?=E2=86=92=20ksize?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libImaging/Resample.c | 154 +++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 76 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 3f4db64d2..2743bedf9 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -126,11 +126,11 @@ static inline UINT8 clip8(int in) int precompute_coeffs(int inSize, float in0, float in1, int outSize, - struct filter *filterp, int **xboundsp, double **kkp) { + struct filter *filterp, int **boundsp, double **kkp) { double support, scale, filterscale; double center, ww, ss; - int xx, x, kmax, xmin, xmax; - int *xbounds; + int xx, x, ksize, xmin, xmax; + int *bounds; double *kk, *k; /* prepare for horizontal stretch */ @@ -143,25 +143,25 @@ precompute_coeffs(int inSize, float in0, float in1, int outSize, support = filterp->support * filterscale; /* maximum number of coeffs */ - kmax = (int) ceil(support) * 2 + 1; + ksize = (int) ceil(support) * 2 + 1; // check for overflow - if (outSize > INT_MAX / (kmax * sizeof(double))) { + if (outSize > INT_MAX / (ksize * sizeof(double))) { ImagingError_MemoryError(); return 0; } /* coefficient buffer */ /* malloc check ok, overflow checked above */ - kk = malloc(outSize * kmax * sizeof(double)); + kk = malloc(outSize * ksize * sizeof(double)); if ( ! kk) { ImagingError_MemoryError(); return 0; } - /* malloc check ok, kmax*sizeof(double) > 2*sizeof(int) */ - xbounds = malloc(outSize * 2 * sizeof(int)); - if ( ! xbounds) { + /* malloc check ok, ksize*sizeof(double) > 2*sizeof(int) */ + bounds = malloc(outSize * 2 * sizeof(int)); + if ( ! bounds) { free(kk); ImagingError_MemoryError(); return 0; @@ -178,7 +178,7 @@ precompute_coeffs(int inSize, float in0, float in1, int outSize, if (xmax > inSize) xmax = inSize; xmax -= xmin; - k = &kk[xx * kmax]; + k = &kk[xx * ksize]; for (x = 0; x < xmax; x++) { double w = filterp->filter((x + xmin - center + 0.5) * ss); k[x] = w; @@ -204,28 +204,28 @@ precompute_coeffs(int inSize, float in0, float in1, int outSize, k[x] /= ww; } // Remaining values should stay empty if they are used despite of xmax. - for (; x < kmax; x++) { + for (; x < ksize; x++) { k[x] = 0; } - xbounds[xx * 2 + 0] = xmin; - xbounds[xx * 2 + 1] = xmax; + bounds[xx * 2 + 0] = xmin; + bounds[xx * 2 + 1] = xmax; } - *xboundsp = xbounds; + *boundsp = bounds; *kkp = kk; - return kmax; + return ksize; } void -normalize_coeffs_8bpc(int outSize, int kmax, double *prekk) +normalize_coeffs_8bpc(int outSize, int ksize, double *prekk) { int x; INT32 *kk; - // We are using the same buffer for normalized coefficients - kk = (INT32 *)prekk; + // use the same buffer for normalized coefficients + kk = (INT32 *) prekk; - for (x = 0; x < outSize * kmax; x++) { + for (x = 0; x < outSize * ksize; x++) { if (prekk[x] < 0) { kk[x] = (int) (-0.5 + prekk[x] * (1 << PRECISION_BITS)); } else { @@ -238,23 +238,24 @@ normalize_coeffs_8bpc(int outSize, int kmax, double *prekk) void ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, - int kmax, int *xbounds, double *prekk) + int ksize, int *bounds, double *prekk) { ImagingSectionCookie cookie; int ss0, ss1, ss2, ss3; int xx, yy, x, xmin, xmax; INT32 *k, *kk; + // use the same buffer for normalized coefficients kk = (INT32 *) prekk; - normalize_coeffs_8bpc(imOut->xsize, kmax, prekk); + normalize_coeffs_8bpc(imOut->xsize, ksize, prekk); ImagingSectionEnter(&cookie); if (imIn->image8) { for (yy = 0; yy < imOut->ysize; yy++) { for (xx = 0; xx < imOut->xsize; xx++) { - xmin = xbounds[xx * 2 + 0]; - xmax = xbounds[xx * 2 + 1]; - k = &kk[xx * kmax]; + xmin = bounds[xx * 2 + 0]; + xmax = bounds[xx * 2 + 1]; + k = &kk[xx * ksize]; ss0 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) ss0 += ((UINT8) imIn->image8[yy][x + xmin]) * k[x]; @@ -265,9 +266,9 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, if (imIn->bands == 2) { for (yy = 0; yy < imOut->ysize; yy++) { for (xx = 0; xx < imOut->xsize; xx++) { - xmin = xbounds[xx * 2 + 0]; - xmax = xbounds[xx * 2 + 1]; - k = &kk[xx * kmax]; + xmin = bounds[xx * 2 + 0]; + xmax = bounds[xx * 2 + 1]; + k = &kk[xx * ksize]; ss0 = ss3 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) { ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x]; @@ -280,9 +281,9 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, } else if (imIn->bands == 3) { for (yy = 0; yy < imOut->ysize; yy++) { for (xx = 0; xx < imOut->xsize; xx++) { - xmin = xbounds[xx * 2 + 0]; - xmax = xbounds[xx * 2 + 1]; - k = &kk[xx * kmax]; + xmin = bounds[xx * 2 + 0]; + xmax = bounds[xx * 2 + 1]; + k = &kk[xx * ksize]; ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) { ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x]; @@ -297,9 +298,9 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, } else { for (yy = 0; yy < imOut->ysize; yy++) { for (xx = 0; xx < imOut->xsize; xx++) { - xmin = xbounds[xx * 2 + 0]; - xmax = xbounds[xx * 2 + 1]; - k = &kk[xx * kmax]; + xmin = bounds[xx * 2 + 0]; + xmax = bounds[xx * 2 + 1]; + k = &kk[xx * ksize]; ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) { ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x]; @@ -321,22 +322,23 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, void ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, - int kmax, int *xbounds, double *prekk) + int ksize, int *bounds, double *prekk) { ImagingSectionCookie cookie; int ss0, ss1, ss2, ss3; int xx, yy, y, ymin, ymax; INT32 *k, *kk; + // use the same buffer for normalized coefficients kk = (INT32 *) prekk; - normalize_coeffs_8bpc(imOut->ysize, kmax, prekk); + normalize_coeffs_8bpc(imOut->ysize, ksize, prekk); ImagingSectionEnter(&cookie); if (imIn->image8) { for (yy = 0; yy < imOut->ysize; yy++) { - k = &kk[yy * kmax]; - ymin = xbounds[yy * 2 + 0]; - ymax = xbounds[yy * 2 + 1]; + k = &kk[yy * ksize]; + ymin = bounds[yy * 2 + 0]; + ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = 1 << (PRECISION_BITS -1); for (y = 0; y < ymax; y++) @@ -347,9 +349,9 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, } else if (imIn->type == IMAGING_TYPE_UINT8) { if (imIn->bands == 2) { for (yy = 0; yy < imOut->ysize; yy++) { - k = &kk[yy * kmax]; - ymin = xbounds[yy * 2 + 0]; - ymax = xbounds[yy * 2 + 1]; + k = &kk[yy * ksize]; + ymin = bounds[yy * 2 + 0]; + ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = ss3 = 1 << (PRECISION_BITS -1); for (y = 0; y < ymax; y++) { @@ -362,9 +364,9 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, } } else if (imIn->bands == 3) { for (yy = 0; yy < imOut->ysize; yy++) { - k = &kk[yy * kmax]; - ymin = xbounds[yy * 2 + 0]; - ymax = xbounds[yy * 2 + 1]; + k = &kk[yy * ksize]; + ymin = bounds[yy * 2 + 0]; + ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1); for (y = 0; y < ymax; y++) { @@ -379,9 +381,9 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, } } else { for (yy = 0; yy < imOut->ysize; yy++) { - k = &kk[yy * kmax]; - ymin = xbounds[yy * 2 + 0]; - ymax = xbounds[yy * 2 + 1]; + k = &kk[yy * ksize]; + ymin = bounds[yy * 2 + 0]; + ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1); for (y = 0; y < ymax; y++) { @@ -404,7 +406,7 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, void ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, - int kmax, int *xbounds, double *kk) + int ksize, int *bounds, double *kk) { ImagingSectionCookie cookie; double ss; @@ -416,9 +418,9 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, case IMAGING_TYPE_INT32: for (yy = 0; yy < imOut->ysize; yy++) { for (xx = 0; xx < imOut->xsize; xx++) { - xmin = xbounds[xx * 2 + 0]; - xmax = xbounds[xx * 2 + 1]; - k = &kk[xx * kmax]; + xmin = bounds[xx * 2 + 0]; + xmax = bounds[xx * 2 + 1]; + k = &kk[xx * ksize]; ss = 0.0; for (x = 0; x < xmax; x++) ss += IMAGING_PIXEL_I(imIn, x + xmin, yy) * k[x]; @@ -430,9 +432,9 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, case IMAGING_TYPE_FLOAT32: for (yy = 0; yy < imOut->ysize; yy++) { for (xx = 0; xx < imOut->xsize; xx++) { - xmin = xbounds[xx * 2 + 0]; - xmax = xbounds[xx * 2 + 1]; - k = &kk[xx * kmax]; + xmin = bounds[xx * 2 + 0]; + xmax = bounds[xx * 2 + 1]; + k = &kk[xx * ksize]; ss = 0.0; for (x = 0; x < xmax; x++) ss += IMAGING_PIXEL_F(imIn, x + xmin, yy) * k[x]; @@ -447,7 +449,7 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, void ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, - int kmax, int *xbounds, double *kk) + int ksize, int *bounds, double *kk) { ImagingSectionCookie cookie; double ss; @@ -458,9 +460,9 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, switch(imIn->type) { case IMAGING_TYPE_INT32: for (yy = 0; yy < imOut->ysize; yy++) { - ymin = xbounds[yy * 2 + 0]; - ymax = xbounds[yy * 2 + 1]; - k = &kk[yy * kmax]; + ymin = bounds[yy * 2 + 0]; + ymax = bounds[yy * 2 + 1]; + k = &kk[yy * ksize]; for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; for (y = 0; y < ymax; y++) @@ -472,9 +474,9 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, case IMAGING_TYPE_FLOAT32: for (yy = 0; yy < imOut->ysize; yy++) { - ymin = xbounds[yy * 2 + 0]; - ymax = xbounds[yy * 2 + 1]; - k = &kk[yy * kmax]; + ymin = bounds[yy * 2 + 0]; + ymax = bounds[yy * 2 + 1]; + k = &kk[yy * ksize]; for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; for (y = 0; y < ymax; y++) @@ -489,7 +491,7 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, typedef void (*ResampleFunction)(Imaging imOut, Imaging imIn, - int kmax, int *xbounds, double *kk); + int ksize, int *bounds, double *kk); Imaging @@ -567,51 +569,51 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, Imaging imTemp = NULL; Imaging imOut = NULL; - int kmax; - int *xbounds; + int ksize; + int *bounds; double *kk; /* two-pass resize, first pass */ if (box[0] || box[2] != xsize) { - kmax = precompute_coeffs(imIn->xsize, box[0], box[2], xsize, filterp, - &xbounds, &kk); - if ( ! kmax) { + ksize = precompute_coeffs(imIn->xsize, box[0], box[2], xsize, filterp, + &bounds, &kk); + if ( ! ksize) { return NULL; } imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); if ( ! imTemp) { - free(xbounds); + free(bounds); free(kk); return NULL; } - ResampleHorizontal(imTemp, imIn, kmax, xbounds, kk); - free(xbounds); + ResampleHorizontal(imTemp, imIn, ksize, bounds, kk); + free(bounds); free(kk); imOut = imIn = imTemp; } /* second pass */ if (box[1] || box[3] != ysize) { - kmax = precompute_coeffs(imIn->ysize, box[1], box[3], ysize, filterp, - &xbounds, &kk); - if ( ! kmax) { + ksize = precompute_coeffs(imIn->ysize, box[1], box[3], ysize, filterp, + &bounds, &kk); + if ( ! ksize) { ImagingDelete(imTemp); return NULL; } imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); if ( ! imOut) { - free(xbounds); + free(bounds); free(kk); return NULL; } /* imIn can be the original image or horizontally resampled one */ - ResampleVertical(imOut, imIn, kmax, xbounds, kk); + ResampleVertical(imOut, imIn, ksize, bounds, kk); /* it's safe to call ImagingDelete with empty value if previous step was not performed. */ ImagingDelete(imTemp); - free(xbounds); + free(bounds); free(kk); } From d382183591491bce02e3f66444c080b5c9487aff Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 1 Dec 2016 14:04:31 +0300 Subject: [PATCH 16/28] compute bounds and kk before passes --- libImaging/Resample.c | 63 +++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 2743bedf9..87fee9b0c 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -569,52 +569,57 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, Imaging imTemp = NULL; Imaging imOut = NULL; - int ksize; - int *bounds; - double *kk; + int ksize_horiz, ksize_vert; + int *bounds_horiz, *bounds_vert; + double *kk_horiz, *kk_vert; + + ksize_horiz = precompute_coeffs(imIn->xsize, box[0], box[2], xsize, + filterp, &bounds_horiz, &kk_horiz); + if ( ! ksize_horiz) { + return NULL; + } + + ksize_vert = precompute_coeffs(imIn->ysize, box[1], box[3], ysize, + filterp, &bounds_vert, &kk_vert); + if ( ! ksize_vert) { + free(bounds_horiz); + free(kk_horiz); + return NULL; + } /* two-pass resize, first pass */ if (box[0] || box[2] != xsize) { - ksize = precompute_coeffs(imIn->xsize, box[0], box[2], xsize, filterp, - &bounds, &kk); - if ( ! ksize) { - return NULL; - } - imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); + if (imTemp) { + ResampleHorizontal(imTemp, imIn, + ksize_horiz, bounds_horiz, kk_horiz); + } + free(bounds_horiz); + free(kk_horiz); if ( ! imTemp) { - free(bounds); - free(kk); + free(bounds_vert); + free(kk_vert); return NULL; } - ResampleHorizontal(imTemp, imIn, ksize, bounds, kk); - free(bounds); - free(kk); imOut = imIn = imTemp; } /* second pass */ if (box[1] || box[3] != ysize) { - ksize = precompute_coeffs(imIn->ysize, box[1], box[3], ysize, filterp, - &bounds, &kk); - if ( ! ksize) { - ImagingDelete(imTemp); - return NULL; - } - imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); - if ( ! imOut) { - free(bounds); - free(kk); - return NULL; + if (imOut) { + /* imIn can be the original image or horizontally resampled one */ + ResampleVertical(imOut, imIn, + ksize_vert, bounds_vert, kk_vert); } - /* imIn can be the original image or horizontally resampled one */ - ResampleVertical(imOut, imIn, ksize, bounds, kk); /* it's safe to call ImagingDelete with empty value if previous step was not performed. */ ImagingDelete(imTemp); - free(bounds); - free(kk); + free(bounds_vert); + free(kk_vert); + if ( ! imOut) { + return NULL; + } } /* none of the previous steps are performed, copying */ From 7ea0611af4e1b05692ef70102d292e49a3b1a719 Mon Sep 17 00:00:00 2001 From: homm Date: Thu, 1 Dec 2016 21:09:29 +0300 Subject: [PATCH 17/28] crop roi for vertical pass if box is used --- libImaging/Resample.c | 53 +++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 87fee9b0c..14ef164ab 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -237,7 +237,7 @@ normalize_coeffs_8bpc(int outSize, int ksize, double *prekk) void -ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, +ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *prekk) { ImagingSectionCookie cookie; @@ -258,7 +258,7 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, k = &kk[xx * ksize]; ss0 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) - ss0 += ((UINT8) imIn->image8[yy][x + xmin]) * k[x]; + ss0 += ((UINT8) imIn->image8[yy + offset][x + xmin]) * k[x]; imOut->image8[yy][xx] = clip8(ss0); } } @@ -271,8 +271,8 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, k = &kk[xx * ksize]; ss0 = ss3 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) { - ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x]; - ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x]; + ss0 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 0]) * k[x]; + ss3 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 3]) * k[x]; } imOut->image[yy][xx*4 + 0] = clip8(ss0); imOut->image[yy][xx*4 + 3] = clip8(ss3); @@ -286,9 +286,9 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, k = &kk[xx * ksize]; ss0 = ss1 = ss2 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) { - ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x]; - ss1 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 1]) * k[x]; - ss2 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 2]) * k[x]; + ss0 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 0]) * k[x]; + ss1 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 1]) * k[x]; + ss2 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 2]) * k[x]; } imOut->image[yy][xx*4 + 0] = clip8(ss0); imOut->image[yy][xx*4 + 1] = clip8(ss1); @@ -303,10 +303,10 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, k = &kk[xx * ksize]; ss0 = ss1 = ss2 = ss3 = 1 << (PRECISION_BITS -1); for (x = 0; x < xmax; x++) { - ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x]; - ss1 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 1]) * k[x]; - ss2 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 2]) * k[x]; - ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x]; + ss0 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 0]) * k[x]; + ss1 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 1]) * k[x]; + ss2 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 2]) * k[x]; + ss3 += ((UINT8) imIn->image[yy + offset][(x + xmin)*4 + 3]) * k[x]; } imOut->image[yy][xx*4 + 0] = clip8(ss0); imOut->image[yy][xx*4 + 1] = clip8(ss1); @@ -321,7 +321,7 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, void -ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, +ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *prekk) { ImagingSectionCookie cookie; @@ -405,7 +405,7 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, void -ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, +ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *kk) { ImagingSectionCookie cookie; @@ -423,7 +423,7 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, k = &kk[xx * ksize]; ss = 0.0; for (x = 0; x < xmax; x++) - ss += IMAGING_PIXEL_I(imIn, x + xmin, yy) * k[x]; + ss += IMAGING_PIXEL_I(imIn, x + xmin, yy + offset) * k[x]; IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss); } } @@ -437,7 +437,7 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, k = &kk[xx * ksize]; ss = 0.0; for (x = 0; x < xmax; x++) - ss += IMAGING_PIXEL_F(imIn, x + xmin, yy) * k[x]; + ss += IMAGING_PIXEL_F(imIn, x + xmin, yy + offset) * k[x]; IMAGING_PIXEL_F(imOut, xx, yy) = ss; } } @@ -448,7 +448,7 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, void -ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, +ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *kk) { ImagingSectionCookie cookie; @@ -490,7 +490,7 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, } -typedef void (*ResampleFunction)(Imaging imOut, Imaging imIn, +typedef void (*ResampleFunction)(Imaging imOut, Imaging imIn, int offset, int ksize, int *bounds, double *kk); @@ -569,6 +569,8 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, Imaging imTemp = NULL; Imaging imOut = NULL; + int i; + int yroi_min, yroi_max; int ksize_horiz, ksize_vert; int *bounds_horiz, *bounds_vert; double *kk_horiz, *kk_vert; @@ -587,11 +589,22 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, return NULL; } + // First used row in the source image + yroi_min = bounds_vert[0]; + // Last used row in the source image + yroi_max = bounds_vert[ysize*2 - 2] + bounds_vert[ysize*2 - 1]; + + /* two-pass resize, first pass */ if (box[0] || box[2] != xsize) { - imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); + // Shift bounds for vertical pass + for (i = 0; i < ysize; i++) { + bounds_vert[i * 2] -= yroi_min; + } + + imTemp = ImagingNew(imIn->mode, xsize, yroi_max - yroi_min); if (imTemp) { - ResampleHorizontal(imTemp, imIn, + ResampleHorizontal(imTemp, imIn, yroi_min, ksize_horiz, bounds_horiz, kk_horiz); } free(bounds_horiz); @@ -609,7 +622,7 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); if (imOut) { /* imIn can be the original image or horizontally resampled one */ - ResampleVertical(imOut, imIn, + ResampleVertical(imOut, imIn, 0, ksize_vert, bounds_vert, kk_vert); } /* it's safe to call ImagingDelete with empty value From 1477fbc07ac268153fc86b5aa7a320378e3c0d99 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 2 Dec 2016 02:59:40 +0300 Subject: [PATCH 18/28] update tests, add comments --- Tests/test_image_resample.py | 70 ++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 3d37bddde..a3e507745 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -1,3 +1,5 @@ +from __future__ import division + from helper import unittest, PillowTestCase, hopper from PIL import Image, ImageDraw, ImageMode @@ -348,7 +350,7 @@ class CoreResampleCoefficientsTest(PillowTestCase): self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) # fourth channel -class CoreResampleRoiTest(PillowTestCase): +class CoreResampleBoxTest(PillowTestCase): def test_wrong_arguments(self): im = hopper() for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, @@ -378,38 +380,52 @@ class CoreResampleRoiTest(PillowTestCase): with self.assertRaisesRegexp(ValueError, "can't exceed"): im.resize((32, 32), resample, (0, 0, im.width, im.height + 1)) + def resize_tiled(self, im, dst_size, xtiles, ytiles): + def split_range(size, tiles): + scale = size / tiles + for i in range(tiles): + yield (int(round(scale * i)), int(round(scale * (i + 1)))) + + tiled = Image.new(im.mode, dst_size) + scale = (im.size[0] / tiled.size[0], im.size[1] / tiled.size[1]) + + for y0, y1 in split_range(dst_size[1], ytiles): + for x0, x1 in split_range(dst_size[0], xtiles): + box = (x0 * scale[0], y0 * scale[1], + x1 * scale[0], y1 * scale[1]) + tile = im.resize((x1 - x0, y1 - y0), Image.BICUBIC, box) + tiled.paste(tile, (x0, y0)) + return tiled + def test_tiles(self): - im = hopper() - # should not be fractional - size = (28, 14) - sc = (3, 4) # scale - o = (5, 10) # offset - # fixed size divisible by scale - im = im.resize((im.width // sc[0] * sc[0], - im.height // sc[1] * sc[1])) + im = Image.open("Tests/images/flower.jpg") + assert im.size == (480, 360) + dst_size = (251, 188) + reference = im.resize(dst_size, Image.BICUBIC) - for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, - Image.BICUBIC, Image.LANCZOS): - box = (o[0] * sc[0], o[1] * sc[1], - (o[0] + size[0]) * sc[0], (o[1] + size[1]) * sc[1]) - tile1 = im.resize(size, resample, box) - big_size = (im.width // sc[0], im.height // sc[1]) - tile2 = im.resize(big_size, resample)\ - .crop(o + (o[0] + size[0], o[1] + size[1])) - - self.assert_image_equal(tile1, tile2) + for tiles in [(1, 1), (3, 3), (9, 7), (100, 100)]: + tiled = self.resize_tiled(im, dst_size, *tiles) + self.assert_image_similar(reference, tiled, 0.01) def test_subsample(self): - im = hopper() - reference = im.crop((0, 0, 125, 125)).resize((26, 26), Image.BICUBIC) - supersampled = im.resize((32, 32), Image.BOX) - without_box = supersampled.resize((26, 26), Image.BICUBIC) - with_box = supersampled.resize((26, 26), Image.BICUBIC, (0, 0, 31.25, 31.25)) + # This test shows advantages of the subpixel resizing + # after supersampling (e.g. during JPEG decoding). + im = Image.open("Tests/images/flower.jpg") + assert im.size == (480, 360) + dst_size = (48, 36) + # Reference is cropped image resized to destination + reference = im.crop((0, 0, 473, 353)).resize(dst_size, Image.BICUBIC) + # Image.BOX emulates supersampling (480 / 8 = 60, 360 / 8 = 45) + supersampled = im.resize((60, 45), Image.BOX) - self.assert_image_similar(reference, with_box, 12) + with_box = supersampled.resize(dst_size, Image.BICUBIC, + (0, 0, 59.125, 44.125)) + without_box = supersampled.resize(dst_size, Image.BICUBIC) - with self.assertRaisesRegexp(AssertionError, "difference 3\d\."): - self.assert_image_similar(reference, without_box, 10) + # error with box should be much smaller than without + self.assert_image_similar(reference, with_box, 6) + with self.assertRaisesRegexp(AssertionError, "difference 29\."): + self.assert_image_similar(reference, without_box, 5) if __name__ == '__main__': From 3d72e9828f3c14defa8ab878b21049a3d3ec0da4 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 2 Dec 2016 03:11:02 +0300 Subject: [PATCH 19/28] add param info --- PIL/Image.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index 9814a7392..844f27ab5 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1532,6 +1532,10 @@ class Image(object): If omitted, or if the image has mode "1" or "P", it is set :py:attr:`PIL.Image.NEAREST`. See: :ref:`concept-filters`. + :param box: An optional 4-tuple of floats giving the region + of the source image which should be scaled. + The values should be within (0, 0, width, height) rectangle. + If omitted or None, the entire source is used. :returns: An :py:class:`~PIL.Image.Image` object. """ From 295382aadc994dc81e5076450dce5cf573a9bbd0 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 2 Dec 2016 15:40:32 +0300 Subject: [PATCH 20/28] vertical and horizontal pass with boxes --- PIL/Image.py | 13 +++++++----- Tests/test_image_resample.py | 40 ++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 844f27ab5..a3171aeaa 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1544,10 +1544,14 @@ class Image(object): ): raise ValueError("unknown resampling filter") - self.load() - size = tuple(size) - if self.size == size: + + if box is None: + box = (0, 0) + self.size + else: + box = tuple(box) + + if self.size == size and box == (0, 0) + self.size: return self.copy() if self.mode in ("1", "P"): @@ -1559,8 +1563,7 @@ class Image(object): if self.mode == 'RGBA': return self.convert('RGBa').resize(size, resample).convert('RGBA') - if box is None: - box = (0, 0) + self.size + self.load() return self._new(self.im.resize(size, resample, box)) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index a3e507745..632ead76c 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -1,5 +1,7 @@ from __future__ import division +from contextlib import contextmanager + from helper import unittest, PillowTestCase, hopper from PIL import Image, ImageDraw, ImageMode @@ -303,24 +305,44 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): class CoreResamplePassesTest(PillowTestCase): + @contextmanager + def count(self, diff): + count = Image.core.getcount() + diff + yield + self.assertEqual(Image.core.getcount(), count) + def test_horizontal(self): im = hopper('L') - count = Image.core.getcount() - im.resize((im.size[0] + 10, im.size[1]), Image.BILINEAR) - self.assertEqual(Image.core.getcount(), count + 1) + with self.count(1): + im.resize((im.size[0] - 10, im.size[1]), Image.BILINEAR) def test_vertical(self): im = hopper('L') - count = Image.core.getcount() - im.resize((im.size[0], im.size[1] + 10), Image.BILINEAR) - self.assertEqual(Image.core.getcount(), count + 1) + with self.count(1): + im.resize((im.size[0], im.size[1] - 10), Image.BILINEAR) def test_both(self): im = hopper('L') - count = Image.core.getcount() - im.resize((im.size[0] + 10, im.size[1] + 10), Image.BILINEAR) - self.assertEqual(Image.core.getcount(), count + 2) + with self.count(2): + im.resize((im.size[0] - 10, im.size[1] - 10), Image.BILINEAR) + def test_box_horizontal(self): + im = hopper('L') + box = (20, 0, im.size[0] - 20, im.size[1]) + with self.count(1): + # the same size, but different box + with_box = im.resize(im.size, Image.BILINEAR, box) + cropped = im.crop(box).resize(im.size, Image.BILINEAR) + self.assert_image_similar(with_box, cropped, 0.1) + + def test_box_vertical(self): + im = hopper('L') + box = (0, 20, im.size[0], im.size[1] - 20) + with self.count(1): + # the same size, but different box + with_box = im.resize(im.size, Image.BILINEAR, box) + cropped = im.crop(box).resize(im.size, Image.BILINEAR) + self.assert_image_similar(with_box, cropped, 0.1) class CoreResampleCoefficientsTest(PillowTestCase): def test_reduce(self): From 98042cd4b7b26c4137fe35738ac7efeb7901d1d1 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 2 Dec 2016 15:40:41 +0300 Subject: [PATCH 21/28] fix memory leak --- libImaging/Resample.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 14ef164ab..64f780c71 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -615,6 +615,10 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, return NULL; } imOut = imIn = imTemp; + } else { + // Free in any case + free(bounds_horiz); + free(kk_horiz); } /* second pass */ @@ -633,6 +637,10 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize, if ( ! imOut) { return NULL; } + } else { + // Free in any case + free(bounds_vert); + free(kk_vert); } /* none of the previous steps are performed, copying */ From cf25722a7318475b6077e612f5579a97eed5e1cf Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 2 Dec 2016 15:42:33 +0300 Subject: [PATCH 22/28] improve error message --- Tests/test_image_resample.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 632ead76c..bbdbf2a02 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -307,9 +307,9 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): class CoreResamplePassesTest(PillowTestCase): @contextmanager def count(self, diff): - count = Image.core.getcount() + diff + count = Image.core.getcount() yield - self.assertEqual(Image.core.getcount(), count) + self.assertEqual(Image.core.getcount() - count, diff) def test_horizontal(self): im = hopper('L') @@ -332,7 +332,8 @@ class CoreResamplePassesTest(PillowTestCase): with self.count(1): # the same size, but different box with_box = im.resize(im.size, Image.BILINEAR, box) - cropped = im.crop(box).resize(im.size, Image.BILINEAR) + with self.count(2): + cropped = im.crop(box).resize(im.size, Image.BILINEAR) self.assert_image_similar(with_box, cropped, 0.1) def test_box_vertical(self): @@ -341,7 +342,8 @@ class CoreResamplePassesTest(PillowTestCase): with self.count(1): # the same size, but different box with_box = im.resize(im.size, Image.BILINEAR, box) - cropped = im.crop(box).resize(im.size, Image.BILINEAR) + with self.count(2): + cropped = im.crop(box).resize(im.size, Image.BILINEAR) self.assert_image_similar(with_box, cropped, 0.1) class CoreResampleCoefficientsTest(PillowTestCase): From 77210e410d5dc5fe16b20d5e783867d3efc6cb75 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 2 Dec 2016 16:33:48 +0300 Subject: [PATCH 23/28] =?UTF-8?q?test=20for=20common=20modes=20and=20filte?= =?UTF-8?q?rs=20pass=20box=20for=20RGBA=20=E2=86=92=20RGBa=20conversion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PIL/Image.py | 4 ++-- Tests/test_image_resample.py | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index a3171aeaa..84122655a 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1558,10 +1558,10 @@ class Image(object): resample = NEAREST if self.mode == 'LA': - return self.convert('La').resize(size, resample).convert('LA') + return self.convert('La').resize(size, resample, box).convert('LA') if self.mode == 'RGBA': - return self.convert('RGBa').resize(size, resample).convert('RGBA') + return self.convert('RGBa').resize(size, resample, box).convert('RGBA') self.load() diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index bbdbf2a02..17461c868 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -451,6 +451,15 @@ class CoreResampleBoxTest(PillowTestCase): with self.assertRaisesRegexp(AssertionError, "difference 29\."): self.assert_image_similar(reference, without_box, 5) + def test_formats(self): + for resample in [Image.NEAREST, Image.BILINEAR]: + for mode in ['RGB', 'L', 'RGBA', 'LA', 'I', '']: + im = hopper(mode) + box = (20, 20, im.size[0] - 20, im.size[1] - 20) + with_box = im.resize((32, 32), resample, box) + cropped = im.crop(box).resize((32, 32), resample) + self.assert_image_similar(cropped, with_box, 0.4) + if __name__ == '__main__': unittest.main() From 19527c8ba0ffe188cbb2230897fcd2fe02b270a4 Mon Sep 17 00:00:00 2001 From: homm Date: Fri, 2 Dec 2016 16:46:54 +0300 Subject: [PATCH 24/28] =?UTF-8?q?rename=20roi=20=E2=86=92=20box=20in=20err?= =?UTF-8?q?or=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _imaging.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_imaging.c b/_imaging.c index 818826f37..f093f598d 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1549,15 +1549,15 @@ _resize(ImagingObject* self, PyObject* args) } if (box[0] < 0 || box[1] < 0) { - return ImagingError_ValueError("region of interest offset can't be negative"); + return ImagingError_ValueError("box offset can't be negative"); } if (box[2] > imIn->xsize || box[3] > imIn->ysize) { - return ImagingError_ValueError("region of interest can't exceed original image size"); + return ImagingError_ValueError("box can't exceed original image size"); } if (box[2] - box[0] <= 0 || box[3] - box[1] <= 0) { - return ImagingError_ValueError("region of interest can't be empty"); + return ImagingError_ValueError("box can't be empty"); } if (box[0] == 0 && box[1] == 0 && box[2] == xsize && box[3] == ysize) { From 79eae6e3bf92848ea6187529f3d8fc9df4100a1b Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 11 Aug 2017 19:36:46 +0300 Subject: [PATCH 25/28] actually box can be empty, this is noop --- Tests/test_image_resample.py | 10 +++++----- _imaging.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 0e8ec347d..a7739c884 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -379,6 +379,8 @@ class CoreResampleBoxTest(PillowTestCase): Image.BICUBIC, Image.LANCZOS): im.resize((32, 32), resample, (0, 0, im.width, im.height)) im.resize((32, 32), resample, (20, 20, im.width, im.height)) + im.resize((32, 32), resample, (20, 20, 20, 100)) + im.resize((32, 32), resample, (20, 20, 100, 20)) with self.assertRaisesRegexp(TypeError, "must be sequence of length 4"): im.resize((32, 32), resample, (im.width, im.height)) @@ -389,13 +391,11 @@ class CoreResampleBoxTest(PillowTestCase): im.resize((32, 32), resample, (20, -20, 100, 100)) with self.assertRaisesRegexp(ValueError, "can't be empty"): - im.resize((32, 32), resample, (20, 20, 20, 100)) + im.resize((32, 32), resample, (20.1, 20, 20, 100)) with self.assertRaisesRegexp(ValueError, "can't be empty"): - im.resize((32, 32), resample, (100, 20, 20, 100)) + im.resize((32, 32), resample, (20, 20.1, 100, 20)) with self.assertRaisesRegexp(ValueError, "can't be empty"): - im.resize((32, 32), resample, (20, 20, 100, 20)) - with self.assertRaisesRegexp(ValueError, "can't be empty"): - im.resize((32, 32), resample, (20, 100, 100, 20)) + im.resize((32, 32), resample, (20.1, 20.1, 20, 20)) with self.assertRaisesRegexp(ValueError, "can't exceed"): im.resize((32, 32), resample, (0, 0, im.width + 1, im.height)) diff --git a/_imaging.c b/_imaging.c index ad083840d..d29622e83 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1542,7 +1542,7 @@ _resize(ImagingObject* self, PyObject* args) return ImagingError_ValueError("box can't exceed original image size"); } - if (box[2] - box[0] <= 0 || box[3] - box[1] <= 0) { + if (box[2] - box[0] < 0 || box[3] - box[1] < 0) { return ImagingError_ValueError("box can't be empty"); } From c7136f7ff8d16e7304770a60d70059d0220efc19 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Aug 2017 22:35:54 +0100 Subject: [PATCH 26/28] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8dedfe866..a3af2e3d4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 4.3.0 (unreleased) ------------------ +- Region of interest (box) for resampling #2254 + [homm] + - Basic support for Termux (android) in setup.py #2684 [wiredfool] From aac0869ca7a5542936ed5944b789bdec6ccb14d8 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Aug 2017 19:02:15 +0300 Subject: [PATCH 27/28] Revert little-endian byte order for "I" and "F" rawmodes --- Tests/test_lib_pack.py | 9 ++++++--- Tests/test_mode_i16.py | 4 +++- libImaging/Pack.c | 4 ++-- libImaging/Unpack.c | 6 ++++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index b4c8b6a56..f07452de7 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -146,31 +146,34 @@ class TestLibPack(PillowTestCase): self.assert_pack("HSV", "V", 1, (9,9,1), (9,9,2), (9,9,3)) def test_I(self): - self.assert_pack("I", "I", 4, 0x04030201, 0x08070605) self.assert_pack("I", "I;16B", 2, 0x0102, 0x0304) self.assert_pack("I", "I;32S", b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) if sys.byteorder == 'little': + self.assert_pack("I", "I", 4, 0x04030201, 0x08070605) self.assert_pack("I", "I;32NS", b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) else: + self.assert_pack("I", "I", 4, 0x01020304, 0x05060708) self.assert_pack("I", "I;32NS", b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083) def test_F_float(self): - self.assert_pack("F", "F", 4, - 1.539989614439558e-36, 4.063216068939723e-34) self.assert_pack("F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34) if sys.byteorder == 'little': + self.assert_pack("F", "F", 4, + 1.539989614439558e-36, 4.063216068939723e-34) self.assert_pack("F", "F;32NF", 4, 1.539989614439558e-36, 4.063216068939723e-34) else: + self.assert_pack("F", "F", 4, + 2.387939260590663e-38, 6.301941157072183e-36) self.assert_pack("F", "F;32NF", 4, 2.387939260590663e-38, 6.301941157072183e-36) diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 53bbce572..d51847199 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -87,10 +87,12 @@ class TestModeI16(PillowTestCase): def tobytes(mode): return Image.new(mode, (1, 1), 1).tobytes() + order = 1 if Image._ENDIAN == '<' else -1 + self.assertEqual(tobytes("L"), b"\x01") self.assertEqual(tobytes("I;16"), b"\x01\x00") self.assertEqual(tobytes("I;16B"), b"\x00\x01") - self.assertEqual(tobytes("I"), b"\x01\x00\x00\x00") + self.assertEqual(tobytes("I"), b"\x01\x00\x00\x00"[::order]) def test_convert(self): diff --git a/libImaging/Pack.c b/libImaging/Pack.c index b9d1d6a2a..0810f51f1 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -599,13 +599,13 @@ static struct { {"HSV", "V", 8, band2}, /* integer */ - {"I", "I", 32, packI32S}, + {"I", "I", 32, copy4}, {"I", "I;16B", 16, packI16B}, {"I", "I;32S", 32, packI32S}, {"I", "I;32NS", 32, copy4}, /* floating point */ - {"F", "F", 32, packI32S}, + {"F", "F", 32, copy4}, {"F", "F;32F", 32, packI32S}, {"F", "F;32NF", 32, copy4}, diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index b0fba0a79..ce47a5727 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -1176,6 +1176,8 @@ static struct { endian byte order (default is little endian); "L" line interleave, "S" signed, "F" floating point */ + /* exception: rawmodes "I" and "F" are always native endian byte order */ + /* bilevel */ {"1", "1", 1, unpack1}, {"1", "1;I", 1, unpack1I}, @@ -1318,7 +1320,7 @@ static struct { {"HSV", "V", 8, band2}, /* integer variations */ - {"I", "I", 32, unpackI32}, + {"I", "I", 32, copy4}, {"I", "I;8", 8, unpackI8}, {"I", "I;8S", 8, unpackI8S}, {"I", "I;16", 16, unpackI16}, @@ -1335,7 +1337,7 @@ static struct { {"I", "I;32NS", 32, unpackI32NS}, /* floating point variations */ - {"F", "F", 32, unpackI32}, + {"F", "F", 32, copy4}, {"F", "F;8", 8, unpackF8}, {"F", "F;8S", 8, unpackF8S}, {"F", "F;16", 16, unpackF16}, From 40fe1f721e57a14d45de0341bb243b07cb93ef0e Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Aug 2017 19:22:59 +0300 Subject: [PATCH 28/28] unpacking tests for "I" and "F" modes --- Tests/test_lib_pack.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index f07452de7..d062b33be 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -366,7 +366,6 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("HSV", "V", 1, (0,0,1), (0,0,2), (0,0,3)) def test_I(self): - self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605) self.assert_unpack("I", "I;8", 1, 0x01, 0x02, 0x03, 0x04) self.assert_unpack("I", "I;8S", b'\x01\x83', 1, -125) self.assert_unpack("I", "I;16", 2, 0x0201, 0x0403) @@ -383,6 +382,7 @@ class TestLibUnpack(PillowTestCase): -2097151999, 0x01000083) if sys.byteorder == 'little': + self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605) self.assert_unpack("I", "I;16N", 2, 0x0201, 0x0403) self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', 0x0183, -31999) self.assert_unpack("I", "I;32N", 4, 0x04030201, 0x08070605) @@ -390,6 +390,7 @@ class TestLibUnpack(PillowTestCase): b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) else: + self.assert_unpack("I", "I", 4, 0x01020304, 0x05060708) self.assert_unpack("I", "I;16N", 2, 0x0102, 0x0304) self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack("I", "I;32N", 4, 0x01020304, 0x05060708) @@ -429,8 +430,6 @@ class TestLibUnpack(PillowTestCase): -2097152000, 16777348) def test_F_float(self): - self.assert_unpack("F", "F", 4, - 1.539989614439558e-36, 4.063216068939723e-34) self.assert_unpack("F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34) self.assert_unpack("F", "F;32BF", 4, @@ -443,12 +442,16 @@ class TestLibUnpack(PillowTestCase): 0.15000000596046448, -1234.5) if sys.byteorder == 'little': + self.assert_unpack("F", "F", 4, + 1.539989614439558e-36, 4.063216068939723e-34) self.assert_unpack("F", "F;32NF", 4, 1.539989614439558e-36, 4.063216068939723e-34) self.assert_unpack("F", "F;64NF", b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0', 0.15000000596046448, -1234.5) else: + self.assert_unpack("F", "F", 4, + 2.387939260590663e-38, 6.301941157072183e-36) self.assert_unpack("F", "F;32NF", 4, 2.387939260590663e-38, 6.301941157072183e-36) self.assert_unpack("F", "F;64NF",