def put_highlight(image, highlight, resample_highlight, opacity, cache=None): if cache is None: cache = {} resample_highlight = getattr(Image, resample_highlight) id = 'highlight_%s_w%d_h%d_o%d'\ % (highlight, image.size[0], image.size[1], opacity) try: highlight = cache[id] except KeyError: highlight = open_image(highlight)\ .convert('RGBA').resize(image.size, resample_highlight) if opacity < 100: #apply opacity highlight_alpha = imtools.get_alpha(highlight) opacity = (255 * opacity) / 100 highlight.putalpha(ImageMath.eval("convert((a * o) / 255, 'L')", a=highlight_alpha, o=opacity)) #store in cache cache[id] = highlight if not has_transparency(image): image = image.convert('RGBA') else: if has_transparency(image): image = image.convert('RGBA') alpha = imtools.get_alpha(image) highlight = highlight.copy() highlight_alpha = imtools.get_alpha(highlight) highlight.putalpha(ImageMath.eval("convert(min(a, b), 'L')", a=alpha, b=highlight_alpha)) overlay = highlight.convert('RGB') paste(image, overlay, mask=highlight) return image
def autocontrast(image, amount=100.0, cutoff=0): """Apply a filter - amount: 0-1 - repeat: how many times it should be repeated""" image = imtools.convert_safe_mode(image) if imtools.has_transparency(image): im = imtools.remove_alpha(image) else: im = image contrasted = ImageOps.autocontrast(im, cutoff) if imtools.has_transparency(image): imtools.put_alpha(contrasted, imtools.get_alpha(image)) if amount < 100: return imtools.blend(image, contrasted, amount / 100.0) return contrasted
def put_contour(image, size=1, offset=0, contour_color=0, fill_color=0, opacity=100, include_image=True): if not has_transparency(image): return put_border(image, size, offset, contour_color, fill_color, opacity, include_image) image = image.convert('RGBA') mask = imtools.get_alpha(image) w, h = image.size outer_mask = mask.resize( (w + 2 * (size + offset), h + 2 * (size + offset)), Image.ANTIALIAS) inner_mask = mask.resize((w + 2 * offset, h + 2 * offset), Image.ANTIALIAS) inner_mask = ImageOps.expand(inner_mask, border=size, fill=0) paste(outer_mask, (255 * opacity) / 100, mask=inner_mask) if include_image: image = ImageOps.expand(image, border=size + offset, fill=(0, 0, 0, 0)) mask = ImageOps.expand(mask, border=size + offset, fill=0) paste(outer_mask, 255, mask=mask) contour = ImageOps.colorize(outer_mask, (255, 255, 255), contour_color) paste(contour, fill_color, mask=inner_mask) if include_image: paste(contour, image, mask=image) contour.putalpha(outer_mask) return contour
def warmup(image, midtone, brighten, amount=100): """Apply a toning filter. Move the midtones to the desired color while preserving blacks and whites with optional mixing with original image - amount: 0-100%""" mode = image.mode info = image.info if image.mode != "L": im = imtools.convert(image, "L") else: im = image if image.mode != "RGBA" and imtools.has_transparency(image): image = imtools.convert(image, "RGBA") luma = imtools.convert(imtools.split(im)[0], "F") o = [] m = ImageColor.getrgb(midtone) b = brighten / 600.0 # Calculate channels separately for l in range(3): o.append(ImageMath.eval("m*(255-i)*i+i", i=luma, m=4 * ((m[l] / 255.0) - 0.5 + b) / 255.0).convert("L")) colorized = Image.merge("RGB", tuple(o)) if imtools.has_alpha(image): imtools.put_alpha(colorized, imtools.get_alpha(image)) if amount < 100: colorized = imtools.blend(image, colorized, amount / 100.0) return colorized
def put_contour(image, size=1, offset=0, contour_color=0, fill_color=0, opacity=100, include_image=True): if not has_transparency(image): return put_border( image, size, offset, contour_color, fill_color, opacity, include_image) image = image.convert('RGBA') mask = imtools.get_alpha(image) w, h = image.size outer_mask = mask.resize( (w + 2 * (size + offset), h + 2 * (size + offset)), Image.ANTIALIAS) inner_mask = mask.resize( (w + 2 * offset, h + 2 * offset), Image.ANTIALIAS) inner_mask = ImageOps.expand(inner_mask, border=size, fill=0) paste(outer_mask, (255 * opacity) / 100, mask=inner_mask) if include_image: image = ImageOps.expand(image, border=size + offset, fill=(0, 0, 0, 0)) mask = ImageOps.expand(mask, border=size + offset, fill=0) paste(outer_mask, 255, mask=mask) contour = ImageOps.colorize(outer_mask, (255, 255, 255), contour_color) paste(contour, fill_color, mask=inner_mask) if include_image: paste(contour, image, mask=image) contour.putalpha(outer_mask) return contour
def mosaic(image, fill_type, fill_image, fill_folder, columns, rows, canvas_width, canvas_height): if has_transparency(image): image = image.convert('RGBA') else: image = image.convert('RGB') return _tamogen.mosaic(image, fill_type, columns, rows, canvas_width, canvas_height, fill_image, fill_folder)
def put_mask(image, mask, resample_mask, cache=None): if cache is None: cache = {} resample_mask = getattr(Image, resample_mask) id = mask + "_w%d_h%d" % image.size try: mask = cache[id] except KeyError: mask = cache[id] = open_image(mask).convert("L").resize(image.size, resample_mask) if not has_transparency(image): image = image.convert("RGBA") else: if has_transparency(image): image = image.convert("RGBA") alpha = imtools.get_alpha(image) mask = ImageMath.eval("convert(min(a, b), 'L')", a=alpha, b=mask) image.putalpha(mask) return image
def border(image, method, border_width=0, left=0, right=0, top=0, bottom=0, color=0, opacity=100): """ """ #set up sizes, and make the target img if method == OPTIONS[0]: left, right, top, bottom = (border_width, ) * 4 else: left, right, top, bottom = [x for x in left, right, top, bottom] #new image size attributes could get really messed up by negatives... new_width = sum([x for x in image.size[0], left, right if x >= 0]) new_height = sum([x for x in image.size[1], top, bottom if x >= 0]) # only need to do conversions when preserving transparency, or when # dealing with transparent overlays negative = [x for x in left, right, top, bottom if x < 0] if (negative and (opacity < 100)) or has_transparency(image): new_image = Image.new('RGBA', (new_width, new_height), color) else: new_image = Image.new('RGB', (new_width, new_height), color) # now for the masking component. The size of the mask needs to be the size # of the original image, and totally opaque. then we will have draw in # negative border values with an opacity scaled appropriately. # NOTE: the technique here is that rotating the image allows me to do # this with one simple draw operation, no need to add and subtract and # otherwise introduce geometry errors if negative: #draw transparent overlays mask = Image.new('L', image.size, 255) drawcolor = int(255 - (opacity / 100.0 * 255)) for val in left, top, right, bottom: if val < 0: mask_draw = ImageDraw.Draw(mask) mask_draw.rectangle((0, 0, abs(val), max(mask.size)), drawcolor) del mask_draw mask = mask.rotate(90) else: mask = None # negative paste position values mess with the result. left = max(left, 0) top = max(top, 0) paste(new_image, image, (left, top), mask) return new_image
def put_mask(image, mask, resample_mask, cache=None): if cache is None: cache = {} resample_mask = getattr(Image, resample_mask) id = mask + '_w%d_h%d' % image.size try: mask = cache[id] except KeyError: mask = cache[id] = open_image(mask).convert('L').resize( image.size, resample_mask) if not has_transparency(image): image = image.convert('RGBA') else: if has_transparency(image): image = image.convert('RGBA') alpha = imtools.get_alpha(image) mask = (ImageMath.eval("convert(min(a, b), 'L')", a=alpha, b=mask)) image.putalpha(mask) return image
def reflect(image, depth, opacity, background_color, background_opacity, scale_method, gap=0, scale_reflection=False, blur_reflection=False, cache=None): if has_transparency(image): image = image.convert('RGBA') else: image = image.convert('RGB') if cache is None: cache = {} opacity = (255 * opacity) / 100 background_opacity = (255 * background_opacity) / 100 scale_method = getattr(Image, scale_method) if background_opacity == 255: mode = 'RGB' color = background_color else: mode = 'RGBA' color = HTMLColorToRGBA(background_color, background_opacity) width, height = image.size depth = min(height, depth) #make reflection if has_alpha(image) and background_opacity > 0: reflection = Image.new(mode, image.size, color) paste(reflection, image, (0, 0), image) else: reflection = image reflection = reflection.transpose(Image.FLIP_TOP_BOTTOM) if scale_reflection: reflection = reflection.resize((width, depth), scale_method) else: reflection = reflection.crop((0, 0, width, depth)) if blur_reflection: reflection = reflection.filter(ImageFilter.BLUR) mask = gradient_mask((width, depth), opacity, cache) #composite total_size = (width, height + gap + depth) total = Image.new(mode, total_size, color) paste(total, image, (0, 0), image) paste(total, reflection, (0, height + gap), mask) return total
def background(image, fill, mark, color, horizontal_offset=None, vertical_offset=None, horizontal_justification=None, vertical_justification=None, orientation=None, method=None, opacity=100): if not has_transparency(image): return image if image.mode == 'P': image = image.convert('RGBA') if fill == FILL_CHOICES[0]: opacity = (255 * opacity) / 100 return fill_background_color(image, HTMLColorToRGBA(color, opacity)) elif fill == FILL_CHOICES[1]: layer = generate_layer(image.size, mark, method, horizontal_offset, vertical_offset, horizontal_justification, vertical_justification, orientation, opacity) paste(layer, image, mask=image) return layer
def warmup(image, midtone, brighten, amount=100): """Apply a toning filter. Move the midtones to the desired color while preserving blacks and whites with optional mixing with original image - amount: 0-100%""" mode = image.mode info = image.info if image.mode != 'L': im = imtools.convert(image, 'L') else: im = image if image.mode != 'RGBA' and imtools.has_transparency(image): image = imtools.convert(image, 'RGBA') luma = imtools.convert(imtools.split(im)[0], 'F') o = [] m = ImageColor.getrgb(midtone) b = brighten / 600.0 # Calculate channels separately for l in range(3): o.append( ImageMath.eval("m*(255-i)*i+i", i=luma, m=4 * ((m[l] / 255.0) - 0.5 + b) / 255.0).convert('L')) colorized = Image.merge('RGB', tuple(o)) if imtools.has_alpha(image): imtools.put_alpha(colorized, imtools.get_alpha(image)) if amount < 100: colorized = imtools.blend(image, colorized, amount / 100.0) return colorized
def border(image, method, border_width=0, left=0, right=0, top=0, bottom=0, color=0, opacity=100): """ """ #set up sizes, and make the target img if method == OPTIONS[0]: left, right, top, bottom = (border_width, ) * 4 else: left, right, top, bottom = [x for x in left, right, top, bottom] #new image size attributes could get really messed up by negatives... new_width = sum([x for x in image.size[0], left, right if x >= 0]) new_height = sum([x for x in image.size[1], top, bottom if x >= 0]) # only need to do conversions when preserving transparency, or when # dealing with transparent overlays negative = [x for x in left, right, top, bottom if x < 0] if (negative and (opacity < 100)) or has_transparency(image): new_image = Image.new('RGBA', (new_width, new_height), color) else: new_image = Image.new('RGB', (new_width, new_height), color) # now for the masking component. The size of the mask needs to be the size # of the original image, and totally opaque. then we will have draw in # negative border values with an opacity scaled appropriately. # NOTE: the technique here is that rotating the image allows me to do # this with one simple draw operation, no need to add and subtract and # otherwise introduce geometry errors if negative: #draw transparent overlays mask = Image.new('L', image.size, 255) drawcolor = int(255 - (opacity / 100.0 * 255)) for val in left, top, right, bottom: if val < 0: mask_draw = ImageDraw.Draw(mask) mask_draw.rectangle((0, 0, abs(val), max(mask.size)), drawcolor) del mask_draw mask = mask.rotate(90) else: mask = None # negative paste position values mess with the result. left = max(left, 0) top = max(top, 0) paste(new_image, image, (left, top), mask) return new_image #---Phatch CHOICES = ['-25', '-10', '-5', '-1', '0', '1', '5', '10', '25'] class Action(models.Action): label = _t('Border') author = 'Erich' email = '*****@*****.**' init = staticmethod(init) pil = staticmethod(border) version = '0.2' tags = [_t('filter')] __doc__ = _t('Draw border inside or outside') def interface(self, fields): fields[_t('Method')] = self.ChoiceField(OPTIONS[0], choices=OPTIONS) fields[_t('Border Width')] = self.PixelField('1px', choices=CHOICES) fields[_t('Left')] = self.PixelField('0px', choices=CHOICES) fields[_t('Right')] = self.PixelField('0px', choices=CHOICES) fields[_t('Top')] = self.PixelField('0px', choices=CHOICES) fields[_t('Bottom')] = self.PixelField('0px', choices=CHOICES) fields[_t('Color')] = self.ColorField('#000000') fields[_t('Opacity')] = self.SliderField(100, 1, 100) def values(self, info): #pixel fields width, height = info['size'] # pass absolute reference for relative pixel values such as % return super(Action, self).values(info, pixel_fields={ 'Border Width': (width + height) / 2, 'Left': width, 'Right': width, 'Top': height, 'Bottom': height, }) def get_relevant_field_labels(self): """If this method is present, Phatch will only show relevant fields. :returns: list of the field labels which are relevant :rtype: list of strings .. note:: It is very important that the list of labels has EXACTLY the same order as defined in the interface method. """ relevant = ['Method', 'Color', 'Opacity'] if self.get_field_string('Method') == OPTIONS[0]: relevant.append('Border Width') else: relevant.extend(['Left', 'Right', 'Top', 'Bottom']) return relevant icon = \ 'x\xda\x01x\x0b\x87\xf4\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x000\x00\
def drop_shadow(image, horizontal_offset=5, vertical_offset=5, background_color=(255, 255, 255, 0), shadow_color=0x444444, border=8, shadow_blur=3, force_background_color=False, cache=None): """Add a gaussian blur drop shadow to an image. :param image: The image to overlay on top of the shadow. :param type: PIL Image :param offset: Offset of the shadow from the image as an (x,y) tuple. Can be positive or negative. :type offset: tuple of integers :param background_color: Background color behind the image. :param shadow_color: Shadow color (darkness). :param border: Width of the border around the image. This must be wide enough to account for the blurring of the shadow. :param shadow_blur: Number of times to apply the filter. More shadow_blur produce a more blurred shadow, but increase processing time. """ if cache is None: cache = {} if has_transparency(image) and image.mode != 'RGBA': # Make sure 'LA' and 'P' with trasparency are handled image = image.convert('RGBA') #get info size = image.size mode = image.mode back = None #assert image is RGBA if mode != 'RGBA': if mode != 'RGB': image = image.convert('RGB') mode = 'RGB' #create cache id id = ''.join([str(x) for x in ['shadow_', size, horizontal_offset, vertical_offset, border, shadow_blur, background_color, shadow_color]]) #look up in cache if id in cache: #retrieve from cache back, back_size = cache[id] if back is None: #size of backdrop back_size = (size[0] + abs(horizontal_offset) + 2 * border, size[1] + abs(vertical_offset) + 2 * border) #create shadow mask if mode == 'RGBA': image_mask = imtools.get_alpha(image) shadow = Image.new('L', back_size, 0) else: image_mask = Image.new(mode, size, shadow_color) shadow = Image.new(mode, back_size, background_color) shadow_left = border + max(horizontal_offset, 0) shadow_top = border + max(vertical_offset, 0) paste(shadow, image_mask, (shadow_left, shadow_top, shadow_left + size[0], shadow_top + size[1])) del image_mask # free up memory #blur shadow mask #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 < shadow_blur: shadow = shadow.filter(ImageFilter.BLUR) n += 1 #create back if mode == 'RGBA': back = Image.new('RGBA', back_size, shadow_color) back.putalpha(shadow) del shadow # free up memory else: back = shadow cache[id] = back, back_size #Paste the input image onto the shadow backdrop image_left = border - min(horizontal_offset, 0) image_top = border - min(vertical_offset, 0) if mode == 'RGBA': paste(back, image, (image_left, image_top), image) if force_background_color: mask = imtools.get_alpha(back) paste(back, Image.new('RGB', back.size, background_color), (0, 0), ImageChops.invert(mask)) back.putalpha(mask) else: paste(back, image, (image_left, image_top)) return back