151 lines
5.1 KiB
Python
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
|