Source code for wheezy.captcha.image

"""
"""

import random

from wheezy.captcha.comp import Draw, Image, ImageFilter, getrgb, truetype


[docs]def captcha(drawings, width=200, height=75): def render(text): image = Image.new("RGB", (width, height), (255, 255, 255)) for drawing in drawings: image = drawing(image, text) assert image return image return render
# region: captcha drawers
[docs]def background(color="#EEEECC"): color = getrgb(color) def drawer(image, text): Draw(image).rectangle([(0, 0), image.size], fill=color) return image return drawer
[docs]def smooth(): def drawer(image, text): return image.filter(ImageFilter.SMOOTH) return drawer
[docs]def curve(color="#5C87B2", width=4, number=6): from wheezy.captcha.bezier import make_bezier if not callable(color): c = getrgb(color) def color(): return c def drawer(image, text): dx, height = image.size dx = dx / number path = [(dx * i, random.randint(0, height)) for i in range(1, number)] bcoefs = make_bezier(number - 1) points = [] for coefs in bcoefs: points.append( tuple( sum([coef * p for coef, p in zip(coefs, ps)]) for ps in zip(*path) ) ) draw = Draw(image) draw.line(points, fill=color(), width=width) return image return drawer
[docs]def noise(number=50, color="#EEEECC", level=2): if not callable(color): c = getrgb(color) def color(): return c def drawer(image, text): width, height = image.size dx = width / 10 width = width - dx dy = height / 10 height = height - dy draw = Draw(image) for _ in range(number): x = int(random.uniform(dx, width)) y = int(random.uniform(dy, height)) draw.line(((x, y), (x + level, y)), fill=color(), width=level) return image return drawer
[docs]def text( fonts, font_sizes=None, drawings=None, color="#5C87B2", squeeze_factor=0.8 ): fonts = tuple( [ truetype(name, size) for name in fonts for size in font_sizes or (65, 70, 75) ] ) if not callable(color): c = getrgb(color) def color(): return c def drawer(image, text): draw = Draw(image) char_images = [] for c in text: font = random.choice(fonts) c_width, c_height = draw.textsize(c, font=font) char_image = Image.new("RGB", (c_width, c_height), (0, 0, 0)) char_draw = Draw(char_image) char_draw.text((0, 0), c, font=font, fill=color()) char_image = char_image.crop(char_image.getbbox()) for drawing in drawings: char_image = drawing(char_image) char_images.append(char_image) width, height = image.size offset = int( ( width - sum( int(i.size[0] * squeeze_factor) for i in char_images[:-1] ) - char_images[-1].size[0] ) / 2 ) for char_image in char_images: c_width, c_height = char_image.size mask = char_image.convert("L").point(lambda i: i * 1.97) image.paste( char_image, (offset, int((height - c_height) / 2)), mask ) offset += int(c_width * squeeze_factor) return image return drawer
# region: text drawers
[docs]def warp(dx_factor=0.27, dy_factor=0.21): def drawer(image): width, height = image.size dx = width * dx_factor dy = height * dy_factor x1 = int(random.uniform(-dx, dx)) y1 = int(random.uniform(-dy, dy)) x2 = int(random.uniform(-dx, dx)) y2 = int(random.uniform(-dy, dy)) image2 = Image.new( "RGB", (width + abs(x1) + abs(x2), height + abs(y1) + abs(y2)) ) image2.paste(image, (abs(x1), abs(y1))) width2, height2 = image2.size return image2.transform( (width, height), Image.QUAD, ( x1, y1, -x1, height2 - y2, width2 + x2, height2 + y2, width2 - x2, -y1, ), ) return drawer
[docs]def offset(dx_factor=0.1, dy_factor=0.2): def drawer(image): width, height = image.size dx = int(random.random() * width * dx_factor) dy = int(random.random() * height * dy_factor) image2 = Image.new("RGB", (width + dx, height + dy)) image2.paste(image, (dx, dy)) return image2 return drawer
[docs]def rotate(angle=25): def drawer(image): return image.rotate( random.uniform(-angle, angle), Image.BILINEAR, expand=1 ) return drawer
if __name__ == "__main__": import string captcha_image = captcha( drawings=[ background(), text( fonts=[ "fonts/CourierNew-Bold.ttf", "fonts/LiberationMono-Bold.ttf", ], drawings=[warp(), rotate(), offset()], ), curve(), noise(), smooth(), ] ) image = captcha_image(random.sample(string.uppercase + string.digits, 4)) image.save("sample.jpg", "JPEG", quality=75)