Commits

Chris Beaven  committed 3daaf2e

Add a "smart cropping" argument to the crop option, which incrementally crops down to the requested size by removing slices from edges with the least entropy.

  • Participants
  • Parent commits 095e19b

Comments (0)

Files changed (3)

File docs/index.rst

     
     * ``crop=",0"`` will keep the default behavior for the x axis (horizontally
       centering the image) and crop from the top edge.
+    
+    The image can also be "smart cropped" by using ``crop="smart"``. The image
+    is incrementally cropped down to the requested size by removing slices
+    from edges with the least entropy. 
 
 max
     Will resize the image to the same size as the *crop* option but it

File sorl/thumbnail/processors.py

 from PIL import Image, ImageFilter, ImageChops
+from sorl.thumbnail import utils
 import re
 
 
             # Center cropping (default).
             ex, ey = dx / 2, dy / 2
             box = [ex, ey, x - ex, y - ey]
-            # See if an edge cropping argument was provided. 
+            # See if an edge cropping argument was provided.
             edge_crop = (isinstance(crop, basestring) and
                            re.match(r'(?:(-?)(\d+))?,(?:(-?)(\d+))?$', crop))
             if edge_crop and filter(None, edge_crop.groups()):
                     else:
                         box[1] = offset
                         box[3] = y - (dy - offset)
-            #assert False, (box, (dx, dy), (xr, yr))
+            # See if the image should be "smart cropped".
+            elif crop == 'smart':
+                left = top = 0
+                right, bottom = x, y
+                while dx:
+                    slice = min(dx, 10)
+                    l_sl = im.crop((0, 0, slice, y))
+                    r_sl = im.crop((x - slice, 0, x, y))
+                    if utils.image_entropy(l_sl) >= utils.image_entropy(r_sl):
+                        right -= slice
+                    else:
+                        left += slice
+                    dx -= slice
+                while dy:
+                    slice = min(dy, 10)
+                    t_sl = im.crop((0, 0, x, slice))
+                    b_sl = im.crop((0, y - slice, x, y))
+                    if utils.image_entropy(t_sl) >= utils.image_entropy(b_sl):
+                        bottom -= slice
+                    else:
+                        top += slice
+                    dy -= slice
+                box = (left, top, right, bottom)
+            # Finally, crop the image!
             im = im.crop([int(v) for v in box])
     return im
 scale_and_crop.valid_options = ('crop', 'upscale', 'max')

File sorl/thumbnail/utils.py

+import math
+import os
 import re
-import os
 
 
 re_thumbnail_file = re.compile(r'(?P<source_filename>.+)_(?P<x>\d+)x(?P<y>\d+)'
         value = len(split_arg) > 1 and split_arg[1] or None
         args_dict[split_arg[0]] = value
     return args_dict
+
+
+def image_entropy(im):
+    """
+    Calculate the entropy of an image. Used for "smart cropping".
+    """
+    hist = im.histogram()
+    hist_size = float(sum(hist))
+    hist = [h / hist_size for h in hist]
+    return -sum([p * math.log(p, 2) for p in hist if p != 0])