Skip to content

Instantly share code, notes, and snippets.

@timmyomahony
Last active September 29, 2015 18:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timmyomahony/1647660 to your computer and use it in GitHub Desktop.
Save timmyomahony/1647660 to your computer and use it in GitHub Desktop.
Custom sorl.thumbnail engine to perform specific crops
from sorl.thumbnail.engines import pil_engine
from sorl.thumbnail import parsers
class CropperEngine(pil_engine.Engine):
"""
A custom sorl.thumbnail engine (using PIL) that first crops an image according to 4 pixel/percentage
values in the source image, then scales that crop down to the size specified in the geometry. This is
in contrast to sorl.thumbnails default engine which first scales the image down to the specified geometry
and applies the crop afterward.
"""
def create(self, image, geometry, options):
image = self.orientation(image, geometry, options)
image = self.colorspace(image, geometry, options)
image = self.crop(image, geometry, options)
image = self.scale(image, geometry, options)
return image
def _crop_parse(self, crop, xy_image, xy_window):
""" Taken from sorl.thumbnail.parsers.crop_parse and (roughly!) adapted here """
crops = crop.split(' ')
if len(crops) != 4:
raise parsers.ThumbnailParseError('Unrecognized crop option: %s' % crop)
x1, y1, x2, y2 = crops
def get_offset(crop, epsilon):
m = parsers.bgpos_pat.match(crop)
if not m:
raise parsers.ThumbnailParseError('Unrecognized crop option: %s' % crop)
value = int(m.group('value')) # we only take ints in the regexp
unit = m.group('unit')
if unit == '%':
value = epsilon * value / 100.0
return int(max(0, min(value, epsilon)))
x1 = get_offset(x1, xy_image[0])
y1 = get_offset(y1, xy_image[1])
x2 = get_offset(x2, xy_image[0])
y2 = get_offset(y2, xy_image[1])
return x1, y1, x2, y2
def crop(self, image, geometry, options):
crop = options['crop']
if not crop or crop == 'noop':
return image
x_image, y_image = self.get_image_size(image)
x1,y1,x2,y2 = self._crop_parse(crop, (x_image, y_image), geometry)
return self._crop(image, x1, y1, x2, y2)
def _crop(self, image, x1, y1, x2, y2):
return image.crop((x1, y1, x2, y2))
def get_crop():
"""
An example of how to use the custom cropping engine inline.
x1, y1, x2, y2 are percentage based values of where to apply the crop (they can also be pixel based)
"""
img = ... // The full-size source image
x1 = 10
y1 = 10
x2 = 80
y2 = 50
use_cropper = True
if self.use_cropper:
# Swap engines
old = default.engine
default.engine = CropperEngine()
crop = get_thumbnail(
self.source,
"%sx%s" % (crop_width, crop_height),
crop="%s%% %s%% %s%% %s%%" % (int(x1), int(y1), int(x2), int(y2)))
# Replace normal engine
default.engine = old
return crop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment