media/image.py

151 lines
5.1 KiB
Python

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