Welcome, guest | Sign In | My Account | Store | Cart

The gen_captcha function generates an image containing text thatÂ’s easy for a human to read, but difficult for a computer. This allows you create a test to tell humans and computers apart. (requires PIL)

Python, 39 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import random
import Image
import ImageFont
import ImageDraw
import ImageFilter


def gen_captcha(text, fnt, fnt_sz, file_name, fmt='JPEG'):
	"""Generate a captcha image"""
	# randomly select the foreground color
	fgcolor = random.randint(0,0xffff00)
	# make the background color the opposite of fgcolor
	bgcolor = fgcolor ^ 0xffffff
	# create a font object 
	font = ImageFont.truetype(fnt,fnt_sz)
	# determine dimensions of the text
	dim = font.getsize(text)
	# create a new image slightly larger that the text
	im = Image.new('RGB', (dim[0]+5,dim[1]+5), bgcolor)
	d = ImageDraw.Draw(im)
	x, y = im.size
	r = random.randint
	# draw 100 random colored boxes on the background
	for num in range(100):
		d.rectangle((r(0,x),r(0,y),r(0,x),r(0,y)),fill=r(0,0xffffff))
	# add the text to the image
	d.text((3,3), text, font=font, fill=fgcolor)
	im = im.filter(ImageFilter.EDGE_ENHANCE_MORE)
	# save the image to a file
	im.save(file_name, format=fmt)

if __name__ == '__main__':
	"""Example: This grabs a random word from the dictionary 'words' (one
	word per line) and generates a jpeg image named 'test.jpg' using
	the truetype font 'porkys.ttf' with a font size of 25.
	"""
	words = open('words').readlines()
	word = words[random.randint(1,len(words))]
	gen_captcha(word.strip(), 'porkys.ttf', 25, "test.jpg")

A CAPTCHA (Completely Automated Public Turing test to Tell Computers and Humans Apart) is a type of challenge and response test used tell humans and computers apart. This could be used to prevent "bots" from accessing online resources, taking part in online polls, signing up for free accounts, etc...

You must provide your own TrueType font(s); it works best if you use odd or irregular shaped fonts. You could even randomly choose a font for each captcha.

7 comments

Kieran Holland 18 years, 6 months ago  # | flag

An alternative python captcha library. Check out the PyCaptcha library too. I have found it to be simple and extensible:

http://freshmeat.net/projects/pycaptcha/

Matthias Urlichs 18 years, 2 months ago  # | flag

dead. no pycaptcha projct on freshmeat; the ones I could find are all PHP.

Antonello Lobianco 17 years, 10 months ago  # | flag

http://sourceforge.net/projects/pycaptcha/. http://sourceforge.net/projects/pycaptcha/

Mayuresh Phadke 17 years, 5 months ago  # | flag

An alternate method to generate the word. Instead of taking the word from dictionary it could be generated as follows:

def gen_random_word(wordLen=6):
        allowedChars = "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWZYZ0123456789"
        word = ""
        for i in range(0, wordLen):
                word = word + allowedChars[random.randint(0,0xffffff) % len(allowedChars)]
        return word

The main function would change to

if __name__ == '__main__':
    """Example: This grabs a random word from the dictionary 'words' (one
    word per line) and generates a jpeg image named 'test.jpg' using
    the truetype font 'porkys.ttf' with a font size of 25.
    """
        word = gen_random_word()
        gen_captcha(word.strip(), 'porkys.ttf', 25, "test.jpg")
Armin Ronacher 16 years, 4 months ago  # | flag

Not yet. It's not dead, the source is currently located here as it seems: http://svn.navi.cx/misc/trunk/pycaptcha/

Armin Ronacher 16 years, 4 months ago  # | flag

Not yet. It's not dead, the source is currently located here as it seems: http://svn.navi.cx/misc/trunk/pycaptcha/

newacct 13 years, 3 months ago  # | flag

There is a problem in this line:

word = words[random.randint(1,len(words))]

random.randint(1,len(words)) generates numbers between 1 and len(words), inclusive; so (1) it would never pick the first element (index 0), and (2) sometimes it would crash, when it tries to use index = len(words), which is past the end of the list