django-imagekit/imagekit/processors.py

187 lines
6.6 KiB
Python
Raw Normal View History

2009-01-04 17:38:06 +00:00
""" Imagekit Image "ImageProcessors"
2009-12-19 16:01:54 +00:00
A processor defines a set of class variables (optional) and a
2009-01-04 17:38:06 +00:00
class method named "process" which processes the supplied image using
the class properties as settings. The process method can be overridden as well allowing user to define their
own effects/processes entirely.
"""
2009-01-08 21:11:15 +00:00
from imagekit.lib import *
2009-01-04 17:38:06 +00:00
class ImageProcessor(object):
""" Base image processor class """
2009-12-19 16:01:54 +00:00
def process(self, img, fmt, obj, spec):
2009-06-04 15:06:11 +00:00
return img, fmt
2009-12-19 16:01:54 +00:00
2009-01-04 17:38:06 +00:00
2011-09-08 13:15:08 +00:00
class Adjust(ImageProcessor):
def __init__(self, color=1.0, brightness=1.0, contrast=1.0, sharpness=1.0):
self.color = color
self.brightness = brightness
self.contrast = contrast
self.sharpness = sharpness
2009-01-09 14:07:10 +00:00
def process(self, img, fmt, obj, spec):
2009-06-04 15:06:11 +00:00
img = img.convert('RGB')
2009-01-09 14:07:10 +00:00
for name in ['Color', 'Brightness', 'Contrast', 'Sharpness']:
2011-09-08 13:15:08 +00:00
factor = getattr(self, name.lower())
2009-01-09 14:07:10 +00:00
if factor != 1.0:
2009-06-04 15:06:11 +00:00
try:
img = getattr(ImageEnhance, name)(img).enhance(factor)
except ValueError:
pass
return img, fmt
2009-01-09 14:07:10 +00:00
class Reflection(ImageProcessor):
background_color = '#FFFFFF'
size = 0.0
opacity = 0.6
2009-12-19 16:01:54 +00:00
def process(self, img, fmt, obj, spec):
2009-01-09 14:07:10 +00:00
# convert bgcolor string to rgb value
2011-09-08 13:15:08 +00:00
background_color = ImageColor.getrgb(self.background_color)
2009-06-04 15:06:11 +00:00
# handle palleted images
img = img.convert('RGB')
2009-01-09 14:07:10 +00:00
# copy orignial image and flip the orientation
2009-06-04 15:06:11 +00:00
reflection = img.copy().transpose(Image.FLIP_TOP_BOTTOM)
2009-01-09 14:07:10 +00:00
# create a new image filled with the bgcolor the same size
2009-06-04 15:06:11 +00:00
background = Image.new("RGB", img.size, background_color)
2009-01-09 14:07:10 +00:00
# calculate our alpha mask
2011-09-08 13:15:08 +00:00
start = int(255 - (255 * self.opacity)) # The start of our gradient
steps = int(255 * self.size) # the number of intermedite values
2009-01-09 14:07:10 +00:00
increment = (255 - start) / float(steps)
mask = Image.new('L', (1, 255))
for y in range(255):
if y < steps:
val = int(y * increment + start)
else:
val = 255
mask.putpixel((0, y), val)
2009-06-04 15:06:11 +00:00
alpha_mask = mask.resize(img.size)
2009-01-09 14:07:10 +00:00
# merge the reflection onto our background color using the alpha mask
reflection = Image.composite(background, reflection, alpha_mask)
# crop the reflection
2011-09-08 13:15:08 +00:00
reflection_height = int(img.size[1] * self.size)
2009-06-04 15:06:11 +00:00
reflection = reflection.crop((0, 0, img.size[0], reflection_height))
2009-01-09 14:07:10 +00:00
# create new image sized to hold both the original image and the reflection
2009-06-04 15:06:11 +00:00
composite = Image.new("RGB", (img.size[0], img.size[1]+reflection_height), background_color)
2009-01-09 14:07:10 +00:00
# paste the orignal image and the reflection into the composite image
2009-06-04 15:06:11 +00:00
composite.paste(img, (0, 0))
composite.paste(reflection, (0, img.size[1]))
# Save the file as a JPEG
fmt = 'JPEG'
2009-01-09 14:07:10 +00:00
# return the image complete with reflection effect
2009-06-04 15:06:11 +00:00
return composite, fmt
2009-01-09 14:07:10 +00:00
2011-09-08 13:15:31 +00:00
class _Resize(ImageProcessor):
2011-09-08 13:15:08 +00:00
2011-09-08 20:49:44 +00:00
width = None
height = None
crop = False
upscale = False
def __init__(self, width=None, height=None, crop=None, upscale=None):
if width is not None:
self.width = width
if height is not None:
self.height = height
if crop is not None:
self.crop = crop
if upscale is not None:
self.upscale = upscale
2011-09-08 13:15:08 +00:00
def process(self, img, fmt, obj, spec):
2009-06-04 15:06:11 +00:00
cur_width, cur_height = img.size
2011-09-08 13:15:08 +00:00
if self.crop:
2009-01-08 18:37:15 +00:00
crop_horz = getattr(obj, obj._ik.crop_horz_field, 1)
crop_vert = getattr(obj, obj._ik.crop_vert_field, 1)
2011-09-08 13:15:08 +00:00
ratio = max(float(self.width)/cur_width, float(self.height)/cur_height)
2009-01-04 22:14:13 +00:00
resize_x, resize_y = ((cur_width * ratio), (cur_height * ratio))
2011-09-08 13:15:08 +00:00
crop_x, crop_y = (abs(self.width - resize_x), abs(self.height - resize_y))
2009-01-04 22:14:13 +00:00
x_diff, y_diff = (int(crop_x / 2), int(crop_y / 2))
box_left, box_right = {
2011-09-08 13:15:08 +00:00
0: (0, self.width),
1: (int(x_diff), int(x_diff + self.width)),
2009-01-08 18:37:15 +00:00
2: (int(crop_x), int(resize_x)),
}[crop_horz]
box_upper, box_lower = {
2011-09-08 13:15:08 +00:00
0: (0, self.height),
1: (int(y_diff), int(y_diff + self.height)),
2009-01-08 18:37:15 +00:00
2: (int(crop_y), int(resize_y)),
}[crop_vert]
2009-01-04 22:14:13 +00:00
box = (box_left, box_upper, box_right, box_lower)
2009-06-04 15:06:11 +00:00
img = img.resize((int(resize_x), int(resize_y)), Image.ANTIALIAS).crop(box)
2009-01-04 22:14:13 +00:00
else:
2011-09-08 13:15:08 +00:00
if not self.width is None and not self.height is None:
ratio = min(float(self.width)/cur_width,
float(self.height)/cur_height)
2009-01-04 22:14:13 +00:00
else:
2011-09-08 13:15:08 +00:00
if self.width is None:
ratio = float(self.height)/cur_height
2009-01-04 22:14:13 +00:00
else:
2011-09-08 13:15:08 +00:00
ratio = float(self.width)/cur_width
2009-01-04 22:14:13 +00:00
new_dimensions = (int(round(cur_width*ratio)),
int(round(cur_height*ratio)))
if new_dimensions[0] > cur_width or \
new_dimensions[1] > cur_height:
2011-09-08 13:15:08 +00:00
if not self.upscale:
2009-06-04 15:06:11 +00:00
return img, fmt
img = img.resize(new_dimensions, Image.ANTIALIAS)
return img, fmt
2009-01-04 22:14:13 +00:00
2009-12-19 16:01:54 +00:00
2011-09-08 13:15:31 +00:00
class Crop(_Resize):
2011-09-08 20:49:44 +00:00
def __init__(self, width=None, height=None):
super(Crop, self).__init__(width, height, crop=True)
2011-09-08 13:15:31 +00:00
class Fit(_Resize):
2011-09-08 20:49:44 +00:00
def __init__(self, width=None, height=None, upscale=None):
super(Fit, self).__init__(width, height, crop=False, upscale=upscale)
2011-09-08 13:15:31 +00:00
2009-01-04 17:38:06 +00:00
class Transpose(ImageProcessor):
""" Rotates or flips the image
2009-12-19 16:01:54 +00:00
2009-01-09 14:07:10 +00:00
Method should be one of the following strings:
2009-01-04 17:38:06 +00:00
- FLIP_LEFT RIGHT
- FLIP_TOP_BOTTOM
- ROTATE_90
- ROTATE_270
- ROTATE_180
- auto
2009-12-19 16:01:54 +00:00
If method is set to 'auto' the processor will attempt to rotate the image
according to the EXIF Orientation data.
2009-12-19 16:01:54 +00:00
2009-01-04 17:38:06 +00:00
"""
EXIF_ORIENTATION_STEPS = {
1: [],
2: ['FLIP_LEFT_RIGHT'],
3: ['ROTATE_180'],
4: ['FLIP_TOP_BOTTOM'],
5: ['ROTATE_270', 'FLIP_LEFT_RIGHT'],
6: ['ROTATE_270'],
7: ['ROTATE_90', 'FLIP_LEFT_RIGHT'],
8: ['ROTATE_90'],
}
2009-12-19 16:01:54 +00:00
method = 'auto'
2009-12-19 16:01:54 +00:00
def process(self, img, fmt, obj, spec):
2011-09-08 13:15:08 +00:00
if self.method == 'auto':
try:
orientation = Image.open(spec._get_imgfield(obj).file)._getexif()[0x0112]
2011-09-08 13:15:08 +00:00
ops = self.EXIF_ORIENTATION_STEPS[orientation]
except:
ops = []
else:
2011-09-08 13:15:08 +00:00
ops = [self.method]
for method in ops:
img = img.transpose(getattr(Image, method))
2009-06-04 15:06:11 +00:00
return img, fmt