Le repo des sources pour le site web des JM2L
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 

157 行
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')