-
-
Save sigilioso/2957026 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*- | |
import Image | |
def resize_and_crop(img_path, modified_path, size, crop_type='top'): | |
""" | |
Resize and crop an image to fit the specified size. | |
args: | |
img_path: path for the image to resize. | |
modified_path: path to store the modified image. | |
size: `(width, height)` tuple. | |
crop_type: can be 'top', 'middle' or 'bottom', depending on this | |
value, the image will cropped getting the 'top/left', 'midle' or | |
'bottom/rigth' of the image to fit the size. | |
raises: | |
Exception: if can not open the file in img_path of there is problems | |
to save the image. | |
ValueError: if an invalid `crop_type` is provided. | |
""" | |
# If height is higher we resize vertically, if not we resize horizontally | |
img = Image.open(img_path) | |
# Get current and desired ratio for the images | |
img_ratio = img.size[0] / float(img.size[1]) | |
ratio = size[0] / float(size[1]) | |
#The image is scaled/cropped vertically or horizontally depending on the ratio | |
if ratio > img_ratio: | |
img = img.resize((size[0], size[0] * img.size[1] / img.size[0]), | |
Image.ANTIALIAS) | |
# Crop in the top, middle or bottom | |
if crop_type == 'top': | |
box = (0, 0, img.size[0], size[1]) | |
elif crop_type == 'middle': | |
box = (0, (img.size[1] - size[1]) / 2, img.size[0], (img.size[1] + size[1]) / 2) | |
elif crop_type == 'bottom': | |
box = (0, img.size[1] - size[1], img.size[0], img.size[1]) | |
else : | |
raise ValueError('ERROR: invalid value for crop_type') | |
img = img.crop(box) | |
elif ratio < img_ratio: | |
img = img.resize((size[1] * img.size[0] / img.size[1], size[1]), | |
Image.ANTIALIAS) | |
# Crop in the top, middle or bottom | |
if crop_type == 'top': | |
box = (0, 0, size[0], img.size[1]) | |
elif crop_type == 'middle': | |
box = ((img.size[0] - size[0]) / 2, 0, (img.size[0] + size[0]) / 2, img.size[1]) | |
elif crop_type == 'bottom': | |
box = (img.size[0] - size[0], 0, img.size[0], img.size[1]) | |
else : | |
raise ValueError('ERROR: invalid value for crop_type') | |
img = img.crop(box) | |
else : | |
img = img.resize((size[0], size[1]), | |
Image.ANTIALIAS) | |
# If the scale is the same, we do not need to crop | |
img.save(modified_path) |
Thank you!
I fixed some typos, added support for Pillow and fixed errors with resize() and crop() where need to use integers instead of floats.
from PIL import Image
def resize_and_crop(img_path, modified_path, size, crop_type='top'):
"""
Resize and crop an image to fit the specified size.
args:
img_path: path for the image to resize.
modified_path: path to store the modified image.
size: `(width, height)` tuple.
crop_type: can be 'top', 'middle' or 'bottom', depending on this
value, the image will cropped getting the 'top/left', 'middle' or
'bottom/right' of the image to fit the size.
raises:
Exception: if can not open the file in img_path of there is problems
to save the image.
ValueError: if an invalid `crop_type` is provided.
"""
# If height is higher we resize vertically, if not we resize horizontally
img = Image.open(img_path)
# Get current and desired ratio for the images
img_ratio = img.size[0] / float(img.size[1])
ratio = size[0] / float(size[1])
#The image is scaled/cropped vertically or horizontally depending on the ratio
if ratio > img_ratio:
img = img.resize((size[0], round(size[0] * img.size[1] / img.size[0])),
Image.ANTIALIAS)
# Crop in the top, middle or bottom
if crop_type == 'top':
box = (0, 0, img.size[0], size[1])
elif crop_type == 'middle':
box = (0, round((img.size[1] - size[1]) / 2), img.size[0],
round((img.size[1] + size[1]) / 2))
elif crop_type == 'bottom':
box = (0, img.size[1] - size[1], img.size[0], img.size[1])
else :
raise ValueError('ERROR: invalid value for crop_type')
img = img.crop(box)
elif ratio < img_ratio:
img = img.resize((round(size[1] * img.size[0] / img.size[1]), size[1]),
Image.ANTIALIAS)
# Crop in the top, middle or bottom
if crop_type == 'top':
box = (0, 0, size[0], img.size[1])
elif crop_type == 'middle':
box = (round((img.size[0] - size[0]) / 2), 0,
round((img.size[0] + size[0]) / 2), img.size[1])
elif crop_type == 'bottom':
box = (img.size[0] - size[0], 0, img.size[0], img.size[1])
else :
raise ValueError('ERROR: invalid value for crop_type')
img = img.crop(box)
else :
img = img.resize((size[0], size[1]),
Image.ANTIALIAS)
# If the scale is the same, we do not need to crop
img.save(modified_path)
Actually just adding round is not enough, as round will still return a float. You will have to convert the rounded values to integers, too. Besides that it works brilliantly. :)
Thanks for sharing! More power to your coding! :)
Thanks a lot !
just had to convert the rounded values to integers.
Here is the block edited to convert the rounded numbers to integers:
def resize_and_crop(img_path, modified_path, size, crop_type='top'):
"""
Resize and crop an image to fit the specified size.
args:
img_path: path for the image to resize.
modified_path: path to store the modified image.
size: `(width, height)` tuple.
crop_type: can be 'top', 'middle' or 'bottom', depending on this
value, the image will cropped getting the 'top/left', 'middle' or
'bottom/right' of the image to fit the size.
raises:
Exception: if can not open the file in img_path of there is problems
to save the image.
ValueError: if an invalid `crop_type` is provided.
"""
# If height is higher we resize vertically, if not we resize horizontally
img = Image.open(img_path)
# Get current and desired ratio for the images
img_ratio = img.size[0] / float(img.size[1])
ratio = size[0] / float(size[1])
#The image is scaled/cropped vertically or horizontally depending on the ratio
if ratio > img_ratio:
img = img.resize((size[0], int(round(size[0] * img.size[1] / img.size[0]))),
Image.ANTIALIAS)
# Crop in the top, middle or bottom
if crop_type == 'top':
box = (0, 0, img.size[0], size[1])
elif crop_type == 'middle':
box = (0, int(round((img.size[1] - size[1]) / 2)), img.size[0],
int(round((img.size[1] + size[1]) / 2)))
elif crop_type == 'bottom':
box = (0, img.size[1] - size[1], img.size[0], img.size[1])
else :
raise ValueError('ERROR: invalid value for crop_type')
img = img.crop(box)
elif ratio < img_ratio:
img = img.resize((int(round(size[1] * img.size[0] / img.size[1])), size[1]),
Image.ANTIALIAS)
# Crop in the top, middle or bottom
if crop_type == 'top':
box = (0, 0, size[0], img.size[1])
elif crop_type == 'middle':
box = (int(round((img.size[0] - size[0]) / 2)), 0,
int(round((img.size[0] + size[0]) / 2)), img.size[1])
elif crop_type == 'bottom':
box = (img.size[0] - size[0], 0, img.size[0], img.size[1])
else :
raise ValueError('ERROR: invalid value for crop_type')
img = img.crop(box)
else :
img = img.resize((size[0], size[1]),
Image.ANTIALIAS)
# If the scale is the same, we do not need to crop
img.save(modified_path)
Cool!
Use this function and got an error...
Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
111. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "./board/views.py" in add
111. 'middle')
File "./board/views.py" in resize_and_crop
239. Image.ANTIALIAS)
File "/usr/lib/python2.7/dist-packages/PIL/Image.py" in resize
1319. self.load()
File "/usr/lib/python2.7/dist-packages/PIL/ImageFile.py" in load
218. raise IOError("image file is truncated (%d bytes not processed)" % len(b))
Exception Type: IOError at /add
Exception Value: image file is truncated (16 bytes not processed)
Django Version: 1.7.1
Python Version: 2.7.6
Ubuntu 14.04 64-bit
awsome man... I will use it in my project...
For middle crop (the most popular case, imho) you can use ImageOps.fit.
thumbnail = ImageOps.fit(
thumbnail,
(height, width),
Image.ANTIALIAS
)
@ProDG: You're the real MVP!!
good hint @ProDG
but his script is faster
PIL : 0.029497146606445312
Script: 0.023000240325927734
Good job 👍
Thanks! @ProDG
Very grateful, really found!