def addWebEntityImage(self, image_url): utils.log("downloading '%s'" % image_url) image = utils.getWebImage(image_url) suffix = os.path.basename(image_url) filename = "entities/%s" % suffix return self.addImage(filename, image)
def addResizedProfileImages(self, screen_name, image_url): # Filename is lowercase screen name prefix = 'users/%s' % screen_name.lower() # Retry getting web image multiple times in case of bad connection num_retries = 0 max_retries = 5 while True: try: image = utils.getWebImage(image_url, "profile") break except urllib2.HTTPError as e: logs.warning('Get web image exception: %s' % e) num_retries += 1 if num_retries > max_retries: msg = "Unable to connect to get web image after %d retries (%s)" % (max_retries, image_url) logs.warning(msg) raise logs.info("Retrying (%s)" % (num_retries)) time.sleep(30) if image.format not in ('JPEG', 'PNG'): logs.warning("Cannot add a profile image of format '%s', only JPEG or PNG") return sizes = self.profileImageSizes max_size = self.profileImageMaxSize def cropImageToSquare(image): width, height = image.size if width != height: # Extract a square aspect ratio image by cropping the longer side diff = abs(height - width) / 2 if width > height: box = (diff, 0, width - diff, height) else: box = (0, diff, width, height - diff) square = image.crop(box) else: # image is already square square = image return square image = cropImageToSquare(image) self._addImageSizes(prefix, image, max_size, sizes, original_url=image_url)
def generate_from_user(self, user, entities): cluster = self.get_clusters(entities) #, limit=max(10, int(.8 * len(entities)))) markers = '%7C'.join("%s,%s" % pt for pt in cluster['data']) images = [] API_KEY = "AIzaSyAxgU3LPU-m5PI7Jh7YTYYKAz6lV6bz2ok" bounds = "" # TODO: test specific center + zoom depending on #markers if len(cluster['data']) > 4: bounds = "center=%s,%s&zoom=%d&" % (cluster['avg'][0], cluster['avg'][1], 14) for size in self._sizes: map_url = "https://maps.googleapis.com/maps/api/staticmap?sensor=false&scale=1&format=jpg&maptype=roadmap&size=%dx%d&%smarkers=%s&key=%s" % (size[0], size[1], bounds, markers, API_KEY) image = utils.getWebImage(map_url) image = self._apply_postprocessing(image, user) images.append(image) return images
def createInstagramImage(self, entity_img_url, user_generated, coordinates, primary_color, secondary_color, user_name, category, types, title, subtitle): def dropShadow(image, rounded, offset=(5,5), background=0xffffff, shadow=0x444444, border=8, iterations=3): """ Add a gaussian blur drop shadow to an image. image - The image to overlay on top of the shadow. offset - Offset of the shadow from the image as an (x,y) tuple. Can be positive or negative. background - Background colour behind the image. shadow - Shadow colour (darkness). border - Width of the border around the image. This must be wide enough to account for the blurring of the shadow. iterations - Number of times to apply the filter. More iterations produce a more blurred shadow, but increase processing time. """ # Create the backdrop image -- a box in the background colour with a # shadow on it. totalWidth = image.size[0] + abs(offset[0]) + 2*border totalHeight = image.size[1] + abs(offset[1]) + 2*border back = Image.new('RGB', (totalWidth, totalHeight), background) if rounded: roundedsquare = Image.open(self.__basepath + 'roundedsquare.png') roundedsquare = roundedsquare.resize((totalWidth, totalHeight), Image.ANTIALIAS) roundedsquare.paste(back, (0, 0), roundedsquare) back = roundedsquare # Place the shadow, taking into account the offset from the image shadowLeft = border + max(offset[0], 0) shadowTop = border + max(offset[1], 0) back.paste(shadow, (shadowLeft, shadowTop, shadowLeft + image.size[0], shadowTop + image.size[1]) ) # Apply the filter to blur the edges of the shadow. Since a small kernel # is used, the filter must be applied repeatedly to get a decent blur. n = 0 while n < iterations: back = back.filter(ImageFilter.BLUR) n += 1 # Paste the input image onto the shadow backdrop imageLeft = border - min(offset[0], 0) imageTop = border - min(offset[1], 0) #back.paste(image, (imageLeft, imageTop)) return back def fitImg(entityImg, width, height): aspect_ratio = width/float(height) w, h = entityImg.size print("aspect ratio: %s w/h:%s" % (aspect_ratio, w/float(h))) print( w/float(h)) if w/float(h) >= aspect_ratio: new_height = int( h / (w/float(width))) print ('width: %s new_height: %s' % (width, new_height)) return entityImg.resize((width, new_height), Image.ANTIALIAS) else: new_width = int( w / (h/float(height))) print ('new_width: %s height: %s' % (new_width, height)) return entityImg.resize((new_width, height), Image.ANTIALIAS) def transformEntityImage(entityImg, stampImg, rounded, pin, a,b,c,d,e,f,g,h, x, y): origAlbumSize = entityImg.size xResize = float(origAlbumSize[0])/600 entityImg = entityImg.resize((600, int(origAlbumSize[1]/xResize)), Image.ANTIALIAS) entityImgSize = entityImg.size # draw = ImageDraw.Draw(entityImg) # draw.line((entityImg.size[0]/2, 0, entityImg.size[0]/2, entityImg.size[1]), fill='#000000') # draw.line((0, entityImg.size[1]/2, entityImg.size[0], entityImg.size[1]/2), fill='#000000') if rounded: buf = Image.new('RGBA', entityImgSize, (255,255,255,0)) #buf2 = Image.new('RGBA', shadowSize, (255,255,255,0)) roundedsquare = Image.open(self.__basepath + 'roundedsquare.png') entityImg = entityImg.resize((600,600), Image.ANTIALIAS) #shadow = shadow.resize((600,600), Image.ANTIALIAS) #roundedEntitySquare = roundedsquare.resize(entityImgSize, Image.ANTIALIAS) #roundedShadowSquare = roundedsquare.resize(shadowSize, Image.ANTIALIAS) buf.paste(entityImg, (0,0), roundedsquare) #buf2.paste(shadow, (0,0), roundedsquare) entityImg = buf #shadow = buf2 shadow = dropShadow(entityImg, False, background=0xffffff, shadow=0xd0d0d0, offset=(0,0), border=20)#.show() shadowSize = shadow.size adjust = (((entityImgSize[1]/float(entityImgSize[0]))-1.0) * 0.2) + 1.0 a = (a*adjust)/2 b = (b*adjust)/2 d = (d*adjust)/2 e = (e*adjust)/2 g = (g*adjust)/2 h = (h*adjust)/2 mode = "RGBA" size = 612, 612 size2x = 612*2, 612*2 shadowsize2x = 660*2, 660*2 shadowOffset = x, y buf2 = Image.new(mode, size2x, (255,255,255,255)) mask = Image.new('1', entityImgSize, 1) shadowmask = Image.new('1', shadowSize, 1) stampImg = stampImg.transform(size2x, Image.PERSPECTIVE, (a,-2.30/2,c,1.006/2,e,f,g,h), Image.BICUBIC) entityImg = entityImg.transform(size2x, Image.PERSPECTIVE, (a,b,c,d,e,f,g,h), Image.BICUBIC) shadow = shadow.transform(shadowsize2x, Image.PERSPECTIVE,(a,b,c,d,e,f,g,h), Image.BICUBIC) mask = mask.transform(size2x, Image.PERSPECTIVE, (a,b,c,d,e,f,g,h), Image.BICUBIC) shadowmask = shadowmask.transform(shadowsize2x, Image.PERSPECTIVE, (a,b,c,d,e,f,g,h), Image.BICUBIC) stampImg = stampImg.filter(ImageFilter.GaussianBlur) stampImg = stampImg.resize((1000,1000), Image.ANTIALIAS) if not rounded: buf2.paste(shadow, shadowOffset, shadowmask) buf2.paste(entityImg, (0, 0), mask) buf2.paste(stampImg, (412,-100), stampImg) entityImg = buf2.resize(size, Image.ANTIALIAS) # if pin: # pinImg = Image.open(self.__basepath + 'pin.png') # entityImg.paste(pinImg, (278,200), pinImg) return entityImg def getCategoryIcon(category): categoryIcons = Image.open(self.__basepath + 'categoryicons18px.png') #icon = Image.new('RGBA', (18,18), (255,255,255,0)) categories = ('restaurant', 'bar', 'cafe', 'place', 'album', 'song', 'artist', 'film', 'tv_show', 'book', 'software', 'other') if category not in categories: categoryIndex = len(categories)-1 else: categoryIndex = categories.index(category) print categoryIndex icon = ImageChops.offset(categoryIcons, -20*categoryIndex, 20) icon = icon.crop((0, 0, 18, 18)) #icon.paste(categoryIcons, (20*categoryIndex, 20, (20*categoryIndex)+18, 38)) return icon def getInstagramTextImg(user_name, category, types, title, subtitle): textImg = Image.new('RGBA', (612*2,195*2), (255,255,255,255)) draw = ImageDraw.Draw(textImg) titling_gothic = ImageFont.truetype(self.__basepath +"TitlingGothicFB.ttf", 80*2) helvetica_neue_bold = ImageFont.truetype(self.__basepath +"HelveticaNeue-Bold.ttf", 24*2) helvetica_neue = ImageFont.truetype(self.__basepath + "HelveticaNeue.ttf", 24*2) header_nameW, header_nameH = draw.textsize(user_name, font=helvetica_neue_bold) prefix = 'a' if len(set(types).intersection(set(['establishment', 'app', 'album', 'artist', 'app']))) > 0: prefix = 'an' type = types[0].replace('_', ' ') if type == 'tv': type = 'TV show' header = ' stamped %s %s' % (prefix, type) headerW, headerH = draw.textsize(header, font=helvetica_neue) # truncate title text if necessary. Allow ten pixels of padding on each side and append ellipsis final_title = title while True: titleW, titleH = draw.textsize(final_title, font=titling_gothic) if titleW > (612-20)*2: final_title = final_title[:-2] + u"\u2026" continue break subtitleW, subtitleH = draw.textsize(subtitle, font=helvetica_neue) draw.text((612-((headerW+header_nameW)/2),0), user_name, font=helvetica_neue_bold, fill='#939393') draw.text((612-((headerW+header_nameW)/2)+header_nameW,0), header, font=helvetica_neue, fill='#939393') draw.text((612-(titleW/2),40*2), final_title, font=titling_gothic, fill='#000000') draw.line((165*2, 134*2, (612-165)*2, 134*2), fill='#e0e0e0') draw.text(((612*2/2)-(subtitleW/2),(134+20)*2), subtitle, font=helvetica_neue, fill='#939393') del draw textImg = textImg.resize((612, 195), Image.ANTIALIAS) icon = getCategoryIcon(category) textImg.paste(icon, ((612/2)-(18/2), 124)) return textImg masks = [ (270, 270, self.__basepath + 'stamp_mask.png'), (612, 9, self.__basepath + 'ribbon-top.png'), (612, 9, self.__basepath + 'ribbon-bottom.png'), ] gradientImgs = [self.__imageDB.generate_gradient_images(primary_color, secondary_color, x[0], x[1], x[2]) for x in masks] stamp = gradientImgs[0] ribbon_top = gradientImgs[1] ribbon_bot = gradientImgs[2] shadow_top = Image.open(self.__basepath + 'shadow-top.png') shadow_bot = shadow_top.transpose(Image.FLIP_TOP_BOTTOM) a = 1.77 b = -1.61 c = 298 d = 1.074 e = 4.11 f = -820 g = -0.0001 h = 0.00215 x = -44 y = 1 size = 612,612 img = Image.new('RGBA', size, (255,255,255,255)) if coordinates is not None and user_generated == False: entity_img_url = "https://maps.googleapis.com/maps/api/staticmap?center=%s&zoom=18&sensor=false&scale=1&format=png&maptype=roadmap&size=600x600&key=AIzaSyAEjlMEfxmlCBQeyw_82jjobQAFjYx-Las" % coordinates if entity_img_url is not None: #entityImg = Image.open(entity_img_url) entityImg = utils.getWebImage(entity_img_url) if user_generated: boxW = 612 boxH = 612-195-40-30 entityImg = fitImg(entityImg, boxW, boxH) w, h = entityImg.size offsetX = (boxW - w)/2 offsetY = (boxH - h)/2 + 195+40 img.paste(entityImg, (offsetX, offsetY)) else: entityImg = transformEntityImage(entityImg, stamp, 'app' in types, coordinates is not None, a,b,c,d,e,f,g,h,x,y) img.paste(entityImg, (7,166)) else: pass textImg = getInstagramTextImg(user_name, category, types, title, subtitle) img.paste(textImg, (0, 40), textImg) img.paste(ribbon_top, (0, 0)) img.paste(shadow_top, (0, ribbon_top.size[1])) img.paste(ribbon_bot, (0, 612-ribbon_bot.size[1])) img.paste(shadow_bot, (0, 612-ribbon_top.size[1]-shadow_bot.size[1]), shadow_bot) return img
def addResizedStampImages(self, sourceUrl, imageId, maxSize, sizes): image = utils.getWebImage(sourceUrl, "stamp") prefix = 'stamps/%s' % imageId return self._addImageSizes(prefix, image, maxSize, sizes, original_url=sourceUrl)
def _create_collage( self, user, images, num_rows=None, num_cols=None, respect_aspect_ratio=False, adaptive_image_resizing=True, enable_drop_shadows=False, row_major=True, shuffle_images=False, ): # must specify num_cols or num_rows, but not both assert (num_cols is not None and num_cols > 0) != (num_rows is not None and num_rows > 0) num_images = len(images) output = [] if num_rows is None: num_cols = int(num_cols) num_rows = int(math.ceil(num_images / num_cols)) elif num_cols is None: num_rows = int(num_rows) num_cols = int(math.ceil(num_images / num_rows)) user_logo_url = "http://static.stamped.com/logos/%s-%s-email-36x36.png" % ( user.color_primary, user.color_secondary, ) try: user_logo = utils.getWebImage(user_logo_url) except Exception: user_logo = None user_logo_cache = {} def get_user_logo(size): if user_logo is None: return None try: return user_logo_cache[size] except KeyError: logo = user_logo.resize(size, Image.ANTIALIAS) user_logo_cache[size] = logo return logo for size in self._sizes: logs.info("[%s] creating %sx%s collage" % (self, size[0], size[1])) canvas = Image.new("RGBA", size, (255, 255, 255, 255)) offsets = [] indices = [] if row_major: for i in xrange(num_rows): for j in xrange(num_cols): indices.append(len(offsets)) offsets.append((i, j)) else: for j in xrange(num_cols): for i in xrange(num_rows): indices.append(len(offsets)) offsets.append((i, j)) if shuffle_images: indices = utils.shuffle(indices) for index in indices: i, j = offsets[index] # wrap images around if necessary to fill last row index = (i * num_cols + j) % num_images image = images[index] cell_size, cell_pos, logo_size, logo_pos = self.get_cell_bounds_func( size, num_cols, num_rows, i, j, image ) # adjust cell layout bounds to align to integer grid (helps minimize aliasing) cell_size = int(math.ceil(cell_size[0])), int(math.ceil(cell_size[1])) cell_pos = int(math.floor(cell_pos[0])), int(math.floor(cell_pos[1])) logo_size = int(math.ceil(logo_size[0])), int(math.ceil(logo_size[1])) logo_pos = int(math.floor(logo_pos[0])), int(math.floor(logo_pos[1])) width = cell_size[0] height = cell_size[1] if adaptive_image_resizing: if image.size[0] / cell_size[0] < image.size[1] / cell_size[1]: width = cell_size[0] height = (width * image.size[1]) / image.size[0] if not respect_aspect_ratio and height > cell_size[1]: height = int((height + cell_size[1]) * 0.5) else: height = cell_size[1] width = (height * image.size[0]) / image.size[1] if not respect_aspect_ratio and width > cell_size[0]: width = int((width + cell_size[0]) * 0.5) cell = image.resize((width, height), Image.ANTIALIAS) w = min(width, cell_size[0]) h = min(height, cell_size[1]) cell = cell.crop((0, 0, w, h)) if enable_drop_shadows: self._paste_image_with_drop_shadow(canvas, cell, cell_pos) else: canvas.paste(cell, cell_pos) # overlay user's stamp logo on top of each entity image logo = get_user_logo(logo_size) if logo is not None: logo_box = (logo_pos[0], logo_pos[1], logo_pos[0] + logo.size[0], logo_pos[1] + logo.size[1]) canvas.paste(logo, logo_box, logo) canvas = self._apply_postprocessing(canvas, user) output.append(canvas) return output
def _get_image(self, image_url): logs.info("downloading '%s'" % image_url) return utils.getWebImage(image_url, "collage")