Python Library Media
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

image.py 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import io
  2. import logging
  3. from PIL import Image, ImageEnhance, ExifTags
  4. try:
  5. from config import APP_NAME as ROOT_LOGGER_NAME
  6. except ImportError:
  7. ROOT_LOGGER_NAME = 'root'
  8. logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
  9. ORIENTATION_NORMAL = 1
  10. ORIENTATION_VERTICAL_MIRRORED = 2
  11. ORIENTATION_HALF_ROTATED = 3
  12. ORIENTATION_HORIZONTAL_MIRRORED = 4
  13. ORIENTATION_LEFT_ROTATED = 6
  14. ORIENTATION_RIGHT_ROTATED = 8
  15. JOIN_TOP_LEFT = 1
  16. JOIN_TOP_RIGHT = 2
  17. JOIN_BOT_LEFT = 3
  18. JOIN_BOT_RIGHT = 4
  19. JOIN_CENTER = 5
  20. class image(object):
  21. def __init__(self, media_instance=None):
  22. if media_instance is not None:
  23. self.load_from_file(media_instance)
  24. else:
  25. self._im = None
  26. def load_from_file(self, media_instance):
  27. from media.convert import get_pil_image
  28. #
  29. self._im = get_pil_image(media_instance)
  30. if self._im is None:
  31. return False
  32. try:
  33. self._exif = dict(self._im._getexif().items())
  34. except AttributeError:
  35. self._exif = {}
  36. if type(self._im) is not Image.Image:
  37. self._im = self._im.copy()
  38. logger.debug('loading image from %s', repr(media_instance))
  39. return True
  40. def save(self, full_path):
  41. if self._im is None:
  42. logger.warning('No image available to be saved (%s)', repr(full_path))
  43. return False
  44. else:
  45. logger.debug('Saving image to %s', repr(full_path))
  46. with open(full_path, 'w') as fh:
  47. im = self._im.convert('RGB')
  48. im.save(fh, 'JPEG')
  49. return True
  50. def image_data(self):
  51. im = self._im.copy().convert('RGB')
  52. output = io.BytesIO()
  53. im.save(output, format='JPEG')
  54. return output.getvalue()
  55. def resize(self, max_size):
  56. if self._im is None:
  57. logger.warning('No image available to be resized')
  58. return False
  59. else:
  60. logger.debug('Resizing picture to max %d pixel in whatever direction', max_size)
  61. x, y = self._im.size
  62. xy_max = max(x, y)
  63. self._im = self._im.resize((int(x * float(max_size) / xy_max), int(y * float(max_size) / xy_max)), Image.NEAREST).rotate(0)
  64. return True
  65. def rotate_by_orientation(self, orientation=None):
  66. if self._im is None:
  67. logger.warning('No image available, rotation not possible')
  68. return False
  69. if orientation is None:
  70. exif_tags = dict((v, k) for k, v in ExifTags.TAGS.items())
  71. try:
  72. orientation = self._exif[exif_tags['Orientation']]
  73. logger.debug("No orientation given, orientation %s extract from exif data", repr(orientation))
  74. except KeyError:
  75. return False
  76. if orientation == ORIENTATION_HALF_ROTATED:
  77. angle = 180
  78. elif orientation == ORIENTATION_LEFT_ROTATED:
  79. angle = 270
  80. elif orientation == ORIENTATION_RIGHT_ROTATED:
  81. angle = 90
  82. else:
  83. if type(orientation) == int and orientation > 8:
  84. logger.warning('Orientation %s unknown for rotation', repr(orientation))
  85. return False
  86. logger.debug('Rotating picture by %d (deg)', angle)
  87. self._im = self._im.rotate(angle, expand=True)
  88. return True
  89. def join(self, join_image, join_pos=JOIN_TOP_RIGHT, opacity=0.7):
  90. from media.convert import get_pil_image
  91. def rgba_copy(im):
  92. if im.mode != 'RGBA':
  93. return im.convert('RGBA')
  94. else:
  95. return im.copy()
  96. if self._im is None:
  97. logger.warning('No image available, joining not possible')
  98. return False
  99. # ensure type of join_image is PIL.Image
  100. join_image = get_pil_image(join_image)
  101. if join_image is None:
  102. logger.warning('Image to be joined is not supported %s', repr(join_image))
  103. return False
  104. im2 = rgba_copy(join_image)
  105. # change opacity of im2
  106. alpha = im2.split()[3]
  107. alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
  108. im2.putalpha(alpha)
  109. self._im = rgba_copy(self._im)
  110. # create a transparent layer
  111. layer = Image.new('RGBA', self._im.size, (0, 0, 0, 0))
  112. # draw im2 in layer
  113. if join_pos == JOIN_TOP_LEFT:
  114. layer.paste(im2, (0, 0))
  115. elif join_pos == JOIN_TOP_RIGHT:
  116. layer.paste(im2, ((self._im.size[0] - im2.size[0]), 0))
  117. elif join_pos == JOIN_BOT_LEFT:
  118. layer.paste(im2, (0, (self._im.size[1] - im2.size[1])))
  119. elif join_pos == JOIN_BOT_RIGHT:
  120. layer.paste(im2, ((self._im.size[0] - im2.size[0]), (self._im.size[1] - im2.size[1])))
  121. elif join_pos == JOIN_CENTER:
  122. layer.paste(im2, (int((self._im.size[0] - im2.size[0]) / 2), int((self._im.size[1] - im2.size[1]) / 2)))
  123. else:
  124. logger.warning("Join position value %s is not supported", join_pos)
  125. return False
  126. logger.debug('Joining two images')
  127. self._im = Image.composite(layer, self._im, layer)
  128. return True