From 73eec9000d5fa0e88786a560882fba921cfc42df Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 17 Oct 2018 01:57:55 +0300 Subject: [PATCH 1/3] Optimise ImageOps.fit by combining resize in crop --- src/PIL/ImageOps.py | 69 ++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 9f516bac1fb..49ea056987f 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -380,9 +380,10 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): (width, height) tuple. :param method: What resampling method to use. Default is :py:attr:`PIL.Image.NEAREST`. - :param bleed: Remove a border around the outside of the image (from all + :param bleed: Remove a border around the outside of the image from all four edges. The value is a decimal percentage (use 0.01 for one percent). The default value is 0 (no border). + Cannot be greater than or equal to 0.5. :param centering: Control the cropping position. Use (0.5, 0.5) for center cropping (e.g. if cropping the width, take 50% off of the left side, and therefore 50% off the right side). @@ -400,66 +401,50 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): # kevin@cazabon.com # http://www.cazabon.com - # ensure inputs are valid - if not isinstance(centering, list): - centering = [centering[0], centering[1]] + # ensure centering is mutable + centering = list(centering) - if centering[0] > 1.0 or centering[0] < 0.0: - centering[0] = 0.50 - if centering[1] > 1.0 or centering[1] < 0.0: - centering[1] = 0.50 + if not 0.0 <= centering[0] <= 1.0: + centering[0] = 0.5 + if not 0.0 <= centering[1] <= 1.0: + centering[1] = 0.5 - if bleed > 0.49999 or bleed < 0.0: + if not 0.0 <= bleed < 0.5: bleed = 0.0 # calculate the area to use for resizing and cropping, subtracting # the 'bleed' around the edges # number of pixels to trim off on Top and Bottom, Left and Right - bleedPixels = ( - int((float(bleed) * float(image.size[0])) + 0.5), - int((float(bleed) * float(image.size[1])) + 0.5) - ) - - liveArea = (0, 0, image.size[0], image.size[1]) - if bleed > 0.0: - liveArea = ( - bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1, - image.size[1] - bleedPixels[1] - 1 - ) + bleed_pixels = (bleed * image.size[0], bleed * image.size[1]) - liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1]) + live_size = (image.size[0] - bleed_pixels[0] * 2, + image.size[1] - bleed_pixels[1] * 2) - # calculate the aspect ratio of the liveArea - liveAreaAspectRatio = float(liveSize[0])/float(liveSize[1]) + # calculate the aspect ratio of the live_size + live_size_ratio = float(live_size[0]) / live_size[1] # calculate the aspect ratio of the output image - aspectRatio = float(size[0]) / float(size[1]) + output_ratio = float(size[0]) / size[1] # figure out if the sides or top/bottom will be cropped off - if liveAreaAspectRatio >= aspectRatio: - # liveArea is wider than what's needed, crop the sides - cropWidth = int((aspectRatio * float(liveSize[1])) + 0.5) - cropHeight = liveSize[1] + if live_size_ratio >= output_ratio: + # live_size is wider than what's needed, crop the sides + crop_width = output_ratio * live_size[1] + crop_height = live_size[1] else: - # liveArea is taller than what's needed, crop the top and bottom - cropWidth = liveSize[0] - cropHeight = int((float(liveSize[0])/aspectRatio) + 0.5) + # live_size is taller than what's needed, crop the top and bottom + crop_width = live_size[0] + crop_height = live_size[0] / output_ratio # make the crop - leftSide = int(liveArea[0] + (float(liveSize[0]-cropWidth) * centering[0])) - if leftSide < 0: - leftSide = 0 - topSide = int(liveArea[1] + (float(liveSize[1]-cropHeight) * centering[1])) - if topSide < 0: - topSide = 0 - - out = image.crop( - (leftSide, topSide, leftSide + cropWidth, topSide + cropHeight) - ) + crop_left = bleed_pixels[0] + (live_size[0]-crop_width) * centering[0] + crop_top = bleed_pixels[1] + (live_size[1]-crop_height) * centering[1] + + crop = (crop_left, crop_top, crop_left + crop_width, crop_top + crop_height) # resize the image and return it - return out.resize(size, method) + return image.resize(size, method, box=crop) def flip(image): From 6cabcadae4c81059b63c2096d02b3fe6d206c02e Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 18 Oct 2018 10:58:20 +0300 Subject: [PATCH 2/3] add release notes --- docs/releasenotes/5.4.0.rst | 14 ++++++++++++++ docs/releasenotes/index.rst | 1 + src/PIL/ImageOps.py | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 docs/releasenotes/5.4.0.rst diff --git a/docs/releasenotes/5.4.0.rst b/docs/releasenotes/5.4.0.rst new file mode 100644 index 00000000000..094efdc617c --- /dev/null +++ b/docs/releasenotes/5.4.0.rst @@ -0,0 +1,14 @@ +5.4.0 (unreleased) +----- + + +Other Changes +============= + +ImageOps.fit +^^^^^^^^^^^^ + +Now uses the one resize operation with ``box`` argument internally +instead of the crop and scale operations sequence. +This improves the performance and accuracy of cropping since +``box`` parameter accepts float values. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index fc8d686eb00..df4fd493090 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 5.4.0 5.3.0 5.2.0 5.1.0 diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 49ea056987f..8092268479c 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -419,7 +419,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): bleed_pixels = (bleed * image.size[0], bleed * image.size[1]) live_size = (image.size[0] - bleed_pixels[0] * 2, - image.size[1] - bleed_pixels[1] * 2) + image.size[1] - bleed_pixels[1] * 2) # calculate the aspect ratio of the live_size live_size_ratio = float(live_size[0]) / live_size[1] From 61e46c265e8f4371a89af340ffde2bffacc3ff04 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 18 Oct 2018 11:40:32 +0300 Subject: [PATCH 3/3] [ci skip] fix notes --- docs/releasenotes/5.4.0.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/releasenotes/5.4.0.rst b/docs/releasenotes/5.4.0.rst index 094efdc617c..df0622cdcb1 100644 --- a/docs/releasenotes/5.4.0.rst +++ b/docs/releasenotes/5.4.0.rst @@ -8,7 +8,7 @@ Other Changes ImageOps.fit ^^^^^^^^^^^^ -Now uses the one resize operation with ``box`` argument internally -instead of the crop and scale operations sequence. +Now uses one resize operation with ``box`` parameter internally +instead of a crop and scale operations sequence. This improves the performance and accuracy of cropping since -``box`` parameter accepts float values. +the ``box`` parameter accepts float values.