#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Author: sundachuan
import random
import StringIO
from PIL import Image, ImageDraw, ImageFont
class Captcha(object):
_letter_cases = "abcdefghjkmnpqrstuvwxy"
_upper_cases = _letter_cases.upper()
_numbers = ''.join(map(str, range(3, 10)))
_candidate_chars = ''.join((_letter_cases, _upper_cases, _numbers))
font_color_range = (64, 160)
bg_color_range = (144, 255)
line_color_range = (48, 176)
noisy_color_range = (144, 224)
interfering_line_cnt = (3, 5)
noisy_point_chance = (2, 5)
bg_font_type = 'fonts/times.ttf'
font_types = ['fonts/eraserdust.ttf', 'fonts/Sporkesso.ttf', 'fonts/Typpea.ttf']
def __init__(self, size=(150, 40), charset=_candidate_chars, font_type=font_types, font_size=(30, 40), char_cnt=4, interfering_line=True, noisy=True):
self.value = None
self.image = None
self.image_data = None
self.size = size
self.width, self.height = size
self.charset = charset
self.font_size = font_size
self.font_type = [font_type] if isinstance(font_type, str) else font_type
self.char_cnt = (char_cnt, char_cnt) if isinstance(char_cnt, int) else char_cnt
self.interfering_line = interfering_line
self.noisy = noisy
self.flush()
def _random_color(self, beg, end):
return (random.randint(beg, end), random.randint(beg, end), random.randint(beg, end))
def flush(self):
def draw_lines(line_cnt):
for i in range(line_cnt):
begin = (random.randint(0, self.width/2), random.randint(0, self.height))
end = (random.randint(self.width/2, self.width), random.randint(0, self.height))
draw.line([begin, end], fill=self._random_color(*self.line_color_range),
width=random.randint(2, 3))
def draw_noisy(noisy_chance):
for h in xrange(self.height):
for w in xrange(self.width):
if random.randint(0, 1000) < noisy_chance:
font = ImageFont.truetype(self.bg_font_type, 10)
draw.text((w, h), random.choice(self._candidate_chars), font=font, fill=self._random_color(*self.noisy_color_range))
break
def draw_chars():
font_size = random.randint(*self.font_size)
char_cnt = random.randint(*self.char_cnt)
chars = random.sample(self.charset, char_cnt)
self.value = ''.join(chars)
font = ImageFont.truetype(random.choice(self.font_type), font_size)
offset = 0
reservation_width = sum(zip(*map(font.getsize, chars))[0])
for char in chars:
font_width, font_height = font.getsize(char)
pos = random.randint(offset, self.width - reservation_width)
x=random.randint(0, self.height - font_height if self.height - font_height > 0 else 0)
draw.text(
(pos, x),
char,
font=font,
fill=self._random_color(*self.font_color_range)
)
offset = pos + int(font_width * (2.0 / 3.0))
reservation_width -= int(font_width * (2.0 / 3.0))
bg_color = self._random_color(*self.bg_color_range)
self.image = Image.new('RGBA', self.size, bg_color)
draw = ImageDraw.Draw(self.image)
line_cnt = random.randint(*self.interfering_line_cnt)
if self.interfering_line:
draw_lines(line_cnt - line_cnt / 2)
if self.noisy:
draw_noisy(random.randint(*self.noisy_point_chance))
draw_chars()
if self.interfering_line:
draw_lines(line_cnt / 2)
image_data = StringIO.StringIO()
self.image.save(image_data, 'PNG')
self.image_data = image_data.getvalue()
def check(self, value, ignore=True):
if ignore:
return True if value.lower() == self.value.lower() else False
else:
return True if value == self.value else False
if __name__ == '__main__':
captcha = Captcha()
print captcha.value
captcha.image.show(captcha.value)
print captcha.check(captcha.value)