Le repo des sources pour le site web des JM2L
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 

157 linhas
5.5 KiB

  1. # Stolen from tgcaptcha/plugins
  2. # http://code.google.com/p/tgcaptcha/source/browse/trunk/tgcaptcha/plugins/image/vanasco_dowty/captcha.py
  3. import random
  4. from PIL import Image, ImageDraw, ImageFont, ImageFilter
  5. import io
  6. # from io import StringIO
  7. import math
  8. from pyramid.view import view_config
  9. from .words import TabMots
  10. from pyramid.response import Response
  11. class Captcha_Img(object):
  12. def __init__(self, width, height):
  13. self.width = width
  14. self.height = height
  15. self._layers = [
  16. _PyCaptcha_SineWarp(amplitudeRange=(4, 8), periodRange=(0.65, 0.73)),
  17. ]
  18. def getImg(self):
  19. """Get a PIL image representing this CAPTCHA test, creating it if necessary"""
  20. if not self._image:
  21. self._image = self.render()
  22. return self._image
  23. def render(self):
  24. """Render this CAPTCHA, returning a PIL image"""
  25. size = (self.width, self.height)
  26. # img = Image.new("RGB", size )
  27. img = self._image
  28. for layer in self._layers:
  29. img = layer.render(img) or img
  30. self._image = img
  31. return self._image
  32. class _PyCaptcha_WarpBase(object):
  33. """Abstract base class for image warping. Subclasses define a
  34. function that maps points in the output image to points in the input image.
  35. This warping engine runs a grid of points through this transform and uses
  36. PIL's mesh transform to warp the image.
  37. """
  38. filtering = Image.BILINEAR
  39. resolution = 40
  40. def get_transform(self, image):
  41. """Return a transformation function, subclasses should override this"""
  42. return lambda x, y: (x, y)
  43. def render(self, image):
  44. r = self.resolution
  45. xPoints = image.size[0] / r + 2
  46. yPoints = image.size[1] / r + 2
  47. f = self.get_transform(image)
  48. # Create a list of arrays with transformed points
  49. xRows = []
  50. yRows = []
  51. for j in range(int(yPoints)):
  52. xRow = []
  53. yRow = []
  54. for i in range(int(xPoints)):
  55. x, y = f(i * r, j * r)
  56. # Clamp the edges so we don't get black undefined areas
  57. x = max(0, min(image.size[0] - 1, x))
  58. y = max(0, min(image.size[1] - 1, y))
  59. xRow.append(x)
  60. yRow.append(y)
  61. xRows.append(xRow)
  62. yRows.append(yRow)
  63. # Create the mesh list, with a transformation for
  64. # each square between points on the grid
  65. mesh = []
  66. for j in range(int(yPoints - 1)):
  67. for i in range(int(xPoints - 1)):
  68. mesh.append((
  69. # Destination rectangle
  70. (i * r, j * r,
  71. (i + 1) * r, (j + 1) * r),
  72. # Source quadrilateral
  73. (xRows[j][i], yRows[j][i],
  74. xRows[j + 1][i], yRows[j + 1][i],
  75. xRows[j + 1][i + 1], yRows[j + 1][i + 1],
  76. xRows[j][i + 1], yRows[j][i + 1]),
  77. ))
  78. return image.transform(image.size, Image.MESH, mesh, self.filtering)
  79. class _PyCaptcha_SineWarp(_PyCaptcha_WarpBase):
  80. """Warp the image using a random composition of sine waves"""
  81. def __init__(self,
  82. amplitudeRange=(1, 1), # (2, 6),
  83. periodRange=(1, 1) # (0.65, 0.73),
  84. ):
  85. self.amplitude = random.uniform(*amplitudeRange)
  86. self.period = random.uniform(*periodRange)
  87. self.offset = (random.uniform(0, math.pi * 2 / self.period),
  88. random.uniform(0, math.pi * 2 / self.period))
  89. def get_transform(self, image):
  90. return (lambda x, y,
  91. a=self.amplitude,
  92. p=self.period,
  93. o=self.offset:
  94. (math.sin((y + o[0]) * p) * a + x,
  95. math.sin((x + o[1]) * p) * a + y))
  96. @view_config(route_name='captcha')
  97. def DoCaptcha(request):
  98. img_size = (230, 100)
  99. work_img = Image.new('RGBA', img_size, (255, 255, 255, 0))
  100. Xmax, Ymax = work_img.size
  101. # Write something on it
  102. draw = ImageDraw.Draw(work_img)
  103. # use a truetype font
  104. # font = ImageFont.truetype("/var/lib/defoma/gs.d/dirs/fonts/LiberationMono-Regular.ttf", 40)
  105. # use it
  106. font = ImageFont.truetype("jm2l/static/fonts/LiberationMono-Regular.ttf", 40)
  107. # Re-position
  108. # Choose a word for captcha
  109. text = random.choice(TabMots)
  110. Xt, Yt = font.getsize(text)
  111. OrX, OrY = (img_size[0] - Xt) / 2, (img_size[1] - Yt) / 2
  112. draw.text((OrX, OrY), text, font=font, fill="#000000")
  113. # Apply a Blur
  114. # work_img=work_img.filter(ImageFilter.BLUR)
  115. # Apply a DETAIL
  116. work_img = work_img.filter(ImageFilter.DETAIL)
  117. # randomize parameters for perspective
  118. ax, ay = (random.uniform(0.9, 1.2), random.uniform(0.9, 1.2))
  119. tx, ty = (random.uniform(0, 0.0003), random.uniform(0, 0.0003))
  120. bx, by = (random.uniform(0.5, 0.8), random.uniform(0, 0.2))
  121. # Apply perspective to Captcha
  122. work_img = work_img.transform(img_size, Image.PERSPECTIVE, (ax, bx, -25, by, ay, -10, tx, ty))
  123. # Apply SinWarp to Captcha
  124. tr = Captcha_Img(Xmax, Ymax)
  125. tr._image = work_img
  126. work_img = tr.render()
  127. # Apply a Smooth on it
  128. work_img = work_img.filter(random.choice([ImageFilter.SMOOTH, ImageFilter.SMOOTH_MORE]))
  129. # Save Result
  130. request.session['Captcha'] = text
  131. # session.save()
  132. ImgHandle = io.BytesIO()
  133. work_img.save(ImgHandle, 'png')
  134. ImgHandle.seek(0)
  135. return Response(app_iter=ImgHandle, content_type='image/png')