# Stolen from tgcaptcha/plugins # http://code.google.com/p/tgcaptcha/source/browse/trunk/tgcaptcha/plugins/image/vanasco_dowty/captcha.py import random from PIL import Image, ImageDraw, ImageFont, ImageFilter import io # from io import StringIO import math from pyramid.view import view_config from .words import TabMots from pyramid.response import Response class Captcha_Img(object): def __init__(self, width, height): self.width = width self.height = height self._layers = [ _PyCaptcha_SineWarp(amplitudeRange=(4, 8), periodRange=(0.65, 0.73)), ] def getImg(self): """Get a PIL image representing this CAPTCHA test, creating it if necessary""" if not self._image: self._image = self.render() return self._image def render(self): """Render this CAPTCHA, returning a PIL image""" size = (self.width, self.height) # img = Image.new("RGB", size ) img = self._image for layer in self._layers: img = layer.render(img) or img self._image = img return self._image class _PyCaptcha_WarpBase(object): """Abstract base class for image warping. Subclasses define a function that maps points in the output image to points in the input image. This warping engine runs a grid of points through this transform and uses PIL's mesh transform to warp the image. """ filtering = Image.BILINEAR resolution = 40 def get_transform(self, image): """Return a transformation function, subclasses should override this""" return lambda x, y: (x, y) def render(self, image): r = self.resolution xPoints = image.size[0] / r + 2 yPoints = image.size[1] / r + 2 f = self.get_transform(image) # Create a list of arrays with transformed points xRows = [] yRows = [] for j in range(int(yPoints)): xRow = [] yRow = [] for i in range(int(xPoints)): x, y = f(i * r, j * r) # Clamp the edges so we don't get black undefined areas x = max(0, min(image.size[0] - 1, x)) y = max(0, min(image.size[1] - 1, y)) xRow.append(x) yRow.append(y) xRows.append(xRow) yRows.append(yRow) # Create the mesh list, with a transformation for # each square between points on the grid mesh = [] for j in range(int(yPoints - 1)): for i in range(int(xPoints - 1)): mesh.append(( # Destination rectangle (i * r, j * r, (i + 1) * r, (j + 1) * r), # Source quadrilateral (xRows[j][i], yRows[j][i], xRows[j + 1][i], yRows[j + 1][i], xRows[j + 1][i + 1], yRows[j + 1][i + 1], xRows[j][i + 1], yRows[j][i + 1]), )) return image.transform(image.size, Image.MESH, mesh, self.filtering) class _PyCaptcha_SineWarp(_PyCaptcha_WarpBase): """Warp the image using a random composition of sine waves""" def __init__(self, amplitudeRange=(1, 1), # (2, 6), periodRange=(1, 1) # (0.65, 0.73), ): self.amplitude = random.uniform(*amplitudeRange) self.period = random.uniform(*periodRange) self.offset = (random.uniform(0, math.pi * 2 / self.period), random.uniform(0, math.pi * 2 / self.period)) def get_transform(self, image): return (lambda x, y, a=self.amplitude, p=self.period, o=self.offset: (math.sin((y + o[0]) * p) * a + x, math.sin((x + o[1]) * p) * a + y)) @view_config(route_name='captcha') def DoCaptcha(request): img_size = (230, 100) work_img = Image.new('RGBA', img_size, (255, 255, 255, 0)) Xmax, Ymax = work_img.size # Write something on it draw = ImageDraw.Draw(work_img) # use a truetype font # font = ImageFont.truetype("/var/lib/defoma/gs.d/dirs/fonts/LiberationMono-Regular.ttf", 40) # use it font = ImageFont.truetype("jm2l/static/fonts/LiberationMono-Regular.ttf", 40) # Re-position # Choose a word for captcha text = random.choice(TabMots) Xt, Yt = font.getsize(text) OrX, OrY = (img_size[0] - Xt) / 2, (img_size[1] - Yt) / 2 draw.text((OrX, OrY), text, font=font, fill="#000000") # Apply a Blur # work_img=work_img.filter(ImageFilter.BLUR) # Apply a DETAIL work_img = work_img.filter(ImageFilter.DETAIL) # randomize parameters for perspective ax, ay = (random.uniform(0.9, 1.2), random.uniform(0.9, 1.2)) tx, ty = (random.uniform(0, 0.0003), random.uniform(0, 0.0003)) bx, by = (random.uniform(0.5, 0.8), random.uniform(0, 0.2)) # Apply perspective to Captcha work_img = work_img.transform(img_size, Image.PERSPECTIVE, (ax, bx, -25, by, ay, -10, tx, ty)) # Apply SinWarp to Captcha tr = Captcha_Img(Xmax, Ymax) tr._image = work_img work_img = tr.render() # Apply a Smooth on it work_img = work_img.filter(random.choice([ImageFilter.SMOOTH, ImageFilter.SMOOTH_MORE])) # Save Result request.session['Captcha'] = text # session.save() ImgHandle = io.BytesIO() work_img.save(ImgHandle, 'png') ImgHandle.seek(0) return Response(app_iter=ImgHandle, content_type='image/png')