mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-17 05:40:25 +00:00
Move SmartCrop to crop module
Addresses one of the issues raised by in @madisvain in #93
This commit is contained in:
parent
a041302c96
commit
09b97ee62f
4 changed files with 96 additions and 84 deletions
|
|
@ -1,4 +1,5 @@
|
|||
from ..lib import Image, ImageChops, ImageDraw, ImageStat
|
||||
from .utils import histogram_entropy
|
||||
|
||||
|
||||
class Side(object):
|
||||
|
|
@ -69,3 +70,70 @@ class TrimBorderColor(object):
|
|||
img = crop(img, bbox, self.sides)
|
||||
|
||||
return img
|
||||
|
||||
|
||||
class SmartCrop(object):
|
||||
"""
|
||||
Crop an image 'smartly' -- based on smart crop implementation from easy-thumbnails:
|
||||
|
||||
https://github.com/SmileyChris/easy-thumbnails/blob/master/easy_thumbnails/processors.py#L193
|
||||
|
||||
Smart cropping whittles away the parts of the image with the least entropy.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, width=None, height=None):
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def compare_entropy(self, start_slice, end_slice, slice, difference):
|
||||
"""
|
||||
Calculate the entropy of two slices (from the start and end of an axis),
|
||||
returning a tuple containing the amount that should be added to the start
|
||||
and removed from the end of the axis.
|
||||
|
||||
"""
|
||||
start_entropy = histogram_entropy(start_slice)
|
||||
end_entropy = histogram_entropy(end_slice)
|
||||
|
||||
if end_entropy and abs(start_entropy / end_entropy - 1) < 0.01:
|
||||
# Less than 1% difference, remove from both sides.
|
||||
if difference >= slice * 2:
|
||||
return slice, slice
|
||||
half_slice = slice // 2
|
||||
return half_slice, slice - half_slice
|
||||
|
||||
if start_entropy > end_entropy:
|
||||
return 0, slice
|
||||
else:
|
||||
return slice, 0
|
||||
|
||||
def process(self, img):
|
||||
source_x, source_y = img.size
|
||||
diff_x = int(source_x - min(source_x, self.width))
|
||||
diff_y = int(source_y - min(source_y, self.height))
|
||||
left = top = 0
|
||||
right, bottom = source_x, source_y
|
||||
|
||||
while diff_x:
|
||||
slice = min(diff_x, max(diff_x // 5, 10))
|
||||
start = img.crop((left, 0, left + slice, source_y))
|
||||
end = img.crop((right - slice, 0, right, source_y))
|
||||
add, remove = self.compare_entropy(start, end, slice, diff_x)
|
||||
left += add
|
||||
right -= remove
|
||||
diff_x = diff_x - add - remove
|
||||
|
||||
while diff_y:
|
||||
slice = min(diff_y, max(diff_y // 5, 10))
|
||||
start = img.crop((0, top, source_x, top + slice))
|
||||
end = img.crop((0, bottom - slice, source_x, bottom))
|
||||
add, remove = self.compare_entropy(start, end, slice, diff_y)
|
||||
top += add
|
||||
bottom -= remove
|
||||
diff_y = diff_y - add - remove
|
||||
|
||||
box = (left, top, right, bottom)
|
||||
img = img.crop(box)
|
||||
|
||||
return img
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import math
|
||||
|
||||
from imagekit.lib import Image
|
||||
from .crop import SmartCrop as _SmartCrop
|
||||
import warnings
|
||||
|
||||
|
||||
class Crop(object):
|
||||
|
|
@ -118,84 +118,9 @@ class Fit(object):
|
|||
return img
|
||||
|
||||
|
||||
def histogram_entropy(im):
|
||||
"""
|
||||
Calculate the entropy of an images' histogram. Used for "smart cropping" in easy-thumbnails;
|
||||
see: https://raw.github.com/SmileyChris/easy-thumbnails/master/easy_thumbnails/utils.py
|
||||
|
||||
"""
|
||||
if not isinstance(im, Image.Image):
|
||||
return 0 # Fall back to a constant entropy.
|
||||
|
||||
histogram = im.histogram()
|
||||
hist_ceil = float(sum(histogram))
|
||||
histonorm = [histocol / hist_ceil for histocol in histogram]
|
||||
|
||||
return -sum([p * math.log(p, 2) for p in histonorm if p != 0])
|
||||
|
||||
|
||||
class SmartCrop(object):
|
||||
"""
|
||||
Crop an image 'smartly' -- based on smart crop implementation from easy-thumbnails:
|
||||
|
||||
https://github.com/SmileyChris/easy-thumbnails/blob/master/easy_thumbnails/processors.py#L193
|
||||
|
||||
Smart cropping whittles away the parts of the image with the least entropy.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, width=None, height=None):
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def compare_entropy(self, start_slice, end_slice, slice, difference):
|
||||
"""
|
||||
Calculate the entropy of two slices (from the start and end of an axis),
|
||||
returning a tuple containing the amount that should be added to the start
|
||||
and removed from the end of the axis.
|
||||
|
||||
"""
|
||||
start_entropy = histogram_entropy(start_slice)
|
||||
end_entropy = histogram_entropy(end_slice)
|
||||
|
||||
if end_entropy and abs(start_entropy / end_entropy - 1) < 0.01:
|
||||
# Less than 1% difference, remove from both sides.
|
||||
if difference >= slice * 2:
|
||||
return slice, slice
|
||||
half_slice = slice // 2
|
||||
return half_slice, slice - half_slice
|
||||
|
||||
if start_entropy > end_entropy:
|
||||
return 0, slice
|
||||
else:
|
||||
return slice, 0
|
||||
|
||||
def process(self, img):
|
||||
source_x, source_y = img.size
|
||||
diff_x = int(source_x - min(source_x, self.width))
|
||||
diff_y = int(source_y - min(source_y, self.height))
|
||||
left = top = 0
|
||||
right, bottom = source_x, source_y
|
||||
|
||||
while diff_x:
|
||||
slice = min(diff_x, max(diff_x // 5, 10))
|
||||
start = img.crop((left, 0, left + slice, source_y))
|
||||
end = img.crop((right - slice, 0, right, source_y))
|
||||
add, remove = self.compare_entropy(start, end, slice, diff_x)
|
||||
left += add
|
||||
right -= remove
|
||||
diff_x = diff_x - add - remove
|
||||
|
||||
while diff_y:
|
||||
slice = min(diff_y, max(diff_y // 5, 10))
|
||||
start = img.crop((0, top, source_x, top + slice))
|
||||
end = img.crop((0, bottom - slice, source_x, bottom))
|
||||
add, remove = self.compare_entropy(start, end, slice, diff_y)
|
||||
top += add
|
||||
bottom -= remove
|
||||
diff_y = diff_y - add - remove
|
||||
|
||||
box = (left, top, right, bottom)
|
||||
img = img.crop(box)
|
||||
|
||||
return img
|
||||
class SmartCrop(_SmartCrop):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn('The SmartCrop processor has been moved to'
|
||||
' `imagekit.processors.crop.SmartCrop`, where it belongs.',
|
||||
DeprecationWarning)
|
||||
super(SmartCrop, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
18
imagekit/processors/utils.py
Normal file
18
imagekit/processors/utils.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import math
|
||||
from imagekit.lib import Image
|
||||
|
||||
|
||||
def histogram_entropy(im):
|
||||
"""
|
||||
Calculate the entropy of an images' histogram. Used for "smart cropping" in easy-thumbnails;
|
||||
see: https://raw.github.com/SmileyChris/easy-thumbnails/master/easy_thumbnails/utils.py
|
||||
|
||||
"""
|
||||
if not isinstance(im, Image.Image):
|
||||
return 0 # Fall back to a constant entropy.
|
||||
|
||||
histogram = im.histogram()
|
||||
hist_ceil = float(sum(histogram))
|
||||
histonorm = [histocol / hist_ceil for histocol in histogram]
|
||||
|
||||
return -sum([p * math.log(p, 2) for p in histonorm if p != 0])
|
||||
|
|
@ -11,7 +11,8 @@ from imagekit import utils
|
|||
from imagekit.lib import Image
|
||||
from imagekit.models import ImageSpec
|
||||
from imagekit.processors import Adjust
|
||||
from imagekit.processors.resize import Crop, SmartCrop
|
||||
from imagekit.processors.resize import Crop
|
||||
from imagekit.processors.crop import SmartCrop
|
||||
|
||||
|
||||
class Photo(models.Model):
|
||||
|
|
|
|||
Loading…
Reference in a new issue