easy_thumbnails.processors: 113 total statements, 0.0% covered

Generated: Wed 2013-03-13 10:33 CET

Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/easy_thumbnails/processors.py

Stats: 0 executed, 107 missed, 6 excluded, 122 ignored

  1. import re
  2. try:
  3. from PIL import Image, ImageChops, ImageFilter
  4. except ImportError:
  5. import Image
  6. import ImageChops
  7. import ImageFilter
  8. from easy_thumbnails import utils
  9. def _compare_entropy(start_slice, end_slice, slice, difference):
  10. """
  11. Calculate the entropy of two slices (from the start and end of an axis),
  12. returning a tuple containing the amount that should be added to the start
  13. and removed from the end of the axis.
  14. """
  15. start_entropy = utils.image_entropy(start_slice)
  16. end_entropy = utils.image_entropy(end_slice)
  17. if end_entropy and abs(start_entropy / end_entropy - 1) < 0.01:
  18. # Less than 1% difference, remove from both sides.
  19. if difference >= slice * 2:
  20. return slice, slice
  21. half_slice = slice // 2
  22. return half_slice, slice - half_slice
  23. if start_entropy > end_entropy:
  24. return 0, slice
  25. else:
  26. return slice, 0
  27. def colorspace(im, bw=False, replace_alpha=False, **kwargs):
  28. """
  29. Convert images to the correct color space.
  30. A passive option (i.e. always processed) of this method is that all images
  31. (unless grayscale) are converted to RGB colorspace.
  32. This processor should be listed before :func:`scale_and_crop` so palette is
  33. changed before the image is resized.
  34. bw
  35. Make the thumbnail grayscale (not really just black & white).
  36. replace_alpha
  37. Replace any transparency layer with a solid color. For example,
  38. ``replace_alpha='#fff'`` would replace the transparency layer with
  39. white.
  40. """
  41. is_transparent = utils.is_transparent(im)
  42. if bw:
  43. if im.mode in ('L', 'LA'):
  44. return im
  45. if is_transparent:
  46. return im.convert('LA')
  47. else:
  48. return im.convert('L')
  49. if im.mode in ('L', 'RGB'):
  50. return im
  51. if is_transparent:
  52. if im.mode != 'RGBA':
  53. im = im.convert('RGBA')
  54. if not replace_alpha:
  55. return im
  56. base = Image.new('RGBA', im.size, replace_alpha)
  57. base.paste(im)
  58. im = base
  59. return im.convert('RGB')
  60. def autocrop(im, autocrop=False, **kwargs):
  61. """
  62. Remove any unnecessary whitespace from the edges of the source image.
  63. This processor should be listed before :func:`scale_and_crop` so the
  64. whitespace is removed from the source image before it is resized.
  65. autocrop
  66. Activates the autocrop method for this image.
  67. """
  68. if autocrop:
  69. bw = im.convert('1')
  70. bw = bw.filter(ImageFilter.MedianFilter)
  71. # White background.
  72. bg = Image.new('1', im.size, 255)
  73. diff = ImageChops.difference(bw, bg)
  74. bbox = diff.getbbox()
  75. if bbox:
  76. im = im.crop(bbox)
  77. return im
  78. def scale_and_crop(im, size, crop=False, upscale=False, **kwargs):
  79. """
  80. Handle scaling and cropping the source image.
  81. Images can be scaled / cropped against a single dimension by using zero
  82. as the placeholder in the size. For example, ``size=(100, 0)`` will cause
  83. the image to be resized to 100 pixels wide, keeping the aspect ratio of
  84. the source image.
  85. crop
  86. Crop the source image height or width to exactly match the requested
  87. thumbnail size (the default is to proportionally resize the source
  88. image to fit within the requested thumbnail size).
  89. By default, the image is centered before being cropped. To crop from
  90. the edges, pass a comma separated string containing the ``x`` and ``y``
  91. percentage offsets (negative values go from the right/bottom). Some
  92. examples follow:
  93. * ``crop="0,0"`` will crop from the left and top edges.
  94. * ``crop="-10,-0"`` will crop from the right edge (with a 10% offset)
  95. and the bottom edge.
  96. * ``crop=",0"`` will keep the default behavior for the x axis
  97. (horizontally centering the image) and crop from the top edge.
  98. The image can also be "smart cropped" by using ``crop="smart"``. The
  99. image is incrementally cropped down to the requested size by removing
  100. slices from edges with the least entropy.
  101. Finally, you can use ``crop="scale"`` to simply scale the image so that
  102. at least one dimension fits within the size dimensions given (you may
  103. want to use the upscale option too).
  104. upscale
  105. Allow upscaling of the source image during scaling.
  106. """
  107. source_x, source_y = [float(v) for v in im.size]
  108. target_x, target_y = [float(v) for v in size]
  109. if crop or not target_x or not target_y:
  110. scale = max(target_x / source_x, target_y / source_y)
  111. else:
  112. scale = min(target_x / source_x, target_y / source_y)
  113. # Handle one-dimensional targets.
  114. if not target_x:
  115. target_x = source_x * scale
  116. elif not target_y:
  117. target_y = source_y * scale
  118. if scale < 1.0 or (scale > 1.0 and upscale):
  119. # Resize the image to the target size boundary. Round the scaled
  120. # boundary sizes to avoid floating point errors.
  121. im = im.resize((int(round(source_x * scale)),
  122. int(round(source_y * scale))),
  123. resample=Image.ANTIALIAS)
  124. if crop:
  125. # Use integer values now.
  126. source_x, source_y = im.size
  127. # Difference between new image size and requested size.
  128. diff_x = int(source_x - min(source_x, target_x))
  129. diff_y = int(source_y - min(source_y, target_y))
  130. if diff_x or diff_y:
  131. # Center cropping (default).
  132. halfdiff_x, halfdiff_y = diff_x // 2, diff_y // 2
  133. box = [halfdiff_x, halfdiff_y,
  134. min(source_x, int(target_x) + halfdiff_x),
  135. min(source_y, int(target_y) + halfdiff_y)]
  136. # See if an edge cropping argument was provided.
  137. edge_crop = (isinstance(crop, basestring) and
  138. re.match(r'(?:(-?)(\d+))?,(?:(-?)(\d+))?$', crop))
  139. if edge_crop and filter(None, edge_crop.groups()):
  140. x_right, x_crop, y_bottom, y_crop = edge_crop.groups()
  141. if x_crop:
  142. offset = min(int(target_x) * int(x_crop) // 100, diff_x)
  143. if x_right:
  144. box[0] = diff_x - offset
  145. box[2] = source_x - offset
  146. else:
  147. box[0] = offset
  148. box[2] = source_x - (diff_x - offset)
  149. if y_crop:
  150. offset = min(int(target_y) * int(y_crop) // 100, diff_y)
  151. if y_bottom:
  152. box[1] = diff_y - offset
  153. box[3] = source_y - offset
  154. else:
  155. box[1] = offset
  156. box[3] = source_y - (diff_y - offset)
  157. # See if the image should be "smart cropped".
  158. elif crop == 'smart':
  159. left = top = 0
  160. right, bottom = source_x, source_y
  161. while diff_x:
  162. slice = min(diff_x, max(diff_x // 5, 10))
  163. start = im.crop((left, 0, left + slice, source_y))
  164. end = im.crop((right - slice, 0, right, source_y))
  165. add, remove = _compare_entropy(start, end, slice, diff_x)
  166. left += add
  167. right -= remove
  168. diff_x = diff_x - add - remove
  169. while diff_y:
  170. slice = min(diff_y, max(diff_y // 5, 10))
  171. start = im.crop((0, top, source_x, top + slice))
  172. end = im.crop((0, bottom - slice, source_x, bottom))
  173. add, remove = _compare_entropy(start, end, slice, diff_y)
  174. top += add
  175. bottom -= remove
  176. diff_y = diff_y - add - remove
  177. box = (left, top, right, bottom)
  178. # Finally, crop the image!
  179. if crop != 'scale':
  180. im = im.crop(box)
  181. return im
  182. def filters(im, detail=False, sharpen=False, **kwargs):
  183. """
  184. Pass the source image through post-processing filters.
  185. sharpen
  186. Sharpen the thumbnail image (using the PIL sharpen filter)
  187. detail
  188. Add detail to the image, like a mild *sharpen* (using the PIL
  189. ``detail`` filter).
  190. """
  191. if detail:
  192. im = im.filter(ImageFilter.DETAIL)
  193. if sharpen:
  194. im = im.filter(ImageFilter.SHARPEN)
  195. return im