Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

123 wiersze
3.6 KiB

  1. from contextlib import contextmanager
  2. from io import BytesIO
  3. import PIL
  4. from PIL import Image
  5. @contextmanager
  6. def open_django_file(field_file):
  7. field_file.open()
  8. try:
  9. yield field_file
  10. finally:
  11. field_file.close()
  12. def scale_and_crop_iter(image, options):
  13. """
  14. Generator which will yield several variations on the input image.
  15. Resize, crop and/or change quality of image.
  16. :param image: Source image file
  17. :type image : :class:`django.core.files.images.ImageFile
  18. :param options: List of option dictionaries, See scale_and_crop_single
  19. argument names for available keys.
  20. :type options: list of dict
  21. """
  22. with open_django_file(image) as img:
  23. im = Image.open(img)
  24. im.load()
  25. for opts in options:
  26. # Use already-loaded file when cropping.
  27. yield scale_and_crop_single(im, **opts)
  28. # this neat function is based on easy-thumbnails
  29. def scale_and_crop_single(image, size, crop=False, upscale=False, quality=None):
  30. """
  31. Resize, crop and/or change quality of an image.
  32. :param image: Source image file
  33. :type image: :class:`PIL.Image`
  34. :param size: Size as width & height, zero as either means unrestricted
  35. :type size: tuple of two int
  36. :param crop: Truncate image or not
  37. :type crop: bool
  38. :param upscale: Enable scale up
  39. :type upscale: bool
  40. :param quality: Value between 1 to 95, or None for keep the same
  41. :type quality: int or NoneType
  42. :return: Handled image
  43. :rtype: class:`PIL.Image`
  44. """
  45. im = image
  46. source_x, source_y = [float(v) for v in im.size]
  47. target_x, target_y = [float(v) for v in size]
  48. if crop or not target_x or not target_y:
  49. scale = max(target_x / source_x, target_y / source_y)
  50. else:
  51. scale = min(target_x / source_x, target_y / source_y)
  52. # Handle one-dimensional targets.
  53. if not target_x:
  54. target_x = source_x * scale
  55. elif not target_y:
  56. target_y = source_y * scale
  57. if scale < 1.0 or (scale > 1.0 and upscale):
  58. im = im.resize((int(source_x * scale), int(source_y * scale)),
  59. resample=Image.ANTIALIAS)
  60. if crop:
  61. # Use integer values now.
  62. source_x, source_y = im.size
  63. # Difference between new image size and requested size.
  64. diff_x = int(source_x - min(source_x, target_x))
  65. diff_y = int(source_y - min(source_y, target_y))
  66. if diff_x or diff_y:
  67. # Center cropping (default).
  68. half_diff_x, half_diff_y = diff_x // 2, diff_y // 2
  69. box = [half_diff_x, half_diff_y,
  70. min(source_x, int(target_x) + half_diff_x),
  71. min(source_y, int(target_y) + half_diff_y)]
  72. # Finally, crop the image!
  73. im = im.crop(box)
  74. # Close image and replace format/metadata, as PIL blows this away.
  75. # We mutate the quality, but needs to passed into save() to actually
  76. # do anything.
  77. info = image.info
  78. if quality is not None:
  79. info['quality'] = quality
  80. im.format, im.info = image.format, info
  81. return im
  82. def write_image_in_memory(img):
  83. # save to memory
  84. buf = BytesIO()
  85. try:
  86. img.save(buf, img.format, **img.info)
  87. except IOError:
  88. if img.info.get('progression'):
  89. orig_MAXBLOCK = PIL.ImageFile.MAXBLOCK
  90. temp_MAXBLOCK = 1048576
  91. if orig_MAXBLOCK >= temp_MAXBLOCK:
  92. raise
  93. PIL.ImageFile.MAXBLOCK = temp_MAXBLOCK
  94. try:
  95. img.save(buf, img.format, **img.info)
  96. finally:
  97. PIL.ImageFile.MAXBLOCK = orig_MAXBLOCK
  98. else:
  99. raise
  100. return buf