Skip to content

Instantly share code, notes, and snippets.

@jperler
Created October 12, 2012 14:39
Show Gist options
  • Save jperler/3879523 to your computer and use it in GitHub Desktop.
Save jperler/3879523 to your computer and use it in GitHub Desktop.
PIL Wrapper
import socket
import urllib2, uuid, os
from PIL import Image, ImageChops, ImageOps
import simplejson as json
from settings import MEDIA_ROOT
from studio.misc import PhotoSize
"""
Dependencies: PIL
Settings: MEDIA_ROOT (directory where files are stored)
Following class is used in the class usage examples:
class PhotoSize:
Timeline=(940, 375)
Thumbnail=(195, 146)
CoverThumbnail = (275, 125)
Thumbnail84x84 = (84, 84)
Logo = (60, 60)
"""
class BaseImageProcessor:
"""
utility to resize images
"""
def __init__(self, base_dir, **kwargs):
self.base_dir = base_dir
self.id = uuid.uuid1()
self.quality = 90
self.extension = None
self.success = True
def save(self):
pass
def get_original(self):
return '%s.%s' % (self.id, self.extension)
def get_filename(self):
return '%s/%s.%s' % (self.base_dir, self.id, self.extension)
def get_extension(self, filename):
return filename.split('/')[-1:][0].split('.')[-1:][0]
def clean_up(self):
os.remove(self.get_filename())
def rescale(self, dimension, force=False):
nw = dimension[0]
nh = dimension[1]
padding = False
try:
img = Image.open(self.get_filename())
pw, ph = img.size
pr = float(pw) / float(ph)
nr = float(nw) / float(nh)
if force:
# this is mainly used for logos or images that shouldn't be cropped
img.thumbnail((nw, nh), Image.ANTIALIAS)
elif pr >= nr:
# photo aspect is wider than destination ratio
tw = int(round(nh * pr))
if pw < tw:
padding = True
else:
# reduce width
img = img.resize((tw, nh), Image.ANTIALIAS)
l = int(round(( tw - nw ) / 2.0))
img = img.crop((l, 0, l + nw, nh))
elif pr < nr:
# photo aspect is taller than destination ratio
th = int(round(nw / pr))
if ph < th:
padding = True
else:
# reduce height
img = img.resize((nw, th), Image.ANTIALIAS)
t = int(round(( th - nh ) / 2.0))
img = img.crop((0, t, nw, t + nh))
if padding:
# add black padding instead of stretching
img = img.crop( (0, 0, nw, nh) )
offset_x = max(int(round(( nw - pw ) / 2.0)), 0)
offset_y = max(int(round(( nh - ph ) / 2.0)), 0)
img = ImageChops.offset(img, offset_x, offset_y)
filename = '%s_%s_%s.%s' % (
self.id,
nw,
nh,
self.extension
)
img.save('%s/%s' % (self.base_dir, filename), 'JPEG', quality = self.quality)
return filename
except IOError:
self.success = False
def is_success(self):
return self.success
def get_json_response(self):
result = {'success': self.is_success(),
'original': self.get_original(),
'id': '%s' % self.id}
return json.dumps(result)
class URLImageProcessor(BaseImageProcessor):
"""
utility to download images from the web, save them
and resize them.
usage:
processor = URLImageProcessor(url='http://www.projectsherpa.com/static/img/companies/moat/logo.png')
if processor.save():
timeline = processor.rescale(PhotoSize.Timeline)
thumbnail = processor.rescale(PhotoSize.Thumbnail, True) # adding True here forces a thumbnail without doing a resize
...
"""
def __init__(self, base_dir=MEDIA_ROOT, **kwargs):
BaseImageProcessor.__init__(self, base_dir, **kwargs)
self.url = kwargs.pop('url')
self.extension = self.get_extension(self.url)
def save(self):
if '?' in self.url:
return False
try:
f = urllib2.urlopen(self.url)
contents = f.read()
local_file = open(self.get_filename(), 'wb')
local_file.write(contents)
local_file.close()
return True
except (urllib2.HTTPError, urllib2.URLError, socket.timeout, socket.error, ValueError) as e:
print 'Download Error', str(e)
return False
class FileImageProcessor(BaseImageProcessor):
"""
utility to resize image files
usage:
processor = FileImageProcessor(filename=filename)
# uploaded is of type HttpRequest in this example
# see: https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.read
processor.load(uploaded)
timeline = processor.rescale(PhotoSize.Timeline)
thumbnail = processor.rescale(PhotoSize.Thumbnail, True)
"""
def __init__(self, base_dir=MEDIA_ROOT, **kwargs):
BaseImageProcessor.__init__(self, base_dir, **kwargs)
self.filename = kwargs.pop('filename')
self.extension = self.get_extension(self.filename)
self.contents = None
def save(self):
try:
local_file = open(self.get_filename(), 'wb')
local_file.write(self.contents)
local_file.close()
return True
except IOError:
return False
def load(self, contents):
self.contents = contents.read()
self.save()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment