From cfd503a2eb801bca868d0fee56ccd2b529dab24c Mon Sep 17 00:00:00 2001 From: Matthew Tretter Date: Tue, 15 Nov 2011 23:42:43 -0500 Subject: [PATCH] TrimColor processor --- imagekit/lib.py | 5 +++- imagekit/processors/crop.py | 60 +++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 imagekit/processors/crop.py diff --git a/imagekit/lib.py b/imagekit/lib.py index d8b2aed..efacb79 100644 --- a/imagekit/lib.py +++ b/imagekit/lib.py @@ -1,7 +1,8 @@ # Required PIL classes may or may not be available from the root namespace # depending on the installation method used. try: - from PIL import Image, ImageColor, ImageChops, ImageEnhance, ImageFile, ImageFilter + from PIL import Image, ImageColor, ImageChops, ImageEnhance, ImageFile, \ + ImageFilter, ImageDraw, ImageStat except ImportError: try: import Image @@ -10,5 +11,7 @@ except ImportError: import ImageEnhance import ImageFile import ImageFilter + import ImageDraw + import ImageStat except ImportError: raise ImportError('ImageKit was unable to import the Python Imaging Library. Please confirm it`s installed and available on your current Python path.') diff --git a/imagekit/processors/crop.py b/imagekit/processors/crop.py new file mode 100644 index 0000000..ff2ea14 --- /dev/null +++ b/imagekit/processors/crop.py @@ -0,0 +1,60 @@ +from ..lib import Image, ImageChops, ImageDraw, ImageStat + + +class Side(object): + TOP = 't' + RIGHT = 'r' + BOTTOM = 'b' + LEFT = 'l' + ALL = (TOP, RIGHT, BOTTOM, LEFT) + + +def crop(img, bbox, sides=Side.ALL): + bbox = ( + bbox[0] if Side.LEFT in sides else 0, + bbox[1] if Side.TOP in sides else 0, + bbox[2] if Side.RIGHT in sides else img.size[0], + bbox[3] if Side.BOTTOM in sides else img.size[1], + ) + return img.crop(bbox) + + +def detect_border_color(img): + mask = Image.new('1', img.size, 1) + w, h = img.size[0] - 2, img.size[1] - 2 + if w > 0 and h > 0: + draw = ImageDraw.Draw(mask) + draw.rectangle([1, 1, w, h], 0) + return ImageStat.Stat(img.convert('RGBA').histogram(mask)).median + + +class TrimColor(object): + """Trims a color from the sides of an image. + + """ + def __init__(self, color=None, tolerance=0.3, sides=Side.ALL): + """ + :param color: The color to trim from the image, in a 4-tuple RGBA value, + where each component is an integer between 0 and 255, inclusive. If + no color is provided, the processor will attempt to detect the + border color automatically. + :param tolerance: A number between 0 and 1 where 0. Zero is the least + tolerant and one is the most. + :param sides: A list of sides that should be trimmed. Possible values + are provided by the :class:`Side` enum class. + + """ + self.color = color + self.sides = sides + self.tolerance = tolerance + + def process(self, img): + source = img.convert('RGBA') + border_color = self.color or tuple(detect_border_color(source)) + bg = Image.new('RGBA', img.size, border_color) + offset = -int(self.tolerance * 255) + bbox = ImageChops.subtract(bg, source, offset=offset).getbbox() + if bbox: + img = crop(img, bbox, self.sides) + + return img