import io import logging from PIL import Image, ImageEnhance, ExifTags try: from config import APP_NAME as ROOT_LOGGER_NAME except ImportError: ROOT_LOGGER_NAME = 'root' logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) ORIENTATION_NORMAL = 1 ORIENTATION_VERTICAL_MIRRORED = 2 ORIENTATION_HALF_ROTATED = 3 ORIENTATION_HORIZONTAL_MIRRORED = 4 ORIENTATION_LEFT_ROTATED = 6 ORIENTATION_RIGHT_ROTATED = 8 JOIN_TOP_LEFT = 1 JOIN_TOP_RIGHT = 2 JOIN_BOT_LEFT = 3 JOIN_BOT_RIGHT = 4 JOIN_CENTER = 5 class image(object): def __init__(self, media_instance=None): if media_instance is not None: self.load_from_file(media_instance) else: self._im = None def load_from_file(self, media_instance): from media.convert import get_pil_image # self._im = get_pil_image(media_instance) if self._im is None: return False try: self._exif = dict(self._im._getexif().items()) except AttributeError: self._exif = {} if type(self._im) is not Image.Image: self._im = self._im.copy() logger.debug('loading image from %s', repr(media_instance)) return True def save(self, full_path): if self._im is None: logger.warning('No image available to be saved (%s)', repr(full_path)) return False else: logger.debug('Saving image to %s', repr(full_path)) with open(full_path, 'w') as fh: im = self._im.convert('RGB') im.save(fh, 'JPEG') return True def image_data(self): im = self._im.copy().convert('RGB') output = io.BytesIO() im.save(output, format='JPEG') return output.getvalue() def resize(self, max_size): if self._im is None: logger.warning('No image available to be resized') return False else: logger.debug('Resizing picture to max %d pixel in whatever direction', max_size) x, y = self._im.size xy_max = max(x, y) self._im = self._im.resize((int(x * float(max_size) / xy_max), int(y * float(max_size) / xy_max)), Image.NEAREST).rotate(0) return True def rotate_by_orientation(self, orientation=None): if self._im is None: logger.warning('No image available, rotation not possible') return False if orientation is None: exif_tags = dict((v, k) for k, v in ExifTags.TAGS.items()) try: orientation = self._exif[exif_tags['Orientation']] logger.debug("No orientation given, orientation %s extract from exif data", repr(orientation)) except KeyError: return False if orientation == ORIENTATION_HALF_ROTATED: angle = 180 elif orientation == ORIENTATION_LEFT_ROTATED: angle = 270 elif orientation == ORIENTATION_RIGHT_ROTATED: angle = 90 else: if type(orientation) == int and orientation > 8: logger.warning('Orientation %s unknown for rotation', repr(orientation)) return False logger.debug('Rotating picture by %d (deg)', angle) self._im = self._im.rotate(angle, expand=True) return True def join(self, join_image, join_pos=JOIN_TOP_RIGHT, opacity=0.7): from media.convert import get_pil_image def rgba_copy(im): if im.mode != 'RGBA': return im.convert('RGBA') else: return im.copy() if self._im is None: logger.warning('No image available, joining not possible') return False # ensure type of join_image is PIL.Image join_image = get_pil_image(join_image) if join_image is None: logger.warning('Image to be joined is not supported %s', repr(join_image)) return False im2 = rgba_copy(join_image) # change opacity of im2 alpha = im2.split()[3] alpha = ImageEnhance.Brightness(alpha).enhance(opacity) im2.putalpha(alpha) self._im = rgba_copy(self._im) # create a transparent layer layer = Image.new('RGBA', self._im.size, (0, 0, 0, 0)) # draw im2 in layer if join_pos == JOIN_TOP_LEFT: layer.paste(im2, (0, 0)) elif join_pos == JOIN_TOP_RIGHT: layer.paste(im2, ((self._im.size[0] - im2.size[0]), 0)) elif join_pos == JOIN_BOT_LEFT: layer.paste(im2, (0, (self._im.size[1] - im2.size[1]))) elif join_pos == JOIN_BOT_RIGHT: layer.paste(im2, ((self._im.size[0] - im2.size[0]), (self._im.size[1] - im2.size[1]))) elif join_pos == JOIN_CENTER: layer.paste(im2, (int((self._im.size[0] - im2.size[0]) / 2), int((self._im.size[1] - im2.size[1]) / 2))) else: logger.warning("Join position value %s is not supported", join_pos) return False logger.debug('Joining two images') self._im = Image.composite(layer, self._im, layer) return True