Page MenuHomePhabricator

Add threading to captcha.py
Closed, ResolvedPublic

Description

Following on from T150029, and in preperation for T150049... Generating captchas is slow.. It'd be nice if it had a parameter, and could run multiple threads of generation simultaneously

Event Timeline

Reedy created this task.Feb 9 2017, 9:14 PM
Restricted Application added a subscriber: Aklapper. · View Herald TranscriptFeb 9 2017, 9:14 PM
Florian claimed this task.Feb 10 2017, 5:35 PM
Florian added a subscriber: Florian.

That's a pretty good idea! :)

Change 337057 had a related patch set uploaded (by Florianschmidtwelzow):
Add threads parameter to captcha.py for multithread CAPTCHA generation

https://gerrit.wikimedia.org/r/337057

Reedy added a comment.Feb 11 2017, 1:35 AM

To generate 10k captchas, using WMF wordlists etc etc...

On a single core vm. 2299.984 MHz

real    43m31.650s
user    41m54.592s
sys     0m33.300s

on terbium, 6 (12 threads with HT) core E 5-2440 at 2.4 GHz

real    26m41.157s
user    26m31.411s
sys     0m7.561s

Will test it with > 1 thread with Florians patch to see the improvement it makes

Great, thanks @Reedy for the test. I can't await the results with multiple threads :)

Reedy added a comment.Feb 11 2017, 4:22 PM

Well, this is fun...

reedy@terbium:~$ time python 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '10000' --dirs '0' --wordlist '/etc/fancycaptcha/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/etc/fancycaptcha/badwords' --threads 2
Generating 10000 CAPTCHA images separated in 5000 image(s) per chunk run by 2 threads...

real    26m39.426s
user    36m26.436s
sys     1m32.109s
reedy@terbium:~$ time python 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '10000' --dirs '0' --wordlist '/etc/fancycaptcha/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/etc/fancycaptcha/badwords' --threads 4
Generating 10000 CAPTCHA images separated in 2500 image(s) per chunk run by 4 threads...

real    25m20.317s
user    40m33.306s
sys     4m5.107s
reedy@terbium:~$ time python 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '10000' --dirs '0' --wordlist '/etc/fancycaptcha/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/etc/fancycaptcha/badwords' --threads 8
Generating 10000 CAPTCHA images separated in 1250 image(s) per chunk run by 8 threads...

real    30m46.742s
user    49m30.369s
sys     7m3.802s

So based on this... Multi threading, at least, in it's current implementation, makes no improvement? :(

@Florian Do you have any python benchmarking/profiling tools? It'd be interesting to see what parts (of the single threaded version) is taking the time, and if there's anything we can do to improve it

I'll deploy my backport of the instrumentation to the php script, and have a look at what % of the overall time this takes....

You saved Florian's code as captcha-old.py?

You saved Florian's code as captcha-old.py?

Kind of, I wanted to test it on the version we're running in production, so copied his changes to the old copy of the script to test the multithreading

https://github.com/wikimedia/mediawiki-extensions-ConfirmEdit/blob/master/captcha.py vs https://github.com/wikimedia/mediawiki-extensions-ConfirmEdit/blob/master/captcha-old.py

For 100 images...

reedy@terbium:~$ time python -m cProfile -s cumtime 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '100' --dirs '0' --wordlist '/etc/fancycaptcha/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/etc/fancycaptcha/badwords'
Generating 100 CAPTCHA images separated in 100 image(s) per chunk run by 1 threads...
         302199 function calls (302198 primitive calls) in 16.427 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.023    0.023   16.427   16.427 captcha-old.py:28(<module>)
      160   16.336    0.102   16.336    0.102 {time.sleep}
        2    0.028    0.014    0.052    0.026 captcha-old.py:175(read_wordlist)
   100052    0.011    0.000    0.011    0.000 {method 'lower' of 'str' objects}
   100048    0.008    0.000    0.008    0.000 {method 'strip' of 'str' objects}
        2    0.006    0.003    0.006    0.003 {method 'readlines' of 'file' objects}
    99639    0.005    0.000    0.005    0.000 {len}
        1    0.001    0.001    0.004    0.004 Image.py:1(<module>)
        1    0.001    0.001    0.003    0.003 Image.py:27(<module>)
        1    0.000    0.000    0.002    0.002 {__import__}
        1    0.000    0.000    0.002    0.002 FixTk.py:1(<module>)
      161    0.001    0.000    0.001    0.000 threading.py:1167(activeCount)
        1    0.001    0.001    0.001    0.001 __init__.py:4(<module>)
        1    0.000    0.000    0.001    0.001 threading.py:726(start)
        1    0.000    0.000    0.001    0.001 threading.py:602(wait)
        1    0.000    0.000    0.001    0.001 threading.py:308(wait)
        7    0.001    0.000    0.001    0.000 {method 'acquire' of 'thread.lock' objects}
        1    0.000    0.000    0.001    0.001 threading.py:1(<module>)
        1    0.000    0.000    0.001    0.001 random.py:40(<module>)
        1    0.000    0.000    0.001    0.001 ImageEnhance.py:1(<module>)
        1    0.000    0.000    0.001    0.001 ImageEnhance.py:21(<module>)
        1    0.000    0.000    0.000    0.000 numbers.py:6(<module>)
        1    0.000    0.000    0.000    0.000 hashlib.py:55(<module>)
        1    0.000    0.000    0.000    0.000 ImageFont.py:1(<module>)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:18(<module>)
       16    0.000    0.000    0.000    0.000 optparse.py:1008(add_option)
        1    0.000    0.000    0.000    0.000 collections.py:1(<module>)
        1    0.000    0.000    0.000    0.000 optparse.py:1191(__init__)
        3    0.000    0.000    0.000    0.000 re.py:188(compile)
        3    0.000    0.000    0.000    0.000 re.py:226(_compile)
        5    0.000    0.000    0.000    0.000 abc.py:86(__new__)
        3    0.000    0.000    0.000    0.000 sre_compile.py:493(compile)
        1    0.000    0.000    0.000    0.000 ImageFont.py:28(<module>)
        2    0.000    0.000    0.000    0.000 gettext.py:580(gettext)
        2    0.000    0.000    0.000    0.000 gettext.py:542(dgettext)
       16    0.000    0.000    0.000    0.000 optparse.py:561(__init__)
        2    0.000    0.000    0.000    0.000 gettext.py:476(translation)
        2    0.000    0.000    0.000    0.000 gettext.py:421(find)
        1    0.000    0.000    0.000    0.000 warnings.py:45(filterwarnings)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:1(<module>)
        1    0.000    0.000    0.000    0.000 __init__.py:265(_reset_cache)
        1    0.000    0.000    0.000    0.000 optparse.py:1258(_populate_option_list)
        1    0.000    0.000    0.000    0.000 optparse.py:1248(_add_help_option)
        1    0.000    0.000    0.000    0.000 optparse.py:1277(set_usage)
        3    0.000    0.000    0.000    0.000 sre_compile.py:478(_code)
        3    0.000    0.000    0.000    0.000 sre_parse.py:675(parse)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:33(<module>)
        2    0.000    0.000    0.000    0.000 threading.py:656(__init__)
        1    0.000    0.000    0.000    0.000 optparse.py:1368(parse_args)
        3    0.000    0.000    0.000    0.000 sre_parse.py:301(_parse_sub)
        1    0.000    0.000    0.000    0.000 heapq.py:31(<module>)
        1    0.000    0.000    0.000    0.000 ImageOps.py:1(<module>)
        3    0.000    0.000    0.000    0.000 sre_parse.py:379(_parse)
       23    0.000    0.000    0.000    0.000 genericpath.py:15(exists)
        4    0.000    0.000    0.000    0.000 abc.py:105(register)
       14    0.000    0.000    0.000    0.000 __init__.py:147(_check_size)
       16    0.000    0.000    0.000    0.000 optparse.py:610(_set_attrs)
        5    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x91d400}
        3    0.000    0.000    0.000    0.000 sre_compile.py:359(_compile_info)
       25    0.000    0.000    0.000    0.000 posixpath.py:68(join)
     10/9    0.000    0.000    0.000    0.000 {issubclass}
        3    0.000    0.000    0.000    0.000 __init__.py:78(CFUNCTYPE)
        4    0.000    0.000    0.000    0.000 abc.py:148(__subclasscheck__)
        1    0.000    0.000    0.000    0.000 optparse.py:1420(_process_args)
        2    0.000    0.000    0.000    0.000 sre_compile.py:178(_compile_charset)
        6    0.000    0.000    0.000    0.000 gettext.py:130(_expand_lang)
        7    0.000    0.000    0.000    0.000 optparse.py:1480(_process_long_opt)
        1    0.000    0.000    0.000    0.000 threading.py:1090(__init__)
      215    0.000    0.000    0.000    0.000 {getattr}
        2    0.000    0.000    0.000    0.000 sre_compile.py:207(_optimize_charset)
        1    0.000    0.000    0.000    0.000 _endian.py:4(<module>)
       23    0.000    0.000    0.000    0.000 {posix.stat}
       46    0.000    0.000    0.000    0.000 abc.py:89(<genexpr>)
        1    0.000    0.000    0.000    0.000 random.py:91(__init__)
        2    0.000    0.000    0.000    0.000 threading.py:541(Event)
        1    0.000    0.000    0.000    0.000 random.py:100(seed)
        1    0.000    0.000    0.000    0.000 {thread.start_new_thread}
        3    0.000    0.000    0.000    0.000 sre_compile.py:32(_compile)
        3    0.000    0.000    0.000    0.000 __init__.py:493(PYFUNCTYPE)
        4    0.000    0.000    0.000    0.000 threading.py:241(Condition)
        2    0.000    0.000    0.000    0.000 threading.py:560(__init__)
       32    0.000    0.000    0.000    0.000 sre_parse.py:201(get)
        4    0.000    0.000    0.000    0.000 threading.py:259(__init__)
        1    0.000    0.000    0.000    0.000 ImageStat.py:24(<module>)
        1    0.000    0.000    0.000    0.000 optparse.py:1319(get_default_values)
        2    0.000    0.000    0.000    0.000 {_ctypes.POINTER}
       37    0.000    0.000    0.000    0.000 sre_parse.py:182(__next)
        1    0.000    0.000    0.000    0.000 posixpath.py:379(realpath)
        7    0.000    0.000    0.000    0.000 optparse.py:779(process)
        1    0.000    0.000    0.000    0.000 __init__.py:349(__init__)
       16    0.000    0.000    0.000    0.000 optparse.py:636(_check_type)
       16    0.000    0.000    0.000    0.000 optparse.py:589(_set_opt_strings)
        2    0.000    0.000    0.000    0.000 posixpath.py:365(abspath)
        5    0.000    0.000    0.000    0.000 _weakrefset.py:58(__iter__)
       16    0.000    0.000    0.000    0.000 _weakrefset.py:36(__init__)
        1    0.000    0.000    0.000    0.000 ImageColor.py:20(<module>)
      214    0.000    0.000    0.000    0.000 {setattr}
        7    0.000    0.000    0.000    0.000 optparse.py:772(convert_value)
       16    0.000    0.000    0.000    0.000 optparse.py:679(_check_dest)
      185    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 _util.py:1(<module>)
        1    0.000    0.000    0.000    0.000 {posix.urandom}
        1    0.000    0.000    0.000    0.000 threading.py:296(_acquire_restore)
        2    0.000    0.000    0.000    0.000 posixpath.py:336(normpath)
        1    0.000    0.000    0.000    0.000 numbers.py:34(Complex)
        3    0.000    0.000    0.000    0.000 sre_parse.py:140(getwidth)
        6    0.000    0.000    0.000    0.000 hashlib.py:94(__get_openssl_constructor)
       18    0.000    0.000    0.000    0.000 {_struct.calcsize}
       42    0.000    0.000    0.000    0.000 {isinstance}
        6    0.000    0.000    0.000    0.000 locale.py:363(normalize)
        1    0.000    0.000    0.000    0.000 posixpath.py:387(_joinrealpath)
        8    0.000    0.000    0.000    0.000 optparse.py:765(check_value)
        1    0.000    0.000    0.000    0.000 ImageOps.py:20(<module>)
        1    0.000    0.000    0.000    0.000 threading.py:640(Thread)
        1    0.000    0.000    0.000    0.000 {function seed at 0x7fc6d94b0ed8}
       16    0.000    0.000    0.000    0.000 optparse.py:580(_check_opt_strings)
       66    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
       68    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
        2    0.000    0.000    0.000    0.000 {method 'close' of 'file' objects}
       74    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        1    0.000    0.000    0.000    0.000 __future__.py:48(<module>)
        2    0.000    0.000    0.000    0.000 {open}
        1    0.000    0.000    0.000    0.000 threading.py:575(set)
        1    0.000    0.000    0.000    0.000 optparse.py:366(__init__)
       23    0.000    0.000    0.000    0.000 sre_parse.py:138(append)
        1    0.000    0.000    0.000    0.000 threading.py:709(_set_daemon)
        1    0.000    0.000    0.000    0.000 numbers.py:295(Integral)
        1    0.000    0.000    0.000    0.000 {math.exp}
       37    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
       16    0.000    0.000    0.000    0.000 optparse.py:981(_check_conflict)
        1    0.000    0.000    0.000    0.000 optparse.py:200(__init__)
        3    0.000    0.000    0.000    0.000 sre_parse.py:178(__init__)
        4    0.000    0.000    0.000    0.000 _weakrefset.py:26(__exit__)
       37    0.000    0.000    0.000    0.000 abc.py:15(abstractmethod)
        1    0.000    0.000    0.000    0.000 Image.py:447(Image)
        8    0.000    0.000    0.000    0.000 _weakrefset.py:83(add)
        1    0.000    0.000    0.000    0.000 optparse.py:838(__init__)
        2    0.000    0.000    0.000    0.000 optparse.py:424(check_builtin)
        1    0.000    0.000    0.000    0.000 threading.py:399(notifyAll)
        2    0.000    0.000    0.000    0.000 optparse.py:413(_parse_int)
        9    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 posixpath.py:139(islink)
       10    0.000    0.000    0.000    0.000 {hasattr}
        8    0.000    0.000    0.000    0.000 threading.py:58(__init__)
        2    0.000    0.000    0.000    0.000 {math.log}
        7    0.000    0.000    0.000    0.000 _weakrefset.py:70(__contains__)
        1    0.000    0.000    0.000    0.000 collections.py:26(OrderedDict)
        7    0.000    0.000    0.000    0.000 optparse.py:1471(_match_long_opt)
       15    0.000    0.000    0.000    0.000 {method 'replace' of 'str' objects}
        4    0.000    0.000    0.000    0.000 _weakrefset.py:20(__enter__)
       16    0.000    0.000    0.000    0.000 optparse.py:666(_check_choice)
        1    0.000    0.000    0.000    0.000 random.py:72(Random)
        9    0.000    0.000    0.000    0.000 {range}
        1    0.000    0.000    0.000    0.000 numbers.py:169(Real)
       16    0.000    0.000    0.000    0.000 optparse.py:833(isbasestring)
        2    0.000    0.000    0.000    0.000 UserDict.py:58(get)
        1    0.000    0.000    0.000    0.000 threading.py:627(_newname)
       15    0.000    0.000    0.000    0.000 {method 'pop' of 'list' objects}
       18    0.000    0.000    0.000    0.000 {method 'find' of 'str' objects}
        2    0.000    0.000    0.000    0.000 optparse.py:400(_parse_num)
       16    0.000    0.000    0.000    0.000 optparse.py:709(_check_callback)
       38    0.000    0.000    0.000    0.000 {_ctypes.sizeof}
        6    0.000    0.000    0.000    0.000 sre_compile.py:472(isstring)
        1    0.000    0.000    0.000    0.000 threading.py:1035(setDaemon)
        3    0.000    0.000    0.000    0.000 sre_parse.py:67(__init__)
        1    0.000    0.000    0.000    0.000 posixpath.py:127(dirname)
        1    0.000    0.000    0.000    0.000 threading.py:1152(currentThread)
        1    0.000    0.000    0.000    0.000 keyword.py:11(<module>)
       16    0.000    0.000    0.000    0.000 {filter}
        1    0.000    0.000    0.000    0.000 numbers.py:270(Rational)
        3    0.000    0.000    0.000    0.000 {_sre.compile}
       16    0.000    0.000    0.000    0.000 optparse.py:700(_check_nargs)
        1    0.000    0.000    0.000    0.000 optparse.py:933(__init__)
        1    0.000    0.000    0.000    0.000 threading.py:372(notify)
        7    0.000    0.000    0.000    0.000 optparse.py:1675(_match_abbrev)
       16    0.000    0.000    0.000    0.000 optparse.py:694(_check_const)
        5    0.000    0.000    0.000    0.000 sre_parse.py:195(match)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:49(ImageDraw)
        1    0.000    0.000    0.000    0.000 ImageMode.py:17(<module>)
        7    0.000    0.000    0.000    0.000 optparse.py:791(take_action)
        1    0.000    0.000    0.000    0.000 {posix.lstat}
        4    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {binascii.hexlify}
        1    0.000    0.000    0.000    0.000 threading.py:422(_Semaphore)
        2    0.000    0.000    0.000    0.000 threading.py:299(_is_owned)
        1    0.000    0.000    0.000    0.000 _binary.py:14(<module>)
        1    0.000    0.000    0.000    0.000 {_ctypes.dlopen}
        1    0.000    0.000    0.000    0.000 threading.py:1024(daemon)
        3    0.000    0.000    0.000    0.000 UserDict.py:18(__getitem__)
        1    0.000    0.000    0.000    0.000 {sorted}
        2    0.000    0.000    0.000    0.000 {method 'setter' of 'property' objects}
        7    0.000    0.000    0.000    0.000 {min}
       16    0.000    0.000    0.000    0.000 optparse.py:630(_check_action)
        3    0.000    0.000    0.000    0.000 posixpath.py:59(isabs)
        1    0.000    0.000    0.000    0.000 collections.py:387(Counter)
        3    0.000    0.000    0.000    0.000 threading.py:63(_note)
        1    0.000    0.000    0.000    0.000 threading.py:293(_release_save)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
        1    0.000    0.000    0.000    0.000 threading.py:789(_set_ident)
        7    0.000    0.000    0.000    0.000 optparse.py:753(takes_value)
        1    0.000    0.000    0.000    0.000 {posix.getcwd}
        6    0.000    0.000    0.000    0.000 {globals}
        2    0.000    0.000    0.000    0.000 {method 'clear' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 ImageFont.py:120(FreeTypeFont)
       23    0.000    0.000    0.000    0.000 {ord}
        4    0.000    0.000    0.000    0.000 _weakrefset.py:52(_commit_removals)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:113(MaxFilter)
        6    0.000    0.000    0.000    0.000 {thread.allocate_lock}
        2    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
        4    0.000    0.000    0.000    0.000 _weakrefset.py:16(__init__)
       12    0.000    0.000    0.000    0.000 {_sre.getlower}
        3    0.000    0.000    0.000    0.000 __init__.py:494(CFunctionType)
        1    0.000    0.000    0.000    0.000 threading.py:124(_RLock)
        6    0.000    0.000    0.000    0.000 {method 'translate' of 'str' objects}
        7    0.000    0.000    0.000    0.000 __future__.py:75(__init__)
        3    0.000    0.000    0.000    0.000 __init__.py:104(CFunctionType)
        1    0.000    0.000    0.000    0.000 {math.sqrt}
        1    0.000    0.000    0.000    0.000 ImageStat.py:29(Stat)
        6    0.000    0.000    0.000    0.000 {method 'reverse' of 'list' objects}
        1    0.000    0.000    0.000    0.000 optparse.py:1243(_create_option_list)
        6    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 threading.py:1058(_Timer)
        1    0.000    0.000    0.000    0.000 __init__.py:294(c_wchar)
        1    0.000    0.000    0.000    0.000 {method 'partition' of 'str' objects}
        1    0.000    0.000    0.000    0.000 threading.py:551(_Event)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:66(Brightness)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:127(ModeFilter)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:145(GaussianBlur)
        1    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:359(_FuncPtr)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:57(BuiltinFilter)
        1    0.000    0.000    0.000    0.000 __init__.py:189(c_int)
        1    0.000    0.000    0.000    0.000 optparse.py:1268(_init_parsing_state)
        1    0.000    0.000    0.000    0.000 ImageMode.py:22(ModeDescriptor)
        1    0.000    0.000    0.000    0.000 __init__.py:291(c_wchar_p)
        1    0.000    0.000    0.000    0.000 __init__.py:238(c_char)
        1    0.000    0.000    0.000    0.000 random.py:800(SystemRandom)
        1    0.000    0.000    0.000    0.000 {method 'keys' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {method 'copy' of 'dict' objects}
        3    0.000    0.000    0.000    0.000 {thread.get_ident}
        1    0.000    0.000    0.000    0.000 __init__.py:172(c_ushort)
        1    0.000    0.000    0.000    0.000 __init__.py:205(c_longdouble)
        1    0.000    0.000    0.000    0.000 __init__.py:243(c_char_p)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:40(Color)
        1    0.000    0.000    0.000    0.000 __init__.py:14(<module>)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:198(DETAIL)
        1    0.000    0.000    0.000    0.000 optparse.py:960(set_conflict_handler)
        1    0.000    0.000    0.000    0.000 numbers.py:13(Number)
        1    0.000    0.000    0.000    0.000 threading.py:1008(daemon)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:225(EMBOSS)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha256}
        1    0.000    0.000    0.000    0.000 threading.py:514(_BoundedSemaphore)
        1    0.000    0.000    0.000    0.000 {_ctypes.set_conversion_mode}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:25(Kernel)
        1    0.000    0.000    0.000    0.000 threading.py:1088(_MainThread)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:85(MedianFilter)
        1    0.000    0.000    0.000    0.000 threading.py:56(_Verbose)
        1    0.000    0.000    0.000    0.000 __init__.py:197(c_float)
        1    0.000    0.000    0.000    0.000 __init__.py:332(CDLL)
        1    0.000    0.000    0.000    0.000 random.py:650(WichmannHill)
        1    0.000    0.000    0.000    0.000 threading.py:1097(_set_daemon)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:21(Filter)
        1    0.000    0.000    0.000    0.000 optparse.py:1313(_get_all_options)
        1    0.000    0.000    0.000    0.000 __future__.py:74(_Feature)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:24(_Enhance)
        2    0.000    0.000    0.000    0.000 __init__.py:429(__init__)
        1    0.000    0.000    0.000    0.000 optparse.py:1407(check_values)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:207(EDGE_ENHANCE)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:178(BLUR)
        3    0.000    0.000    0.000    0.000 sre_parse.py:90(__init__)
        1    0.000    0.000    0.000    0.000 ImageFont.py:166(TransposedFont)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:234(FIND_EDGES)
        1    0.000    0.000    0.000    0.000 stat.py:55(S_ISLNK)
        1    0.000    0.000    0.000    0.000 __init__.py:168(c_short)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:53(Contrast)
        2    0.000    0.000    0.000    0.000 UserDict.py:70(__contains__)
        1    0.000    0.000    0.000    0.000 ImageFont.py:65(ImageFont)
        1    0.000    0.000    0.000    0.000 __init__.py:255(c_void_p)
        4    0.000    0.000    0.000    0.000 {method 'remove' of 'set' objects}
        1    0.000    0.000    0.000    0.000 _endian.py:49(BigEndianStructure)
        1    0.000    0.000    0.000    0.000 threading.py:254(_Condition)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:216(EDGE_ENHANCE_MORE)
        1    0.000    0.000    0.000    0.000 Image.py:33(_imaging_not_installed)
        1    0.000    0.000    0.000    0.000 Image.py:417(_E)
        1    0.000    0.000    0.000    0.000 Image.py:1710(_ImageCrop)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:99(MinFilter)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:388(PyDLL)
        1    0.000    0.000    0.000    0.000 ImageFont.py:39(_imagingft_not_installed)
        1    0.000    0.000    0.000    0.000 __init__.py:193(c_uint)
        1    0.000    0.000    0.000    0.000 optparse.py:225(set_parser)
        1    0.000    0.000    0.000    0.000 __init__.py:233(c_byte)
        1    0.000    0.000    0.000    0.000 Image.py:1749(ImageTransformHandler)
        1    0.000    0.000    0.000    0.000 __init__.py:180(c_ulong)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha224}
        2    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:78(Sharpness)
        1    0.000    0.000    0.000    0.000 threading.py:1128(_DummyThread)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:243(SMOOTH)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha1}
        1    0.000    0.000    0.000    0.000 optparse.py:756(get_opt_string)
        8    0.000    0.000    0.000    0.000 sre_compile.py:24(_identityfunction)
        1    0.000    0.000    0.000    0.000 _endian.py:26(_swapped_meta)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:62(RankFilter)
        4    0.000    0.000    0.000    0.000 {method '__subclasshook__' of 'object' objects}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:263(SHARPEN)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:252(SMOOTH_MORE)
        1    0.000    0.000    0.000    0.000 stat.py:24(S_IFMT)
        1    0.000    0.000    0.000    0.000 {max}
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha384}
        1    0.000    0.000    0.000    0.000 optparse.py:944(_create_option_mappings)
        4    0.000    0.000    0.000    0.000 {method '__subclasses__' of 'type' objects}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:159(UnsharpMask)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha512}
        1    0.000    0.000    0.000    0.000 Image.py:1745(ImagePointHandler)
        1    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:176(c_long)
        1    0.000    0.000    0.000    0.000 __init__.py:201(c_double)
        1    0.000    0.000    0.000    0.000 __init__.py:260(c_bool)
        2    0.000    0.000    0.000    0.000 threading.py:569(isSet)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:189(CONTOUR)
        1    0.000    0.000    0.000    0.000 optparse.py:965(set_description)
        3    0.000    0.000    0.000    0.000 {method 'release' of 'thread.lock' objects}
        1    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:226(c_ubyte)
        1    0.000    0.000    0.000    0.000 optparse.py:1362(_get_args)
        1    0.000    0.000    0.000    0.000 __init__.py:428(LibraryLoader)
        1    0.000    0.000    0.000    0.000 __init__.py:159(py_object)



real	0m16.480s
user	0m16.140s
sys	0m0.272s
reedy@terbium:~$

And for 1000...

reedy@terbium:~$ time python -m cProfile -s cumtime 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '1000' --dirs '0' --wordlist '/etc/fancycaptcha/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/etc/fancycaptcha/badwords'
Generating 1000 CAPTCHA images separated in 1000 image(s) per chunk run by 1 threads...
         308827 function calls (308826 primitive calls) in 185.216 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.041    0.041  185.216  185.216 captcha-old.py:28(<module>)
     1817  185.074    0.102  185.074    0.102 {time.sleep}
        2    0.033    0.017    0.061    0.031 captcha-old.py:175(read_wordlist)
     1818    0.020    0.000    0.021    0.000 threading.py:1167(activeCount)
   100052    0.013    0.000    0.013    0.000 {method 'lower' of 'str' objects}
   100048    0.009    0.000    0.009    0.000 {method 'strip' of 'str' objects}
   102953    0.007    0.000    0.007    0.000 {len}
        2    0.007    0.003    0.007    0.003 {method 'readlines' of 'file' objects}
        1    0.001    0.001    0.005    0.005 Image.py:1(<module>)
        1    0.001    0.001    0.004    0.004 Image.py:27(<module>)
        1    0.000    0.000    0.002    0.002 {__import__}
        1    0.000    0.000    0.002    0.002 FixTk.py:1(<module>)
        1    0.000    0.000    0.002    0.002 threading.py:726(start)
        1    0.001    0.001    0.002    0.002 __init__.py:4(<module>)
        1    0.000    0.000    0.002    0.002 threading.py:602(wait)
        1    0.000    0.000    0.002    0.002 threading.py:308(wait)
        7    0.002    0.000    0.002    0.000 {method 'acquire' of 'thread.lock' objects}
        1    0.001    0.001    0.001    0.001 threading.py:1(<module>)
        1    0.000    0.000    0.001    0.001 random.py:40(<module>)
        1    0.000    0.000    0.001    0.001 ImageEnhance.py:1(<module>)
        1    0.000    0.000    0.001    0.001 ImageEnhance.py:21(<module>)
        1    0.000    0.000    0.001    0.001 numbers.py:6(<module>)
        1    0.001    0.001    0.001    0.001 hashlib.py:55(<module>)
        1    0.000    0.000    0.000    0.000 ImageFont.py:1(<module>)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:18(<module>)
       16    0.000    0.000    0.000    0.000 optparse.py:1008(add_option)
        1    0.000    0.000    0.000    0.000 collections.py:1(<module>)
        1    0.000    0.000    0.000    0.000 optparse.py:1191(__init__)
        3    0.000    0.000    0.000    0.000 re.py:188(compile)
        3    0.000    0.000    0.000    0.000 re.py:226(_compile)
        5    0.000    0.000    0.000    0.000 abc.py:86(__new__)
        3    0.000    0.000    0.000    0.000 sre_compile.py:493(compile)
        1    0.000    0.000    0.000    0.000 ImageFont.py:28(<module>)
       16    0.000    0.000    0.000    0.000 optparse.py:561(__init__)
        2    0.000    0.000    0.000    0.000 gettext.py:580(gettext)
        2    0.000    0.000    0.000    0.000 gettext.py:542(dgettext)
        2    0.000    0.000    0.000    0.000 gettext.py:476(translation)
        2    0.000    0.000    0.000    0.000 gettext.py:421(find)
        1    0.000    0.000    0.000    0.000 warnings.py:45(filterwarnings)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:1(<module>)
        1    0.000    0.000    0.000    0.000 __init__.py:265(_reset_cache)
        1    0.000    0.000    0.000    0.000 optparse.py:1258(_populate_option_list)
        1    0.000    0.000    0.000    0.000 optparse.py:1248(_add_help_option)
        1    0.000    0.000    0.000    0.000 optparse.py:1277(set_usage)
        3    0.000    0.000    0.000    0.000 sre_compile.py:478(_code)
        3    0.000    0.000    0.000    0.000 sre_parse.py:675(parse)
        2    0.000    0.000    0.000    0.000 threading.py:656(__init__)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:33(<module>)
        1    0.000    0.000    0.000    0.000 optparse.py:1368(parse_args)
        3    0.000    0.000    0.000    0.000 sre_parse.py:301(_parse_sub)
        1    0.000    0.000    0.000    0.000 heapq.py:31(<module>)
        1    0.000    0.000    0.000    0.000 ImageOps.py:1(<module>)
        3    0.000    0.000    0.000    0.000 sre_parse.py:379(_parse)
       23    0.000    0.000    0.000    0.000 genericpath.py:15(exists)
        4    0.000    0.000    0.000    0.000 abc.py:105(register)
       14    0.000    0.000    0.000    0.000 __init__.py:147(_check_size)
       16    0.000    0.000    0.000    0.000 optparse.py:610(_set_attrs)
        5    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x91d400}
        3    0.000    0.000    0.000    0.000 sre_compile.py:359(_compile_info)
     10/9    0.000    0.000    0.000    0.000 {issubclass}
       25    0.000    0.000    0.000    0.000 posixpath.py:68(join)
        3    0.000    0.000    0.000    0.000 __init__.py:78(CFUNCTYPE)
        4    0.000    0.000    0.000    0.000 abc.py:148(__subclasscheck__)
        1    0.000    0.000    0.000    0.000 optparse.py:1420(_process_args)
        6    0.000    0.000    0.000    0.000 gettext.py:130(_expand_lang)
        2    0.000    0.000    0.000    0.000 sre_compile.py:178(_compile_charset)
        7    0.000    0.000    0.000    0.000 optparse.py:1480(_process_long_opt)
      215    0.000    0.000    0.000    0.000 {getattr}
        1    0.000    0.000    0.000    0.000 threading.py:1090(__init__)
       23    0.000    0.000    0.000    0.000 {posix.stat}
        2    0.000    0.000    0.000    0.000 sre_compile.py:207(_optimize_charset)
        1    0.000    0.000    0.000    0.000 _endian.py:4(<module>)
        3    0.000    0.000    0.000    0.000 sre_compile.py:32(_compile)
       46    0.000    0.000    0.000    0.000 abc.py:89(<genexpr>)
        2    0.000    0.000    0.000    0.000 threading.py:541(Event)
        4    0.000    0.000    0.000    0.000 threading.py:241(Condition)
        1    0.000    0.000    0.000    0.000 random.py:91(__init__)
        3    0.000    0.000    0.000    0.000 __init__.py:493(PYFUNCTYPE)
        1    0.000    0.000    0.000    0.000 random.py:100(seed)
        2    0.000    0.000    0.000    0.000 threading.py:560(__init__)
        4    0.000    0.000    0.000    0.000 threading.py:259(__init__)
       32    0.000    0.000    0.000    0.000 sre_parse.py:201(get)
        1    0.000    0.000    0.000    0.000 ImageStat.py:24(<module>)
        2    0.000    0.000    0.000    0.000 {_ctypes.POINTER}
        1    0.000    0.000    0.000    0.000 {thread.start_new_thread}
        1    0.000    0.000    0.000    0.000 optparse.py:1319(get_default_values)
       37    0.000    0.000    0.000    0.000 sre_parse.py:182(__next)
       16    0.000    0.000    0.000    0.000 optparse.py:589(_set_opt_strings)
        1    0.000    0.000    0.000    0.000 posixpath.py:379(realpath)
        7    0.000    0.000    0.000    0.000 optparse.py:779(process)
        1    0.000    0.000    0.000    0.000 __init__.py:349(__init__)
       16    0.000    0.000    0.000    0.000 optparse.py:636(_check_type)
        2    0.000    0.000    0.000    0.000 posixpath.py:365(abspath)
        5    0.000    0.000    0.000    0.000 _weakrefset.py:58(__iter__)
        1    0.000    0.000    0.000    0.000 ImageColor.py:20(<module>)
       16    0.000    0.000    0.000    0.000 _weakrefset.py:36(__init__)
      214    0.000    0.000    0.000    0.000 {setattr}
       16    0.000    0.000    0.000    0.000 optparse.py:679(_check_dest)
        1    0.000    0.000    0.000    0.000 _util.py:1(<module>)
      185    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        7    0.000    0.000    0.000    0.000 optparse.py:772(convert_value)
        2    0.000    0.000    0.000    0.000 posixpath.py:336(normpath)
       68    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
       74    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        1    0.000    0.000    0.000    0.000 posixpath.py:387(_joinrealpath)
        8    0.000    0.000    0.000    0.000 optparse.py:765(check_value)
        1    0.000    0.000    0.000    0.000 numbers.py:34(Complex)
       18    0.000    0.000    0.000    0.000 {_struct.calcsize}
        1    0.000    0.000    0.000    0.000 {posix.urandom}
       42    0.000    0.000    0.000    0.000 {isinstance}
        6    0.000    0.000    0.000    0.000 locale.py:363(normalize)
        6    0.000    0.000    0.000    0.000 hashlib.py:94(__get_openssl_constructor)
        3    0.000    0.000    0.000    0.000 sre_parse.py:140(getwidth)
        1    0.000    0.000    0.000    0.000 threading.py:640(Thread)
       16    0.000    0.000    0.000    0.000 optparse.py:580(_check_opt_strings)
        1    0.000    0.000    0.000    0.000 {function seed at 0x7f2f2e9f4ed8}
        1    0.000    0.000    0.000    0.000 ImageOps.py:20(<module>)
        2    0.000    0.000    0.000    0.000 {method 'close' of 'file' objects}
        2    0.000    0.000    0.000    0.000 {open}
        1    0.000    0.000    0.000    0.000 __future__.py:48(<module>)
        1    0.000    0.000    0.000    0.000 optparse.py:366(__init__)
        1    0.000    0.000    0.000    0.000 threading.py:575(set)
       16    0.000    0.000    0.000    0.000 optparse.py:981(_check_conflict)
        1    0.000    0.000    0.000    0.000 {math.exp}
        1    0.000    0.000    0.000    0.000 threading.py:709(_set_daemon)
       37    0.000    0.000    0.000    0.000 abc.py:15(abstractmethod)
        1    0.000    0.000    0.000    0.000 threading.py:296(_acquire_restore)
       10    0.000    0.000    0.000    0.000 {hasattr}
        1    0.000    0.000    0.000    0.000 numbers.py:295(Integral)
        1    0.000    0.000    0.000    0.000 optparse.py:200(__init__)
       23    0.000    0.000    0.000    0.000 sre_parse.py:138(append)
        1    0.000    0.000    0.000    0.000 Image.py:447(Image)
        3    0.000    0.000    0.000    0.000 sre_parse.py:178(__init__)
        4    0.000    0.000    0.000    0.000 _weakrefset.py:26(__exit__)
       66    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
        8    0.000    0.000    0.000    0.000 _weakrefset.py:83(add)
        9    0.000    0.000    0.000    0.000 {range}
        1    0.000    0.000    0.000    0.000 optparse.py:838(__init__)
       16    0.000    0.000    0.000    0.000 optparse.py:833(isbasestring)
        2    0.000    0.000    0.000    0.000 optparse.py:424(check_builtin)
        2    0.000    0.000    0.000    0.000 optparse.py:413(_parse_int)
        1    0.000    0.000    0.000    0.000 posixpath.py:139(islink)
        8    0.000    0.000    0.000    0.000 threading.py:58(__init__)
        1    0.000    0.000    0.000    0.000 collections.py:26(OrderedDict)
        1    0.000    0.000    0.000    0.000 threading.py:399(notifyAll)
        7    0.000    0.000    0.000    0.000 optparse.py:1471(_match_long_opt)
       15    0.000    0.000    0.000    0.000 {method 'pop' of 'list' objects}
       37    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
        9    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 random.py:72(Random)
        6    0.000    0.000    0.000    0.000 sre_compile.py:472(isstring)
       16    0.000    0.000    0.000    0.000 optparse.py:694(_check_const)
       15    0.000    0.000    0.000    0.000 {method 'replace' of 'str' objects}
        2    0.000    0.000    0.000    0.000 optparse.py:400(_parse_num)
        1    0.000    0.000    0.000    0.000 numbers.py:169(Real)
        1    0.000    0.000    0.000    0.000 posixpath.py:127(dirname)
        1    0.000    0.000    0.000    0.000 keyword.py:11(<module>)
        1    0.000    0.000    0.000    0.000 numbers.py:270(Rational)
        3    0.000    0.000    0.000    0.000 {_sre.compile}
        7    0.000    0.000    0.000    0.000 optparse.py:791(take_action)
        1    0.000    0.000    0.000    0.000 optparse.py:933(__init__)
        1    0.000    0.000    0.000    0.000 threading.py:372(notify)
        2    0.000    0.000    0.000    0.000 {math.log}
        2    0.000    0.000    0.000    0.000 UserDict.py:58(get)
        1    0.000    0.000    0.000    0.000 threading.py:627(_newname)
        3    0.000    0.000    0.000    0.000 sre_parse.py:67(__init__)
        1    0.000    0.000    0.000    0.000 threading.py:1152(currentThread)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:49(ImageDraw)
        4    0.000    0.000    0.000    0.000 _weakrefset.py:20(__enter__)
       18    0.000    0.000    0.000    0.000 {method 'find' of 'str' objects}
       16    0.000    0.000    0.000    0.000 optparse.py:700(_check_nargs)
        7    0.000    0.000    0.000    0.000 _weakrefset.py:70(__contains__)
        1    0.000    0.000    0.000    0.000 threading.py:1035(setDaemon)
        3    0.000    0.000    0.000    0.000 UserDict.py:18(__getitem__)
       16    0.000    0.000    0.000    0.000 {filter}
        6    0.000    0.000    0.000    0.000 {thread.allocate_lock}
        1    0.000    0.000    0.000    0.000 ImageMode.py:17(<module>)
        1    0.000    0.000    0.000    0.000 {posix.lstat}
        4    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
        1    0.000    0.000    0.000    0.000 threading.py:422(_Semaphore)
        1    0.000    0.000    0.000    0.000 _binary.py:14(<module>)
        1    0.000    0.000    0.000    0.000 {_ctypes.dlopen}
        5    0.000    0.000    0.000    0.000 sre_parse.py:195(match)
        7    0.000    0.000    0.000    0.000 {min}
       16    0.000    0.000    0.000    0.000 optparse.py:666(_check_choice)
       16    0.000    0.000    0.000    0.000 optparse.py:630(_check_action)
       16    0.000    0.000    0.000    0.000 optparse.py:709(_check_callback)
       38    0.000    0.000    0.000    0.000 {_ctypes.sizeof}
        3    0.000    0.000    0.000    0.000 threading.py:63(_note)
        1    0.000    0.000    0.000    0.000 {binascii.hexlify}
        2    0.000    0.000    0.000    0.000 threading.py:299(_is_owned)
        1    0.000    0.000    0.000    0.000 threading.py:1024(daemon)
        1    0.000    0.000    0.000    0.000 ImageFont.py:120(FreeTypeFont)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:113(MaxFilter)
        1    0.000    0.000    0.000    0.000 {sorted}
        1    0.000    0.000    0.000    0.000 threading.py:254(_Condition)
        1    0.000    0.000    0.000    0.000 collections.py:387(Counter)
        7    0.000    0.000    0.000    0.000 __future__.py:75(__init__)
        1    0.000    0.000    0.000    0.000 {math.sqrt}
        1    0.000    0.000    0.000    0.000 optparse.py:1243(_create_option_list)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
        1    0.000    0.000    0.000    0.000 {posix.getcwd}
        1    0.000    0.000    0.000    0.000 random.py:800(SystemRandom)
        8    0.000    0.000    0.000    0.000 sre_compile.py:24(_identityfunction)
       23    0.000    0.000    0.000    0.000 {ord}
        4    0.000    0.000    0.000    0.000 _weakrefset.py:52(_commit_removals)
        1    0.000    0.000    0.000    0.000 __init__.py:14(<module>)
        1    0.000    0.000    0.000    0.000 {_ctypes.set_conversion_mode}
        2    0.000    0.000    0.000    0.000 {method 'setter' of 'property' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:332(CDLL)
        3    0.000    0.000    0.000    0.000 __init__.py:494(CFunctionType)
        1    0.000    0.000    0.000    0.000 threading.py:124(_RLock)
        1    0.000    0.000    0.000    0.000 random.py:650(WichmannHill)
        3    0.000    0.000    0.000    0.000 posixpath.py:59(isabs)
        6    0.000    0.000    0.000    0.000 {method 'translate' of 'str' objects}
        1    0.000    0.000    0.000    0.000 optparse.py:1362(_get_args)
        1    0.000    0.000    0.000    0.000 ImageStat.py:29(Stat)
        6    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 threading.py:1058(_Timer)
        7    0.000    0.000    0.000    0.000 optparse.py:1675(_match_abbrev)
        1    0.000    0.000    0.000    0.000 threading.py:293(_release_save)
        1    0.000    0.000    0.000    0.000 optparse.py:225(set_parser)
        1    0.000    0.000    0.000    0.000 threading.py:551(_Event)
        1    0.000    0.000    0.000    0.000 threading.py:789(_set_ident)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:66(Brightness)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:127(ModeFilter)
        1    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:233(c_byte)
        1    0.000    0.000    0.000    0.000 __init__.py:359(_FuncPtr)
        7    0.000    0.000    0.000    0.000 optparse.py:753(takes_value)
        2    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:78(Sharpness)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:243(SMOOTH)
        1    0.000    0.000    0.000    0.000 optparse.py:1268(_init_parsing_state)
        1    0.000    0.000    0.000    0.000 ImageMode.py:22(ModeDescriptor)
        6    0.000    0.000    0.000    0.000 {globals}
        1    0.000    0.000    0.000    0.000 _endian.py:26(_swapped_meta)
        1    0.000    0.000    0.000    0.000 {method 'keys' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {method 'copy' of 'dict' objects}
        3    0.000    0.000    0.000    0.000 {thread.get_ident}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:62(RankFilter)
        1    0.000    0.000    0.000    0.000 __init__.py:243(c_char_p)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:198(DETAIL)
        1    0.000    0.000    0.000    0.000 optparse.py:960(set_conflict_handler)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha384}
        1    0.000    0.000    0.000    0.000 numbers.py:13(Number)
        1    0.000    0.000    0.000    0.000 threading.py:1008(daemon)
        1    0.000    0.000    0.000    0.000 optparse.py:944(_create_option_mappings)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:225(EMBOSS)
        4    0.000    0.000    0.000    0.000 {method '__subclasses__' of 'type' objects}
        1    0.000    0.000    0.000    0.000 threading.py:514(_BoundedSemaphore)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:159(UnsharpMask)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:25(Kernel)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha512}
        2    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:85(MedianFilter)
        1    0.000    0.000    0.000    0.000 threading.py:56(_Verbose)
        1    0.000    0.000    0.000    0.000 __init__.py:197(c_float)
        1    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        4    0.000    0.000    0.000    0.000 _weakrefset.py:16(__init__)
        1    0.000    0.000    0.000    0.000 __init__.py:201(c_double)
       12    0.000    0.000    0.000    0.000 {_sre.getlower}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:21(Filter)
        1    0.000    0.000    0.000    0.000 optparse.py:1313(_get_all_options)
        1    0.000    0.000    0.000    0.000 __future__.py:74(_Feature)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:24(_Enhance)
        2    0.000    0.000    0.000    0.000 __init__.py:429(__init__)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:207(EDGE_ENHANCE)
        3    0.000    0.000    0.000    0.000 {method 'release' of 'thread.lock' objects}
        3    0.000    0.000    0.000    0.000 sre_parse.py:90(__init__)
        1    0.000    0.000    0.000    0.000 ImageFont.py:166(TransposedFont)
        1    0.000    0.000    0.000    0.000 stat.py:55(S_ISLNK)
        2    0.000    0.000    0.000    0.000 UserDict.py:70(__contains__)
        1    0.000    0.000    0.000    0.000 ImageFont.py:65(ImageFont)
        1    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}
        4    0.000    0.000    0.000    0.000 {method 'remove' of 'set' objects}
        1    0.000    0.000    0.000    0.000 _endian.py:49(BigEndianStructure)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:263(SHARPEN)
        3    0.000    0.000    0.000    0.000 __init__.py:104(CFunctionType)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:216(EDGE_ENHANCE_MORE)
        1    0.000    0.000    0.000    0.000 Image.py:417(_E)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:40(Color)
        1    0.000    0.000    0.000    0.000 __init__.py:428(LibraryLoader)
        1    0.000    0.000    0.000    0.000 __init__.py:159(py_object)
        1    0.000    0.000    0.000    0.000 __init__.py:388(PyDLL)
        1    0.000    0.000    0.000    0.000 ImageFont.py:39(_imagingft_not_installed)
        6    0.000    0.000    0.000    0.000 {method 'reverse' of 'list' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:193(c_uint)
        1    0.000    0.000    0.000    0.000 __init__.py:294(c_wchar)
        1    0.000    0.000    0.000    0.000 {method 'partition' of 'str' objects}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:145(GaussianBlur)
        1    0.000    0.000    0.000    0.000 Image.py:1749(ImageTransformHandler)
        1    0.000    0.000    0.000    0.000 __init__.py:180(c_ulong)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha224}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:57(BuiltinFilter)
        1    0.000    0.000    0.000    0.000 __init__.py:189(c_int)
        1    0.000    0.000    0.000    0.000 threading.py:1128(_DummyThread)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha1}
        1    0.000    0.000    0.000    0.000 optparse.py:756(get_opt_string)
        1    0.000    0.000    0.000    0.000 __init__.py:291(c_wchar_p)
        1    0.000    0.000    0.000    0.000 __init__.py:238(c_char)
        2    0.000    0.000    0.000    0.000 {method 'clear' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:172(c_ushort)
        1    0.000    0.000    0.000    0.000 __init__.py:205(c_longdouble)
        4    0.000    0.000    0.000    0.000 {method '__subclasshook__' of 'object' objects}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:252(SMOOTH_MORE)
        1    0.000    0.000    0.000    0.000 stat.py:24(S_IFMT)
        1    0.000    0.000    0.000    0.000 {max}
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha256}
        1    0.000    0.000    0.000    0.000 threading.py:1088(_MainThread)
        1    0.000    0.000    0.000    0.000 Image.py:1745(ImagePointHandler)
        1    0.000    0.000    0.000    0.000 __init__.py:176(c_long)
        1    0.000    0.000    0.000    0.000 __init__.py:260(c_bool)
        1    0.000    0.000    0.000    0.000 threading.py:1097(_set_daemon)
        2    0.000    0.000    0.000    0.000 threading.py:569(isSet)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:189(CONTOUR)
        1    0.000    0.000    0.000    0.000 optparse.py:965(set_description)
        1    0.000    0.000    0.000    0.000 optparse.py:1407(check_values)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:178(BLUR)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:234(FIND_EDGES)
        1    0.000    0.000    0.000    0.000 __init__.py:168(c_short)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:53(Contrast)
        1    0.000    0.000    0.000    0.000 __init__.py:255(c_void_p)
        1    0.000    0.000    0.000    0.000 __init__.py:226(c_ubyte)
        1    0.000    0.000    0.000    0.000 Image.py:33(_imaging_not_installed)
        1    0.000    0.000    0.000    0.000 Image.py:1710(_ImageCrop)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:99(MinFilter)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



real	3m5.262s
user	3m1.194s
sys	0m4.044s

And 1000 on 4 threads

reedy@terbium:~$ time python -m cProfile -s cumtime 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '1000' --dirs '0' --wordlist '/etc/fancycaptcha/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/etc/fancycaptcha/badwords' --threads 4
Generating 1000 CAPTCHA images separated in 250 image(s) per chunk run by 4 threads...
         307233 function calls (307232 primitive calls) in 148.558 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.069    0.069  148.558  148.558 captcha-old.py:28(<module>)
     1384  147.918    0.107  147.918    0.107 {time.sleep}
     1385    0.483    0.000    0.484    0.000 threading.py:1167(activeCount)
        2    0.033    0.016    0.062    0.031 captcha-old.py:175(read_wordlist)
   100054    0.013    0.000    0.013    0.000 {method 'lower' of 'str' objects}
   100048    0.009    0.000    0.009    0.000 {method 'strip' of 'str' objects}
   102088    0.007    0.000    0.007    0.000 {len}
        4    0.000    0.000    0.007    0.002 threading.py:726(start)
        4    0.000    0.000    0.007    0.002 threading.py:602(wait)
        4    0.000    0.000    0.006    0.002 threading.py:308(wait)
       22    0.006    0.000    0.006    0.000 {method 'acquire' of 'thread.lock' objects}
        2    0.006    0.003    0.006    0.003 {method 'readlines' of 'file' objects}
        1    0.001    0.001    0.005    0.005 Image.py:1(<module>)
        1    0.001    0.001    0.004    0.004 Image.py:27(<module>)
        5    0.000    0.000    0.003    0.001 threading.py:656(__init__)
        4    0.003    0.001    0.003    0.001 threading.py:709(_set_daemon)
        1    0.000    0.000    0.002    0.002 {__import__}
        1    0.000    0.000    0.002    0.002 FixTk.py:1(<module>)
        1    0.001    0.001    0.002    0.002 __init__.py:4(<module>)
        1    0.001    0.001    0.001    0.001 threading.py:1(<module>)
        1    0.000    0.000    0.001    0.001 random.py:40(<module>)
        1    0.000    0.000    0.001    0.001 ImageEnhance.py:1(<module>)
        1    0.000    0.000    0.001    0.001 ImageEnhance.py:21(<module>)
        1    0.000    0.000    0.001    0.001 numbers.py:6(<module>)
        1    0.001    0.001    0.001    0.001 hashlib.py:55(<module>)
        1    0.000    0.000    0.000    0.000 ImageFont.py:1(<module>)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:18(<module>)
        1    0.000    0.000    0.000    0.000 optparse.py:1191(__init__)
       16    0.000    0.000    0.000    0.000 optparse.py:1008(add_option)
        3    0.000    0.000    0.000    0.000 re.py:188(compile)
        3    0.000    0.000    0.000    0.000 re.py:226(_compile)
        1    0.000    0.000    0.000    0.000 collections.py:1(<module>)
        5    0.000    0.000    0.000    0.000 abc.py:86(__new__)
        3    0.000    0.000    0.000    0.000 sre_compile.py:493(compile)
        1    0.000    0.000    0.000    0.000 ImageFont.py:28(<module>)
        2    0.000    0.000    0.000    0.000 gettext.py:580(gettext)
        2    0.000    0.000    0.000    0.000 gettext.py:542(dgettext)
        2    0.000    0.000    0.000    0.000 gettext.py:476(translation)
        2    0.000    0.000    0.000    0.000 gettext.py:421(find)
       16    0.000    0.000    0.000    0.000 optparse.py:561(__init__)
        1    0.000    0.000    0.000    0.000 warnings.py:45(filterwarnings)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:1(<module>)
        1    0.000    0.000    0.000    0.000 __init__.py:265(_reset_cache)
        1    0.000    0.000    0.000    0.000 optparse.py:1258(_populate_option_list)
        1    0.000    0.000    0.000    0.000 optparse.py:1248(_add_help_option)
        1    0.000    0.000    0.000    0.000 optparse.py:1277(set_usage)
        3    0.000    0.000    0.000    0.000 sre_compile.py:478(_code)
        3    0.000    0.000    0.000    0.000 sre_parse.py:675(parse)
       10    0.000    0.000    0.000    0.000 threading.py:241(Condition)
        1    0.000    0.000    0.000    0.000 optparse.py:1368(parse_args)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:33(<module>)
        4    0.000    0.000    0.000    0.000 {thread.start_new_thread}
        5    0.000    0.000    0.000    0.000 threading.py:541(Event)
        3    0.000    0.000    0.000    0.000 sre_parse.py:301(_parse_sub)
        5    0.000    0.000    0.000    0.000 threading.py:560(__init__)
        3    0.000    0.000    0.000    0.000 sre_parse.py:379(_parse)
       23    0.000    0.000    0.000    0.000 genericpath.py:15(exists)
        1    0.000    0.000    0.000    0.000 heapq.py:31(<module>)
        1    0.000    0.000    0.000    0.000 ImageOps.py:1(<module>)
       10    0.000    0.000    0.000    0.000 threading.py:259(__init__)
       14    0.000    0.000    0.000    0.000 __init__.py:147(_check_size)
        4    0.000    0.000    0.000    0.000 abc.py:105(register)
        3    0.000    0.000    0.000    0.000 sre_compile.py:359(_compile_info)
        5    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x91d400}
       16    0.000    0.000    0.000    0.000 optparse.py:610(_set_attrs)
        1    0.000    0.000    0.000    0.000 optparse.py:1420(_process_args)
       25    0.000    0.000    0.000    0.000 posixpath.py:68(join)
     10/9    0.000    0.000    0.000    0.000 {issubclass}
        3    0.000    0.000    0.000    0.000 __init__.py:78(CFUNCTYPE)
        8    0.000    0.000    0.000    0.000 optparse.py:1480(_process_long_opt)
        4    0.000    0.000    0.000    0.000 abc.py:148(__subclasscheck__)
        2    0.000    0.000    0.000    0.000 sre_compile.py:178(_compile_charset)
        6    0.000    0.000    0.000    0.000 gettext.py:130(_expand_lang)
        1    0.000    0.000    0.000    0.000 threading.py:1090(__init__)
       23    0.000    0.000    0.000    0.000 {posix.stat}
        2    0.000    0.000    0.000    0.000 sre_compile.py:207(_optimize_charset)
        1    0.000    0.000    0.000    0.000 _endian.py:4(<module>)
        3    0.000    0.000    0.000    0.000 sre_compile.py:32(_compile)
        4    0.000    0.000    0.000    0.000 threading.py:296(_acquire_restore)
        3    0.000    0.000    0.000    0.000 __init__.py:493(PYFUNCTYPE)
      215    0.000    0.000    0.000    0.000 {getattr}
       46    0.000    0.000    0.000    0.000 abc.py:89(<genexpr>)
        1    0.000    0.000    0.000    0.000 random.py:91(__init__)
        1    0.000    0.000    0.000    0.000 random.py:100(seed)
       32    0.000    0.000    0.000    0.000 sre_parse.py:201(get)
        1    0.000    0.000    0.000    0.000 optparse.py:1319(get_default_values)
        8    0.000    0.000    0.000    0.000 optparse.py:779(process)
      188    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        2    0.000    0.000    0.000    0.000 {_ctypes.POINTER}
        1    0.000    0.000    0.000    0.000 ImageStat.py:24(<module>)
       16    0.000    0.000    0.000    0.000 optparse.py:636(_check_type)
       37    0.000    0.000    0.000    0.000 sre_parse.py:182(__next)
        1    0.000    0.000    0.000    0.000 posixpath.py:379(realpath)
       16    0.000    0.000    0.000    0.000 optparse.py:589(_set_opt_strings)
        1    0.000    0.000    0.000    0.000 __init__.py:349(__init__)
        2    0.000    0.000    0.000    0.000 posixpath.py:365(abspath)
        5    0.000    0.000    0.000    0.000 _weakrefset.py:58(__iter__)
      215    0.000    0.000    0.000    0.000 {setattr}
        4    0.000    0.000    0.000    0.000 threading.py:627(_newname)
       16    0.000    0.000    0.000    0.000 _weakrefset.py:36(__init__)
        1    0.000    0.000    0.000    0.000 ImageColor.py:20(<module>)
        8    0.000    0.000    0.000    0.000 optparse.py:772(convert_value)
       16    0.000    0.000    0.000    0.000 optparse.py:679(_check_dest)
        9    0.000    0.000    0.000    0.000 optparse.py:765(check_value)
        2    0.000    0.000    0.000    0.000 posixpath.py:336(normpath)
        1    0.000    0.000    0.000    0.000 _util.py:1(<module>)
       18    0.000    0.000    0.000    0.000 {_struct.calcsize}
        6    0.000    0.000    0.000    0.000 locale.py:363(normalize)
        3    0.000    0.000    0.000    0.000 sre_parse.py:140(getwidth)
        1    0.000    0.000    0.000    0.000 posixpath.py:387(_joinrealpath)
        1    0.000    0.000    0.000    0.000 threading.py:640(Thread)
        1    0.000    0.000    0.000    0.000 {posix.urandom}
        1    0.000    0.000    0.000    0.000 numbers.py:34(Complex)
       42    0.000    0.000    0.000    0.000 {isinstance}
       20    0.000    0.000    0.000    0.000 threading.py:58(__init__)
        2    0.000    0.000    0.000    0.000 {method 'close' of 'file' objects}
        6    0.000    0.000    0.000    0.000 hashlib.py:94(__get_openssl_constructor)
       37    0.000    0.000    0.000    0.000 abc.py:15(abstractmethod)
        1    0.000    0.000    0.000    0.000 ImageOps.py:20(<module>)
       74    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        1    0.000    0.000    0.000    0.000 threading.py:575(set)
        4    0.000    0.000    0.000    0.000 threading.py:1152(currentThread)
        1    0.000    0.000    0.000    0.000 {function seed at 0x7fc930b57ed8}
        1    0.000    0.000    0.000    0.000 __future__.py:48(<module>)
        2    0.000    0.000    0.000    0.000 {open}
        1    0.000    0.000    0.000    0.000 optparse.py:366(__init__)
       16    0.000    0.000    0.000    0.000 optparse.py:580(_check_opt_strings)
       68    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
       23    0.000    0.000    0.000    0.000 sre_parse.py:138(append)
       16    0.000    0.000    0.000    0.000 optparse.py:981(_check_conflict)
        3    0.000    0.000    0.000    0.000 sre_parse.py:178(__init__)
        1    0.000    0.000    0.000    0.000 numbers.py:295(Integral)
        1    0.000    0.000    0.000    0.000 optparse.py:200(__init__)
       10    0.000    0.000    0.000    0.000 {hasattr}
        3    0.000    0.000    0.000    0.000 optparse.py:424(check_builtin)
        4    0.000    0.000    0.000    0.000 threading.py:1035(setDaemon)
        5    0.000    0.000    0.000    0.000 threading.py:299(_is_owned)
        1    0.000    0.000    0.000    0.000 Image.py:447(Image)
        1    0.000    0.000    0.000    0.000 {math.exp}
        3    0.000    0.000    0.000    0.000 optparse.py:413(_parse_int)
        4    0.000    0.000    0.000    0.000 _weakrefset.py:26(__exit__)
        9    0.000    0.000    0.000    0.000 {range}
        9    0.000    0.000    0.000    0.000 threading.py:63(_note)
        1    0.000    0.000    0.000    0.000 threading.py:399(notifyAll)
       16    0.000    0.000    0.000    0.000 optparse.py:833(isbasestring)
        9    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 posixpath.py:139(islink)
       66    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
        1    0.000    0.000    0.000    0.000 optparse.py:838(__init__)
        1    0.000    0.000    0.000    0.000 collections.py:26(OrderedDict)
       15    0.000    0.000    0.000    0.000 {method 'replace' of 'str' objects}
       16    0.000    0.000    0.000    0.000 {filter}
        4    0.000    0.000    0.000    0.000 _weakrefset.py:20(__enter__)
        8    0.000    0.000    0.000    0.000 _weakrefset.py:83(add)
        1    0.000    0.000    0.000    0.000 numbers.py:169(Real)
        2    0.000    0.000    0.000    0.000 {math.log}
        8    0.000    0.000    0.000    0.000 optparse.py:1471(_match_long_opt)
        1    0.000    0.000    0.000    0.000 random.py:72(Random)
        1    0.000    0.000    0.000    0.000 threading.py:372(notify)
        1    0.000    0.000    0.000    0.000 threading.py:422(_Semaphore)
       16    0.000    0.000    0.000    0.000 optparse.py:694(_check_const)
        1    0.000    0.000    0.000    0.000 posixpath.py:127(dirname)
        1    0.000    0.000    0.000    0.000 keyword.py:11(<module>)
       38    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 numbers.py:270(Rational)
       16    0.000    0.000    0.000    0.000 optparse.py:630(_check_action)
        8    0.000    0.000    0.000    0.000 optparse.py:791(take_action)
        3    0.000    0.000    0.000    0.000 optparse.py:400(_parse_num)
        1    0.000    0.000    0.000    0.000 optparse.py:933(__init__)
       16    0.000    0.000    0.000    0.000 optparse.py:709(_check_callback)
        2    0.000    0.000    0.000    0.000 UserDict.py:58(get)
        4    0.000    0.000    0.000    0.000 threading.py:1024(daemon)
        5    0.000    0.000    0.000    0.000 sre_parse.py:195(match)
        1    0.000    0.000    0.000    0.000 ImageDraw.py:49(ImageDraw)
       17    0.000    0.000    0.000    0.000 {method 'pop' of 'list' objects}
       15    0.000    0.000    0.000    0.000 {thread.allocate_lock}
        3    0.000    0.000    0.000    0.000 {_sre.compile}
        7    0.000    0.000    0.000    0.000 _weakrefset.py:70(__contains__)
        6    0.000    0.000    0.000    0.000 sre_compile.py:472(isstring)
        3    0.000    0.000    0.000    0.000 sre_parse.py:67(__init__)
        1    0.000    0.000    0.000    0.000 _binary.py:14(<module>)
        3    0.000    0.000    0.000    0.000 UserDict.py:18(__getitem__)
        1    0.000    0.000    0.000    0.000 ImageMode.py:17(<module>)
       16    0.000    0.000    0.000    0.000 optparse.py:700(_check_nargs)
        1    0.000    0.000    0.000    0.000 {posix.lstat}
       38    0.000    0.000    0.000    0.000 {_ctypes.sizeof}
        4    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
        4    0.000    0.000    0.000    0.000 threading.py:293(_release_save)
        1    0.000    0.000    0.000    0.000 {_ctypes.dlopen}
        1    0.000    0.000    0.000    0.000 {sorted}
        4    0.000    0.000    0.000    0.000 threading.py:1008(daemon)
        7    0.000    0.000    0.000    0.000 {min}
       18    0.000    0.000    0.000    0.000 {method 'find' of 'str' objects}
       16    0.000    0.000    0.000    0.000 optparse.py:666(_check_choice)
        1    0.000    0.000    0.000    0.000 collections.py:387(Counter)
        1    0.000    0.000    0.000    0.000 {binascii.hexlify}
        1    0.000    0.000    0.000    0.000 ImageFont.py:120(FreeTypeFont)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:113(MaxFilter)
        2    0.000    0.000    0.000    0.000 {method 'setter' of 'property' objects}
        3    0.000    0.000    0.000    0.000 __init__.py:494(CFunctionType)
        3    0.000    0.000    0.000    0.000 posixpath.py:59(isabs)
        1    0.000    0.000    0.000    0.000 {math.sqrt}
        1    0.000    0.000    0.000    0.000 ImageStat.py:29(Stat)
        1    0.000    0.000    0.000    0.000 optparse.py:1243(_create_option_list)
        8    0.000    0.000    0.000    0.000 optparse.py:1675(_match_abbrev)
        1    0.000    0.000    0.000    0.000 threading.py:551(_Event)
        1    0.000    0.000    0.000    0.000 threading.py:789(_set_ident)
        8    0.000    0.000    0.000    0.000 optparse.py:753(takes_value)
        2    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {posix.getcwd}
        8    0.000    0.000    0.000    0.000 sre_compile.py:24(_identityfunction)
        6    0.000    0.000    0.000    0.000 {thread.get_ident}
        4    0.000    0.000    0.000    0.000 _weakrefset.py:52(_commit_removals)
        1    0.000    0.000    0.000    0.000 {_ctypes.set_conversion_mode}
        1    0.000    0.000    0.000    0.000 __init__.py:332(CDLL)
        4    0.000    0.000    0.000    0.000 _weakrefset.py:16(__init__)
        1    0.000    0.000    0.000    0.000 threading.py:254(_Condition)
       12    0.000    0.000    0.000    0.000 {_sre.getlower}
        1    0.000    0.000    0.000    0.000 threading.py:124(_RLock)
        1    0.000    0.000    0.000    0.000 random.py:650(WichmannHill)
        8    0.000    0.000    0.000    0.000 threading.py:569(isSet)
        1    0.000    0.000    0.000    0.000 __future__.py:74(_Feature)
        9    0.000    0.000    0.000    0.000 {method 'release' of 'thread.lock' objects}
        3    0.000    0.000    0.000    0.000 sre_parse.py:90(__init__)
        6    0.000    0.000    0.000    0.000 {method 'translate' of 'str' objects}
        7    0.000    0.000    0.000    0.000 __future__.py:75(__init__)
        3    0.000    0.000    0.000    0.000 __init__.py:104(CFunctionType)
        1    0.000    0.000    0.000    0.000 optparse.py:1362(_get_args)
        1    0.000    0.000    0.000    0.000 __init__.py:159(py_object)
        1    0.000    0.000    0.000    0.000 __init__.py:193(c_uint)
        6    0.000    0.000    0.000    0.000 {method 'pop' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 threading.py:1058(_Timer)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:145(GaussianBlur)
        1    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:359(_FuncPtr)
        1    0.000    0.000    0.000    0.000 __init__.py:180(c_ulong)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha224}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:57(BuiltinFilter)
        1    0.000    0.000    0.000    0.000 __init__.py:189(c_int)
        1    0.000    0.000    0.000    0.000 optparse.py:1268(_init_parsing_state)
        1    0.000    0.000    0.000    0.000 __init__.py:238(c_char)
        1    0.000    0.000    0.000    0.000 random.py:800(SystemRandom)
        1    0.000    0.000    0.000    0.000 {method 'copy' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:205(c_longdouble)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:62(RankFilter)
        1    0.000    0.000    0.000    0.000 __init__.py:243(c_char_p)
        1    0.000    0.000    0.000    0.000 __init__.py:14(<module>)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:198(DETAIL)
        1    0.000    0.000    0.000    0.000 optparse.py:960(set_conflict_handler)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha384}
        1    0.000    0.000    0.000    0.000 optparse.py:944(_create_option_mappings)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:225(EMBOSS)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha256}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:159(UnsharpMask)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:25(Kernel)
        2    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 threading.py:1088(_MainThread)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:85(MedianFilter)
        1    0.000    0.000    0.000    0.000 threading.py:56(_Verbose)
        1    0.000    0.000    0.000    0.000 __init__.py:197(c_float)
        1    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:176(c_long)
        1    0.000    0.000    0.000    0.000 __init__.py:201(c_double)
        1    0.000    0.000    0.000    0.000 threading.py:1097(_set_daemon)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:189(CONTOUR)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:24(_Enhance)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:207(EDGE_ENHANCE)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:178(BLUR)
        1    0.000    0.000    0.000    0.000 ImageFont.py:166(TransposedFont)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:234(FIND_EDGES)
        1    0.000    0.000    0.000    0.000 stat.py:55(S_ISLNK)
        1    0.000    0.000    0.000    0.000 __init__.py:168(c_short)
        2    0.000    0.000    0.000    0.000 UserDict.py:70(__contains__)
        1    0.000    0.000    0.000    0.000 ImageFont.py:65(ImageFont)
        1    0.000    0.000    0.000    0.000 __init__.py:255(c_void_p)
        4    0.000    0.000    0.000    0.000 {method 'remove' of 'set' objects}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:263(SHARPEN)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:216(EDGE_ENHANCE_MORE)
        1    0.000    0.000    0.000    0.000 Image.py:33(_imaging_not_installed)
        1    0.000    0.000    0.000    0.000 Image.py:417(_E)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:99(MinFilter)
        1    0.000    0.000    0.000    0.000 __init__.py:428(LibraryLoader)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:388(PyDLL)
        1    0.000    0.000    0.000    0.000 ImageFont.py:39(_imagingft_not_installed)
        6    0.000    0.000    0.000    0.000 {method 'reverse' of 'list' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:294(c_wchar)
        1    0.000    0.000    0.000    0.000 {method 'partition' of 'str' objects}
        1    0.000    0.000    0.000    0.000 optparse.py:225(set_parser)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:66(Brightness)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:127(ModeFilter)
        1    0.000    0.000    0.000    0.000 __init__.py:233(c_byte)
        1    0.000    0.000    0.000    0.000 Image.py:1749(ImageTransformHandler)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:78(Sharpness)
        1    0.000    0.000    0.000    0.000 threading.py:1128(_DummyThread)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:243(SMOOTH)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha1}
        1    0.000    0.000    0.000    0.000 ImageMode.py:22(ModeDescriptor)
        1    0.000    0.000    0.000    0.000 optparse.py:756(get_opt_string)
        1    0.000    0.000    0.000    0.000 __init__.py:291(c_wchar_p)
        6    0.000    0.000    0.000    0.000 {globals}
        2    0.000    0.000    0.000    0.000 {method 'clear' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 _endian.py:26(_swapped_meta)
        1    0.000    0.000    0.000    0.000 {method 'keys' of 'dict' objects}
       23    0.000    0.000    0.000    0.000 {ord}
        1    0.000    0.000    0.000    0.000 __init__.py:172(c_ushort)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:40(Color)
        4    0.000    0.000    0.000    0.000 {method '__subclasshook__' of 'object' objects}
        1    0.000    0.000    0.000    0.000 ImageFilter.py:252(SMOOTH_MORE)
        1    0.000    0.000    0.000    0.000 stat.py:24(S_IFMT)
        1    0.000    0.000    0.000    0.000 {max}
        1    0.000    0.000    0.000    0.000 numbers.py:13(Number)
        4    0.000    0.000    0.000    0.000 {method '__subclasses__' of 'type' objects}
        1    0.000    0.000    0.000    0.000 threading.py:514(_BoundedSemaphore)
        1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha512}
        1    0.000    0.000    0.000    0.000 Image.py:1745(ImagePointHandler)
        1    0.000    0.000    0.000    0.000 __init__.py:260(c_bool)
        1    0.000    0.000    0.000    0.000 ImageFilter.py:21(Filter)
        1    0.000    0.000    0.000    0.000 optparse.py:1313(_get_all_options)
        1    0.000    0.000    0.000    0.000 optparse.py:965(set_description)
        2    0.000    0.000    0.000    0.000 __init__.py:429(__init__)
        1    0.000    0.000    0.000    0.000 optparse.py:1407(check_values)
        1    0.000    0.000    0.000    0.000 ImageEnhance.py:53(Contrast)
        1    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:226(c_ubyte)
        1    0.000    0.000    0.000    0.000 _endian.py:49(BigEndianStructure)
        1    0.000    0.000    0.000    0.000 Image.py:1710(_ImageCrop)



real	2m28.609s
user	3m59.963s
sys	0m22.756s

This method of profiling, used as is... Doesn't give us anything very useful

If we follow https://stackoverflow.com/questions/21274898/python-getting-meaningful-results-from-cprofile and switch to doing it in code... And only in the run_in_thread

1800    7.072    0.004    7.072    0.004 {method 'rotate' of 'ImagingCore' objects}
2700    5.284    0.002    5.284    0.002 {method 'filter' of 'ImagingCore' objects}

seem to be taking most of the time...

1 thread:

reedy@terbium:~$ time python 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '100' --dirs '0' --wordlist '/etc/fancycaptcha/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/etc/fancycaptcha/badwords'
Generating 100 CAPTCHA images separated in 100 image(s) per chunk run by 1 threads...
         3721397 function calls (3721379 primitive calls) in 17.073 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1800    7.072    0.004    7.072    0.004 {method 'rotate' of 'ImagingCore' objects}
     2700    5.284    0.002    5.284    0.002 {method 'filter' of 'ImagingCore' objects}
      900    0.943    0.001    0.943    0.001 {PIL._imaging.blend}
   149210    0.530    0.000    1.552    0.000 Image.py:1046(paste)
      900    0.486    0.001   16.733    0.019 captcha-old.py:52(wobbly_copy)
   149310    0.204    0.000    0.204    0.000 {method 'crop' of 'ImagingCore' objects}
     2700    0.184    0.000    0.184    0.000 {method 'getband' of 'ImagingCore' objects}
   149310    0.181    0.000    0.290    0.000 Image.py:1712(__init__)
   149310    0.181    0.000    0.419    0.000 Image.py:1729(load)
   448632    0.173    0.000    0.173    0.000 {hasattr}
     2700    0.173    0.000    0.173    0.000 {method 'putband' of 'ImagingCore' objects}
   307020    0.164    0.000    0.232    0.000 Image.py:628(load)
   149310    0.158    0.000    0.571    0.000 Image.py:785(crop)
   447630    0.134    0.000    0.307    0.000 Image.py:104(isImageType)
      100    0.132    0.001    0.132    0.001 {method 'encode' of 'ImagingEncoder' objects}
   157710    0.119    0.000    0.119    0.000 Image.py:461(__init__)
   456330    0.102    0.000    0.102    0.000 {method 'pixel_access' of 'ImagingCore' objects}
     1000    0.096    0.000    0.096    0.000 {method 'getbbox' of 'ImagingCore' objects}
      900    0.085    0.000    0.085    0.000 {PIL._imaging.new}
   149210    0.082    0.000    0.082    0.000 {method 'paste' of 'ImagingCore' objects}
   151910    0.080    0.000    0.095    0.000 random.py:356(uniform)
     1100    0.060    0.000    0.060    0.000 {PIL._imaging.fill}
      100    0.058    0.001   17.063    0.171 captcha-old.py:77(gen_captcha)
151742/151741    0.054    0.000    0.058    0.000 {isinstance}
   150310    0.049    0.000    0.102    0.000 _util.py:4(isStringType)
   149210    0.044    0.000    0.044    0.000 {math.sin}
      100    0.043    0.000    0.043    0.000 {method 'render' of 'Font' objects}
     7400    0.025    0.000    0.036    0.000 Image.py:472(_new)
      200    0.024    0.000    0.024    0.000 {method 'getsize' of 'Font' objects}
152767/152765    0.019    0.000    0.019    0.000 {len}
   152276    0.016    0.000    0.016    0.000 {method 'random' of '_random.Random' objects}
      900    0.013    0.000    5.784    0.006 Image.py:831(filter)
      900    0.010    0.000    0.280    0.000 Image.py:2104(merge)
      100    0.007    0.000    0.007    0.000 {method 'draw_bitmap' of 'ImagingDraw' objects}
      133    0.006    0.000    0.007    0.000 captcha-old.py:131(try_pick_word)
     1800    0.006    0.000    7.089    0.004 Image.py:1338(rotate)
      100    0.004    0.000    0.004    0.000 {PIL._imagingft.getfont}
     2700    0.004    0.000    5.288    0.002 ImageFilter.py:51(filter)
     3001    0.004    0.000    0.004    0.000 {range}
      100    0.004    0.000    0.013    0.000 ImageOps.py:353(invert)
     1000    0.004    0.000    0.072    0.000 Image.py:1765(new)
     1000    0.004    0.000    0.004    0.000 {method 'write' of 'file' objects}
      600    0.004    0.000    0.004    0.000 {PIL._imaging.crc32}
      900    0.004    0.000    5.788    0.006 ImageEnhance.py:85(__init__)
      100    0.003    0.000    0.003    0.000 {method 'point' of 'ImagingCore' objects}
      100    0.003    0.000    0.003    0.000 {open}
      900    0.002    0.000    0.952    0.001 Image.py:2048(blend)
    28371    0.002    0.000    0.002    0.000 {method 'append' of 'list' objects}
     1800    0.002    0.000    0.003    0.000 Image.py:300(getmodebands)
      900    0.002    0.000    0.003    0.000 abc.py:128(__instancecheck__)
      100    0.002    0.000    0.143    0.001 ImageFile.py:435(_save)
      100    0.002    0.000    0.150    0.001 PngImagePlugin.py:493(_save)
      300    0.001    0.000    0.011    0.000 PngImagePlugin.py:474(putchunk)
     3600    0.001    0.000    0.001    0.000 ImageMode.py:36(getmode)
     7400    0.001    0.000    0.001    0.000 {method 'copy' of 'dict' objects}
      100    0.001    0.000    0.001    0.000 {method 'close' of 'file' objects}
     1000    0.001    0.000    0.099    0.000 Image.py:864(getbbox)
      910    0.001    0.000    0.001    0.000 _weakrefset.py:70(__contains__)
     3200    0.001    0.000    0.002    0.000 _binary.py:18(o8)
      900    0.001    0.000    0.953    0.001 ImageEnhance.py:26(enhance)
      100    0.001    0.000    0.159    0.002 Image.py:1398(save)
      500    0.001    0.000    0.002    0.000 _binary.py:50(o32be)
     1800    0.001    0.000    0.001    0.000 Image.py:273(getmodetype)
      100    0.001    0.000    0.001    0.000 {method 'flush' of 'file' objects}
      366    0.001    0.000    0.001    0.000 random.py:173(randrange)
      100    0.001    0.000    0.009    0.000 captcha-old.py:169(pick_word)
      100    0.001    0.000    0.002    0.000 ImageDraw.py:293(Draw)
     3200    0.001    0.000    0.001    0.000 {chr}
      100    0.001    0.000    0.008    0.000 ImageOps.py:44(_lut)
      100    0.001    0.000    0.003    0.000 Image.py:316(preinit)
     1020    0.001    0.000    0.001    0.000 {getattr}
      100    0.000    0.000    0.001    0.000 ImageDraw.py:61(__init__)
      600    0.000    0.000    0.001    0.000 _binary.py:47(o16be)
        1    0.000    0.000    0.001    0.001 io.py:34(<module>)
      100    0.000    0.000    0.007    0.000 Image.py:1130(point)
      100    0.000    0.000    0.062    0.001 ImageDraw.py:258(text)
      100    0.000    0.000    0.054    0.001 ImageFont.py:151(getmask2)
      100    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
      100    0.000    0.000    0.000    0.000 genericpath.py:85(_splitext)
      133    0.000    0.000    0.000    0.000 {method 'search' of '_sre.SRE_Pattern' objects}
      100    0.000    0.000    0.000    0.000 {method 'sort' of 'list' objects}
      100    0.000    0.000    0.001    0.000 Image.py:394(_getencoder)
      100    0.000    0.000    0.000    0.000 posixpath.py:68(join)
      100    0.000    0.000    0.005    0.000 ImageFont.py:200(truetype)
      266    0.000    0.000    0.001    0.000 random.py:236(randint)
      100    0.000    0.000    0.000    0.000 {PIL._imaging.zip_encoder}
      300    0.000    0.000    0.000    0.000 _util.py:6(isPath)
        1    0.000    0.000    0.001    0.001 BmpImagePlugin.py:27(<module>)
      100    0.000    0.000    0.000    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
      100    0.000    0.000    0.005    0.000 ImageFont.py:123(__init__)
      200    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
      200    0.000    0.000    0.000    0.000 {max}
      100    0.000    0.000    0.014    0.000 ImageFont.py:142(getsize)
      100    0.000    0.000    0.000    0.000 ImageDraw.py:137(_getink)
      302    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
      900    0.000    0.000    0.000    0.000 ImageFilter.py:58(__init__)
        1    0.000    0.000    0.000    0.000 ascii.py:8(<module>)
      100    0.000    0.000    0.000    0.000 Image.py:519(__getattr__)
      100    0.000    0.000    0.000    0.000 {method 'setimage' of 'ImagingEncoder' objects}
      100    0.000    0.000    0.001    0.000 posixpath.py:104(splitext)
      208    0.000    0.000    0.000    0.000 {min}
        1    0.000    0.000    0.000    0.000 GifImagePlugin.py:28(<module>)
      100    0.000    0.000    0.008    0.000 PngImagePlugin.py:490(write)
        1    0.000    0.000    0.001    0.001 ImageFile.py:30(<module>)
      614    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
      200    0.000    0.000    0.000    0.000 {method 'draw_ink' of 'ImagingDraw' objects}
      100    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:34(<module>)
        1    0.000    0.000    0.000    0.000 JpegImagePlugin.py:35(<module>)
        9    0.000    0.000    0.000    0.000 abc.py:148(__subclasscheck__)
        5    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x91d400}
      100    0.000    0.000    0.000    0.000 PngImagePlugin.py:487(__init__)
        1    0.000    0.000    0.000    0.000 {__import__}
        2    0.000    0.000    0.000    0.000 sre_parse.py:379(_parse)
        1    0.000    0.000    0.000    0.000 PpmImagePlugin.py:18(<module>)
      100    0.000    0.000    0.000    0.000 {PIL._imaging.draw}
      100    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
        4    0.000    0.000    0.000    0.000 abc.py:86(__new__)
      110    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
      4/2    0.000    0.000    0.000    0.000 sre_compile.py:32(_compile)
       20    0.000    0.000    0.000    0.000 _weakrefset.py:36(__init__)
      100    0.000    0.000    0.000    0.000 ImageFile.py:62(_tilesort)
        1    0.000    0.000    0.000    0.000 ImagePalette.py:19(<module>)
      123    0.000    0.000    0.000    0.000 {method 'upper' of 'str' objects}
       19    0.000    0.000    0.000    0.000 _weakrefset.py:58(__iter__)
      6/4    0.000    0.000    0.000    0.000 sre_parse.py:140(getwidth)
      100    0.000    0.000    0.000    0.000 {callable}
       17    0.000    0.000    0.000    0.000 _weakrefset.py:83(add)
        2    0.000    0.000    0.000    0.000 locale.py:363(normalize)
        5    0.000    0.000    0.000    0.000 sre_compile.py:178(_compile_charset)
       17    0.000    0.000    0.000    0.000 sre_parse.py:182(__next)
        2    0.000    0.000    0.000    0.000 sre_compile.py:359(_compile_info)
        8    0.000    0.000    0.000    0.000 abc.py:105(register)
    27/16    0.000    0.000    0.000    0.000 {issubclass}
        2    0.000    0.000    0.000    0.000 re.py:226(_compile)
        2    0.000    0.000    0.000    0.000 sre_compile.py:493(compile)
        1    0.000    0.000    0.000    0.000 __init__.py:71(search_function)
        1    0.000    0.000    0.000    0.000 locale.py:493(getdefaultlocale)
        2    0.000    0.000    0.000    0.000 sre_parse.py:675(parse)
        5    0.000    0.000    0.000    0.000 Image.py:2131(register_open)
        1    0.000    0.000    0.000    0.000 JpegPresets.py:67(<module>)
        5    0.000    0.000    0.000    0.000 sre_compile.py:207(_optimize_charset)
       12    0.000    0.000    0.000    0.000 sre_parse.py:130(__getitem__)
        1    0.000    0.000    0.000    0.000 {method 'encode' of 'unicode' objects}
        8    0.000    0.000    0.000    0.000 _weakrefset.py:26(__exit__)
       10    0.000    0.000    0.000    0.000 Image.py:2168(register_extension)
       15    0.000    0.000    0.000    0.000 sre_parse.py:201(get)
        2    0.000    0.000    0.000    0.000 sre_parse.py:301(_parse_sub)
        1    0.000    0.000    0.000    0.000 _abcoll.py:24(_hasattr)
        4    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
        8    0.000    0.000    0.000    0.000 _weakrefset.py:20(__enter__)
        1    0.000    0.000    0.000    0.000 locale.py:347(_replace_encoding)
        8    0.000    0.000    0.000    0.000 _weakrefset.py:52(_commit_removals)
        7    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        2    0.000    0.000    0.000    0.000 {_sre.compile}
        5    0.000    0.000    0.000    0.000 Image.py:2157(register_save)
        2    0.000    0.000    0.000    0.000 __init__.py:49(normalize_encoding)
       25    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
        4    0.000    0.000    0.000    0.000 sre_compile.py:472(isstring)
        1    0.000    0.000    0.000    0.000 codecs.py:77(__new__)
        4    0.000    0.000    0.000    0.000 abc.py:89(<genexpr>)
       16    0.000    0.000    0.000    0.000 ImageMode.py:24(__init__)
        1    0.000    0.000    0.000    0.000 {_codecs.utf_8_decode}
        1    0.000    0.000    0.000    0.000 ascii.py:41(getregentry)
        2    0.000    0.000    0.000    0.000 locale.py:447(_parse_localename)
        2    0.000    0.000    0.000    0.000 _abcoll.py:26(<genexpr>)
        9    0.000    0.000    0.000    0.000 sre_parse.py:138(append)
        1    0.000    0.000    0.000    0.000 _abcoll.py:128(__subclasshook__)
        2    0.000    0.000    0.000    0.000 sre_parse.py:178(__init__)
        1    0.000    0.000    0.000    0.000 {method 'decode' of 'str' objects}
        2    0.000    0.000    0.000    0.000 sre_compile.py:478(_code)
        2    0.000    0.000    0.000    0.000 sre_compile.py:354(_simple)
        8    0.000    0.000    0.000    0.000 _weakrefset.py:16(__init__)
        3    0.000    0.000    0.000    0.000 UserDict.py:70(__contains__)
        8    0.000    0.000    0.000    0.000 {method 'remove' of 'set' objects}
        3    0.000    0.000    0.000    0.000 UserDict.py:58(get)
        1    0.000    0.000    0.000    0.000 {any}
        8    0.000    0.000    0.000    0.000 {method '__subclasshook__' of 'object' objects}
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:78(ChunkStream)
        2    0.000    0.000    0.000    0.000 re.py:188(compile)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:179(PngStream)
        1    0.000    0.000    0.000    0.000 {_locale.setlocale}
        1    0.000    0.000    0.000    0.000 locale.py:546(getlocale)
        3    0.000    0.000    0.000    0.000 Image.py:2146(register_mime)
        1    0.000    0.000    0.000    0.000 utf_8.py:15(decode)
        4    0.000    0.000    0.000    0.000 sre_parse.py:90(__init__)
        4    0.000    0.000    0.000    0.000 sre_parse.py:257(_escape)
        1    0.000    0.000    0.000    0.000 ImagePalette.py:23(ImagePalette)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:484(_idat)
        2    0.000    0.000    0.000    0.000 sre_parse.py:67(__init__)
        1    0.000    0.000    0.000    0.000 ascii.py:13(Codec)
        1    0.000    0.000    0.000    0.000 GifImagePlugin.py:53(GifImageFile)
        1    0.000    0.000    0.000    0.000 ImageFile.py:268(StubImageFile)
        2    0.000    0.000    0.000    0.000 sre_parse.py:134(__setitem__)
        3    0.000    0.000    0.000    0.000 {method 'replace' of 'str' objects}
        3    0.000    0.000    0.000    0.000 {ord}
        1    0.000    0.000    0.000    0.000 UserDict.py:18(__getitem__)
        1    0.000    0.000    0.000    0.000 {method 'index' of 'str' objects}
        1    0.000    0.000    0.000    0.000 BmpImagePlugin.py:171(DibImageFile)
        1    0.000    0.000    0.000    0.000 JpegImagePlugin.py:267(JpegImageFile)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:341(PngImageFile)
        1    0.000    0.000    0.000    0.000 ImageFile.py:298(Parser)
        1    0.000    0.000    0.000    0.000 ascii.py:20(IncrementalEncoder)
        1    0.000    0.000    0.000    0.000 ImageFile.py:70(ImageFile)
        4    0.000    0.000    0.000    0.000 {method 'translate' of 'str' objects}
        1    0.000    0.000    0.000    0.000 io.py:69(IOBase)
        8    0.000    0.000    0.000    0.000 {method '__subclasses__' of 'type' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        8    0.000    0.000    0.000    0.000 sre_parse.py:126(__len__)
        1    0.000    0.000    0.000    0.000 io.py:78(TextIOBase)
        1    0.000    0.000    0.000    0.000 BmpImagePlugin.py:59(BmpImageFile)
        1    0.000    0.000    0.000    0.000 ascii.py:24(IncrementalDecoder)
        4    0.000    0.000    0.000    0.000 sre_parse.py:195(match)
        1    0.000    0.000    0.000    0.000 ascii.py:34(StreamConverter)
        1    0.000    0.000    0.000    0.000 io.py:75(BufferedIOBase)
        1    0.000    0.000    0.000    0.000 PpmImagePlugin.py:56(PpmImageFile)
        1    0.000    0.000    0.000    0.000 ascii.py:31(StreamReader)
        1    0.000    0.000    0.000    0.000 io.py:72(RawIOBase)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:154(PngInfo)
        1    0.000    0.000    0.000    0.000 ascii.py:28(StreamWriter)



real	0m17.277s
user	0m16.908s
sys	0m0.268s

4 threads:

reedy@terbium:~$ time python 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '100' --dirs '0' --wordlist '/etc/fancycaptcha/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/etc/fancycaptcha/badwords' --threads 4
Generating 100 CAPTCHA images separated in 25 image(s) per chunk run by 4 threads...
         916345 function calls (916344 primitive calls) in 17.555 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    36751    5.345    0.000    5.345    0.000 {method 'crop' of 'ImagingCore' objects}
      450    4.114    0.009    4.114    0.009 {method 'rotate' of 'ImagingCore' objects}
      675    2.063    0.003    2.063    0.003 {method 'filter' of 'ImagingCore' objects}
    36726    1.528    0.000    1.528    0.000 {method 'paste' of 'ImagingCore' objects}
      275    0.845    0.003    0.845    0.003 {PIL._imaging.fill}
      225    0.643    0.003   17.142    0.076 captcha-old.py:52(wobbly_copy)
    36751    0.443    0.000    5.809    0.000 Image.py:1729(load)
    36726    0.392    0.000    7.986    0.000 Image.py:1046(paste)
      225    0.390    0.002    0.390    0.002 {PIL._imaging.blend}
    36751    0.219    0.000    0.298    0.000 Image.py:1712(__init__)
    75602    0.192    0.000    0.217    0.000 Image.py:628(load)
      675    0.178    0.000    0.178    0.000 {method 'getband' of 'ImagingCore' objects}
       25    0.165    0.007    0.253    0.010 ImageOps.py:353(invert)
    38851    0.099    0.000    0.099    0.000 Image.py:461(__init__)
    36751    0.085    0.000    0.454    0.000 Image.py:785(crop)
   110428    0.080    0.000    0.080    0.000 {hasattr}
      225    0.066    0.000    0.066    0.000 {PIL._imaging.new}
   110178    0.064    0.000    0.145    0.000 Image.py:104(isImageType)
      675    0.063    0.000    0.063    0.000 {method 'putband' of 'ImagingCore' objects}
    37401    0.063    0.000    0.070    0.000 random.py:356(uniform)
     1850    0.050    0.000    0.070    0.000 Image.py:472(_new)
   112353    0.046    0.000    0.046    0.000 {method 'pixel_access' of 'ImagingCore' objects}
       25    0.043    0.002    0.043    0.002 {method 'encode' of 'ImagingEncoder' objects}
       25    0.040    0.002    0.040    0.002 {method 'point' of 'ImagingCore' objects}
      250    0.032    0.000    0.032    0.000 {method 'getbbox' of 'ImagingCore' objects}
      450    0.030    0.000    0.043    0.000 Image.py:300(getmodebands)
      225    0.030    0.000    0.243    0.001 Image.py:2104(merge)
    37001    0.027    0.000    0.049    0.000 _util.py:4(isStringType)
    36726    0.025    0.000    0.025    0.000 {math.sin}
37352/37351    0.023    0.000    0.025    0.000 {isinstance}
      450    0.020    0.000    4.142    0.009 Image.py:1338(rotate)
       25    0.017    0.001    0.017    0.001 {method 'render' of 'Font' objects}
      250    0.014    0.000    0.014    0.000 {method 'write' of 'file' objects}
      900    0.013    0.000    0.013    0.000 ImageMode.py:36(getmode)
       25    0.011    0.000   17.552    0.702 captcha-old.py:77(gen_captcha)
       25    0.010    0.000    0.010    0.000 {open}
       25    0.009    0.000    0.009    0.000 {method 'flush' of 'file' objects}
       50    0.009    0.000    0.009    0.000 {method 'getsize' of 'Font' objects}
    37594    0.009    0.000    0.009    0.000 {len}
       25    0.009    0.000    0.081    0.003 PngImagePlugin.py:493(_save)
    37488    0.007    0.000    0.007    0.000 {method 'random' of '_random.Random' objects}
       25    0.007    0.000    0.007    0.000 {method 'draw_bitmap' of 'ImagingDraw' objects}
      225    0.006    0.000    2.502    0.011 Image.py:831(filter)
       25    0.003    0.000    0.003    0.000 {method 'close' of 'file' objects}
      250    0.002    0.000    0.901    0.004 Image.py:1765(new)
       31    0.002    0.000    0.002    0.000 captcha-old.py:131(try_pick_word)
      675    0.002    0.000    2.064    0.003 ImageFilter.py:51(filter)
       25    0.002    0.000    0.002    0.000 {PIL._imagingft.getfont}
      751    0.002    0.000    0.002    0.000 {range}
      225    0.002    0.000    2.503    0.011 ImageEnhance.py:85(__init__)
       75    0.002    0.000    0.016    0.000 PngImagePlugin.py:474(putchunk)
      150    0.001    0.000    0.001    0.000 {PIL._imaging.crc32}
       25    0.001    0.000    0.053    0.002 ImageFile.py:435(_save)
       25    0.001    0.000    0.096    0.004 Image.py:1398(save)
      225    0.001    0.000    0.394    0.002 Image.py:2048(blend)
     7075    0.001    0.000    0.001    0.000 {method 'append' of 'list' objects}
      225    0.001    0.000    0.002    0.000 abc.py:128(__instancecheck__)
      250    0.001    0.000    0.035    0.000 Image.py:864(getbbox)
     1850    0.001    0.000    0.001    0.000 {method 'copy' of 'dict' objects}
      226    0.001    0.000    0.001    0.000 _weakrefset.py:70(__contains__)
      225    0.000    0.000    0.394    0.002 ImageEnhance.py:26(enhance)
      800    0.000    0.000    0.001    0.000 _binary.py:18(o8)
      125    0.000    0.000    0.001    0.000 _binary.py:50(o32be)
      450    0.000    0.000    0.001    0.000 Image.py:273(getmodetype)
       87    0.000    0.000    0.000    0.000 random.py:173(randrange)
       25    0.000    0.000    0.087    0.003 ImageOps.py:44(_lut)
       25    0.000    0.000    0.001    0.000 ImageDraw.py:293(Draw)
       25    0.000    0.000    0.003    0.000 captcha-old.py:169(pick_word)
      250    0.000    0.000    0.000    0.000 {getattr}
      800    0.000    0.000    0.000    0.000 {chr}
      150    0.000    0.000    0.000    0.000 _binary.py:47(o16be)
       25    0.000    0.000    0.087    0.003 Image.py:1130(point)
       25    0.000    0.000    0.000    0.000 ImageDraw.py:61(__init__)
       25    0.000    0.000    0.030    0.001 ImageDraw.py:258(text)
       25    0.000    0.000    0.023    0.001 ImageFont.py:151(getmask2)
       25    0.000    0.000    0.000    0.000 {method 'sort' of 'list' objects}
       25    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
       25    0.000    0.000    0.000    0.000 Image.py:394(_getencoder)
       25    0.000    0.000    0.000    0.000 ImageDraw.py:137(_getink)
       25    0.000    0.000    0.000    0.000 posixpath.py:68(join)
       25    0.000    0.000    0.000    0.000 genericpath.py:85(_splitext)
       31    0.000    0.000    0.000    0.000 {method 'search' of '_sre.SRE_Pattern' objects}
       75    0.000    0.000    0.000    0.000 _util.py:6(isPath)
       25    0.000    0.000    0.002    0.000 ImageFont.py:200(truetype)
       25    0.000    0.000    0.002    0.000 ImageFont.py:123(__init__)
       25    0.000    0.000    0.000    0.000 {PIL._imaging.zip_encoder}
       25    0.000    0.000    0.000    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
       50    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
       62    0.000    0.000    0.000    0.000 random.py:236(randint)
       25    0.000    0.000    0.005    0.000 ImageFont.py:142(getsize)
      225    0.000    0.000    0.000    0.000 ImageFilter.py:58(__init__)
       50    0.000    0.000    0.000    0.000 {max}
       25    0.000    0.000    0.000    0.000 posixpath.py:104(splitext)
       75    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
       25    0.000    0.000    0.000    0.000 Image.py:519(__getattr__)
       50    0.000    0.000    0.000    0.000 {min}
       25    0.000    0.000    0.000    0.000 {method 'setimage' of 'ImagingEncoder' objects}
       25    0.000    0.000    0.008    0.000 PngImagePlugin.py:490(write)
       50    0.000    0.000    0.000    0.000 {method 'draw_ink' of 'ImagingDraw' objects}
       25    0.000    0.000    0.000    0.000 PngImagePlugin.py:487(__init__)
       25    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
      150    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
       25    0.000    0.000    0.000    0.000 Image.py:316(preinit)
       25    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
       25    0.000    0.000    0.000    0.000 {callable}
       25    0.000    0.000    0.000    0.000 {PIL._imaging.draw}
        1    0.000    0.000    0.000    0.000 abc.py:148(__subclasscheck__)
       25    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       25    0.000    0.000    0.000    0.000 ImageFile.py:62(_tilesort)
        1    0.000    0.000    0.000    0.000 _abcoll.py:24(_hasattr)
        1    0.000    0.000    0.000    0.000 _weakrefset.py:36(__init__)
       25    0.000    0.000    0.000    0.000 {method 'upper' of 'str' objects}
        1    0.000    0.000    0.000    0.000 _weakrefset.py:83(add)
        2    0.000    0.000    0.000    0.000 _abcoll.py:26(<genexpr>)
       16    0.000    0.000    0.000    0.000 ImageMode.py:24(__init__)
        1    0.000    0.000    0.000    0.000 _abcoll.py:128(__subclasshook__)
        1    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {any}
        1    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


         900231 function calls (900214 primitive calls) in 17.768 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    36054    5.523    0.000    5.523    0.000 {method 'crop' of 'ImagingCore' objects}
      450    3.884    0.009    3.884    0.009 {method 'rotate' of 'ImagingCore' objects}
      675    2.047    0.003    2.047    0.003 {method 'filter' of 'ImagingCore' objects}
    36029    1.555    0.000    1.555    0.000 {method 'paste' of 'ImagingCore' objects}
      275    0.869    0.003    0.869    0.003 {PIL._imaging.fill}
      225    0.730    0.003   17.382    0.077 captcha-old.py:52(wobbly_copy)
    36029    0.464    0.000    8.258    0.000 Image.py:1046(paste)
    36054    0.463    0.000    6.007    0.000 Image.py:1729(load)
      225    0.447    0.002    0.447    0.002 {PIL._imaging.blend}
      675    0.236    0.000    0.236    0.000 {method 'getband' of 'ImagingCore' objects}
    74208    0.199    0.000    0.224    0.000 Image.py:628(load)
    38154    0.165    0.000    0.165    0.000 Image.py:461(__init__)
    36054    0.163    0.000    0.305    0.000 Image.py:1712(__init__)
       25    0.141    0.006    0.210    0.008 ImageOps.py:353(invert)
    36054    0.080    0.000    0.459    0.000 Image.py:785(crop)
   108339    0.079    0.000    0.079    0.000 {hasattr}
      675    0.064    0.000    0.064    0.000 {method 'putband' of 'ImagingCore' objects}
      225    0.063    0.000    0.063    0.000 {PIL._imaging.new}
   108087    0.054    0.000    0.133    0.000 Image.py:104(isImageType)
       25    0.053    0.002    0.053    0.002 {method 'encode' of 'ImagingEncoder' objects}
    36704    0.048    0.000    0.056    0.000 random.py:356(uniform)
       25    0.047    0.002    0.047    0.002 {method 'point' of 'ImagingCore' objects}
   110262    0.046    0.000    0.046    0.000 {method 'pixel_access' of 'ImagingCore' objects}
     1850    0.041    0.000    0.064    0.000 Image.py:472(_new)
    36304    0.035    0.000    0.058    0.000 _util.py:4(isStringType)
      250    0.032    0.000    0.032    0.000 {method 'getbbox' of 'ImagingCore' objects}
      250    0.030    0.000    0.030    0.000 {method 'write' of 'file' objects}
      225    0.030    0.000    0.229    0.001 Image.py:2104(merge)
    36029    0.025    0.000    0.025    0.000 {math.sin}
    36685    0.023    0.000    0.025    0.000 {isinstance}
       25    0.017    0.001    0.017    0.001 {open}
       25    0.015    0.001    0.015    0.001 {method 'render' of 'Font' objects}
      250    0.012    0.000    0.060    0.000 Image.py:864(getbbox)
36949/36947    0.009    0.000    0.009    0.000 {len}
       50    0.008    0.000    0.008    0.000 {method 'getsize' of 'Font' objects}
       25    0.007    0.000   17.765    0.711 captcha-old.py:77(gen_captcha)
    36787    0.007    0.000    0.007    0.000 {method 'random' of '_random.Random' objects}
       25    0.006    0.000    0.006    0.000 {method 'draw_bitmap' of 'ImagingDraw' objects}
      225    0.006    0.000    2.538    0.011 Image.py:831(filter)
      450    0.004    0.000    3.922    0.009 Image.py:1338(rotate)
       25    0.003    0.000    0.003    0.000 {method 'close' of 'file' objects}
       25    0.003    0.000    0.003    0.000 {method 'flush' of 'file' objects}
      250    0.002    0.000    0.891    0.004 Image.py:1765(new)
       29    0.002    0.000    0.002    0.000 captcha-old.py:131(try_pick_word)
      675    0.002    0.000    2.049    0.003 ImageFilter.py:51(filter)
      751    0.002    0.000    0.002    0.000 {range}
      225    0.002    0.000    2.540    0.011 ImageEnhance.py:85(__init__)
       25    0.002    0.000    0.002    0.000 {PIL._imagingft.getfont}
       75    0.001    0.000    0.015    0.000 PngImagePlugin.py:474(putchunk)
      450    0.001    0.000    0.002    0.000 Image.py:300(getmodebands)
       25    0.001    0.000    0.094    0.004 PngImagePlugin.py:493(_save)
      150    0.001    0.000    0.001    0.000 {PIL._imaging.crc32}
       25    0.001    0.000    0.120    0.005 Image.py:1398(save)
      125    0.001    0.000    0.002    0.000 _binary.py:50(o32be)
      225    0.001    0.000    0.451    0.002 Image.py:2048(blend)
     7146    0.001    0.000    0.001    0.000 {method 'append' of 'list' objects}
       25    0.001    0.000    0.060    0.002 ImageFile.py:435(_save)
      225    0.001    0.000    0.002    0.000 abc.py:128(__instancecheck__)
      800    0.001    0.000    0.001    0.000 _binary.py:18(o8)
       25    0.001    0.000    0.004    0.000 Image.py:316(preinit)
      900    0.001    0.000    0.001    0.000 ImageMode.py:36(getmode)
     1850    0.001    0.000    0.001    0.000 {method 'copy' of 'dict' objects}
        1    0.001    0.001    0.001    0.001 io.py:34(<module>)
      234    0.001    0.000    0.001    0.000 _weakrefset.py:70(__contains__)
      450    0.000    0.000    0.001    0.000 Image.py:273(getmodetype)
      225    0.000    0.000    0.451    0.002 ImageEnhance.py:26(enhance)
       25    0.000    0.000    0.001    0.000 ImageDraw.py:293(Draw)
       25    0.000    0.000    0.068    0.003 ImageOps.py:44(_lut)
       83    0.000    0.000    0.000    0.000 random.py:173(randrange)
      270    0.000    0.000    0.000    0.000 {getattr}
      800    0.000    0.000    0.000    0.000 {chr}
        1    0.000    0.000    0.002    0.002 BmpImagePlugin.py:27(<module>)
       25    0.000    0.000    0.003    0.000 captcha-old.py:169(pick_word)
       25    0.000    0.000    0.067    0.003 Image.py:1130(point)
       25    0.000    0.000    0.027    0.001 ImageDraw.py:258(text)
       25    0.000    0.000    0.000    0.000 ImageDraw.py:61(__init__)
      150    0.000    0.000    0.000    0.000 _binary.py:47(o16be)
        1    0.000    0.000    0.000    0.000 GifImagePlugin.py:28(<module>)
       25    0.000    0.000    0.000    0.000 {method 'sort' of 'list' objects}
        1    0.000    0.000    0.001    0.001 ImageFile.py:30(<module>)
       25    0.000    0.000    0.021    0.001 ImageFont.py:151(getmask2)
       25    0.000    0.000    0.000    0.000 genericpath.py:85(_splitext)
       25    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
        1    0.000    0.000    0.000    0.000 JpegPresets.py:67(<module>)
       25    0.000    0.000    0.000    0.000 posixpath.py:68(join)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:34(<module>)
        1    0.000    0.000    0.000    0.000 JpegImagePlugin.py:35(<module>)
       25    0.000    0.000    0.000    0.000 {PIL._imaging.zip_encoder}
        8    0.000    0.000    0.000    0.000 _weakrefset.py:20(__enter__)
       25    0.000    0.000    0.000    0.000 Image.py:394(_getencoder)
       25    0.000    0.000    0.002    0.000 ImageFont.py:200(truetype)
       25    0.000    0.000    0.000    0.000 Image.py:519(__getattr__)
       25    0.000    0.000    0.000    0.000 posixpath.py:104(splitext)
       75    0.000    0.000    0.000    0.000 _util.py:6(isPath)
       29    0.000    0.000    0.000    0.000 {method 'search' of '_sre.SRE_Pattern' objects}
        5    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x91d400}
       25    0.000    0.000    0.000    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
       50    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
       25    0.000    0.000    0.002    0.000 ImageFont.py:123(__init__)
        1    0.000    0.000    0.000    0.000 ascii.py:8(<module>)
       25    0.000    0.000    0.000    0.000 ImageDraw.py:137(_getink)
        8    0.000    0.000    0.000    0.000 abc.py:148(__subclasscheck__)
       58    0.000    0.000    0.000    0.000 random.py:236(randint)
      225    0.000    0.000    0.000    0.000 ImageFilter.py:58(__init__)
       58    0.000    0.000    0.000    0.000 {min}
        1    0.000    0.000    0.000    0.000 {__import__}
      164    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
       77    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
        2    0.000    0.000    0.000    0.000 sre_parse.py:379(_parse)
        4    0.000    0.000    0.000    0.000 abc.py:86(__new__)
       50    0.000    0.000    0.000    0.000 {max}
       25    0.000    0.000    0.005    0.000 ImageFont.py:142(getsize)
        1    0.000    0.000    0.000    0.000 PpmImagePlugin.py:18(<module>)
       25    0.000    0.000    0.006    0.000 PngImagePlugin.py:490(write)
       19    0.000    0.000    0.000    0.000 _weakrefset.py:36(__init__)
       25    0.000    0.000    0.000    0.000 {method 'setimage' of 'ImagingEncoder' objects}
      4/2    0.000    0.000    0.000    0.000 sre_compile.py:32(_compile)
       50    0.000    0.000    0.000    0.000 {method 'draw_ink' of 'ImagingDraw' objects}
       19    0.000    0.000    0.000    0.000 _weakrefset.py:58(__iter__)
       25    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        1    0.000    0.000    0.000    0.000 ImagePalette.py:19(<module>)
       17    0.000    0.000    0.000    0.000 sre_parse.py:182(__next)
        8    0.000    0.000    0.000    0.000 abc.py:105(register)
       25    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
      6/4    0.000    0.000    0.000    0.000 sre_parse.py:140(getwidth)
       25    0.000    0.000    0.000    0.000 PngImagePlugin.py:487(__init__)
       25    0.000    0.000    0.000    0.000 {PIL._imaging.draw}
        5    0.000    0.000    0.000    0.000 sre_compile.py:178(_compile_charset)
        2    0.000    0.000    0.000    0.000 re.py:226(_compile)
        5    0.000    0.000    0.000    0.000 Image.py:2131(register_open)
        2    0.000    0.000    0.000    0.000 sre_compile.py:359(_compile_info)
        2    0.000    0.000    0.000    0.000 sre_compile.py:493(compile)
        2    0.000    0.000    0.000    0.000 sre_parse.py:675(parse)
       12    0.000    0.000    0.000    0.000 sre_parse.py:130(__getitem__)
       25    0.000    0.000    0.000    0.000 {callable}
       16    0.000    0.000    0.000    0.000 _weakrefset.py:83(add)
       35    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
        2    0.000    0.000    0.000    0.000 locale.py:363(normalize)
        1    0.000    0.000    0.000    0.000 __init__.py:71(search_function)
       10    0.000    0.000    0.000    0.000 Image.py:2168(register_extension)
        1    0.000    0.000    0.000    0.000 locale.py:493(getdefaultlocale)
    27/16    0.000    0.000    0.000    0.000 {issubclass}
       48    0.000    0.000    0.000    0.000 {method 'upper' of 'str' objects}
        5    0.000    0.000    0.000    0.000 sre_compile.py:207(_optimize_charset)
        1    0.000    0.000    0.000    0.000 {method 'encode' of 'unicode' objects}
       25    0.000    0.000    0.000    0.000 ImageFile.py:62(_tilesort)
        2    0.000    0.000    0.000    0.000 __init__.py:49(normalize_encoding)
        8    0.000    0.000    0.000    0.000 _weakrefset.py:26(__exit__)
        8    0.000    0.000    0.000    0.000 _weakrefset.py:16(__init__)
        3    0.000    0.000    0.000    0.000 UserDict.py:58(get)
        4    0.000    0.000    0.000    0.000 sre_compile.py:472(isstring)
        9    0.000    0.000    0.000    0.000 sre_parse.py:138(append)
        2    0.000    0.000    0.000    0.000 sre_parse.py:301(_parse_sub)
       24    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
        8    0.000    0.000    0.000    0.000 sre_parse.py:126(__len__)
        4    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
        8    0.000    0.000    0.000    0.000 _weakrefset.py:52(_commit_removals)
        2    0.000    0.000    0.000    0.000 sre_compile.py:478(_code)
        2    0.000    0.000    0.000    0.000 {_sre.compile}
        1    0.000    0.000    0.000    0.000 {_codecs.utf_8_decode}
        1    0.000    0.000    0.000    0.000 ascii.py:41(getregentry)
        1    0.000    0.000    0.000    0.000 locale.py:347(_replace_encoding)
        1    0.000    0.000    0.000    0.000 locale.py:546(getlocale)
        2    0.000    0.000    0.000    0.000 locale.py:447(_parse_localename)
        5    0.000    0.000    0.000    0.000 Image.py:2157(register_save)
        1    0.000    0.000    0.000    0.000 ImagePalette.py:23(ImagePalette)
        1    0.000    0.000    0.000    0.000 codecs.py:77(__new__)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:78(ChunkStream)
        4    0.000    0.000    0.000    0.000 abc.py:89(<genexpr>)
        4    0.000    0.000    0.000    0.000 sre_parse.py:195(match)
        1    0.000    0.000    0.000    0.000 {method 'decode' of 'str' objects}
        1    0.000    0.000    0.000    0.000 UserDict.py:18(__getitem__)
       15    0.000    0.000    0.000    0.000 sre_parse.py:201(get)
        2    0.000    0.000    0.000    0.000 sre_compile.py:354(_simple)
        8    0.000    0.000    0.000    0.000 {method 'remove' of 'set' objects}
        1    0.000    0.000    0.000    0.000 GifImagePlugin.py:53(GifImageFile)
        2    0.000    0.000    0.000    0.000 re.py:188(compile)
        3    0.000    0.000    0.000    0.000 {method 'replace' of 'str' objects}
        2    0.000    0.000    0.000    0.000 sre_parse.py:178(__init__)
        1    0.000    0.000    0.000    0.000 ImageFile.py:70(ImageFile)
        4    0.000    0.000    0.000    0.000 sre_parse.py:90(__init__)
        8    0.000    0.000    0.000    0.000 {method '__subclasshook__' of 'object' objects}
        2    0.000    0.000    0.000    0.000 sre_parse.py:67(__init__)
        1    0.000    0.000    0.000    0.000 ascii.py:13(Codec)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:179(PngStream)
        6    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {_locale.setlocale}
        1    0.000    0.000    0.000    0.000 {method 'index' of 'str' objects}
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:341(PngImageFile)
        1    0.000    0.000    0.000    0.000 ImageFile.py:298(Parser)
        1    0.000    0.000    0.000    0.000 utf_8.py:15(decode)
        4    0.000    0.000    0.000    0.000 {method 'translate' of 'str' objects}
        3    0.000    0.000    0.000    0.000 UserDict.py:70(__contains__)
        4    0.000    0.000    0.000    0.000 sre_parse.py:257(_escape)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:484(_idat)
        1    0.000    0.000    0.000    0.000 BmpImagePlugin.py:59(BmpImageFile)
        1    0.000    0.000    0.000    0.000 ImageFile.py:268(StubImageFile)
        1    0.000    0.000    0.000    0.000 ascii.py:24(IncrementalDecoder)
        2    0.000    0.000    0.000    0.000 sre_parse.py:134(__setitem__)
        1    0.000    0.000    0.000    0.000 BmpImagePlugin.py:171(DibImageFile)
        3    0.000    0.000    0.000    0.000 Image.py:2146(register_mime)
        1    0.000    0.000    0.000    0.000 ascii.py:20(IncrementalEncoder)
        1    0.000    0.000    0.000    0.000 io.py:75(BufferedIOBase)
        1    0.000    0.000    0.000    0.000 PpmImagePlugin.py:56(PpmImageFile)
        1    0.000    0.000    0.000    0.000 io.py:69(IOBase)
        8    0.000    0.000    0.000    0.000 {method '__subclasses__' of 'type' objects}
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:154(PngInfo)
        1    0.000    0.000    0.000    0.000 JpegImagePlugin.py:267(JpegImageFile)
        1    0.000    0.000    0.000    0.000 ascii.py:28(StreamWriter)
        1    0.000    0.000    0.000    0.000 io.py:78(TextIOBase)
        3    0.000    0.000    0.000    0.000 {ord}
        1    0.000    0.000    0.000    0.000 ascii.py:34(StreamConverter)
        1    0.000    0.000    0.000    0.000 ascii.py:31(StreamReader)
        1    0.000    0.000    0.000    0.000 io.py:72(RawIOBase)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


         907540 function calls in 17.809 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    36383    5.530    0.000    5.530    0.000 {method 'crop' of 'ImagingCore' objects}
      450    4.446    0.010    4.446    0.010 {method 'rotate' of 'ImagingCore' objects}
      675    2.115    0.003    2.115    0.003 {method 'filter' of 'ImagingCore' objects}
    36358    1.425    0.000    1.425    0.000 {method 'paste' of 'ImagingCore' objects}
      275    0.773    0.003    0.773    0.003 {PIL._imaging.fill}
      225    0.613    0.003   17.493    0.078 captcha-old.py:52(wobbly_copy)
    36383    0.517    0.000    6.068    0.000 Image.py:1729(load)
      225    0.445    0.002    0.445    0.002 {PIL._imaging.blend}
    36358    0.362    0.000    8.158    0.000 Image.py:1046(paste)
      675    0.143    0.000    0.143    0.000 {method 'getband' of 'ImagingCore' objects}
    36383    0.137    0.000    0.226    0.000 Image.py:1712(__init__)
    74866    0.135    0.000    0.161    0.000 Image.py:628(load)
     1850    0.102    0.000    0.110    0.000 Image.py:472(_new)
    38483    0.097    0.000    0.097    0.000 Image.py:461(__init__)
    36383    0.096    0.000    0.387    0.000 Image.py:785(crop)
   109074    0.080    0.000    0.159    0.000 Image.py:104(isImageType)
   109324    0.080    0.000    0.080    0.000 {hasattr}
    37033    0.077    0.000    0.084    0.000 random.py:356(uniform)
       25    0.067    0.003    0.067    0.003 {method 'point' of 'ImagingCore' objects}
      675    0.064    0.000    0.064    0.000 {method 'putband' of 'ImagingCore' objects}
       25    0.057    0.002    0.131    0.005 ImageOps.py:353(invert)
   111249    0.047    0.000    0.047    0.000 {method 'pixel_access' of 'ImagingCore' objects}
       25    0.042    0.002    0.042    0.002 {method 'encode' of 'ImagingEncoder' objects}
       25    0.032    0.001    0.032    0.001 {open}
      250    0.032    0.000    0.032    0.000 {method 'getbbox' of 'ImagingCore' objects}
      225    0.031    0.000    0.031    0.000 {PIL._imaging.new}
      225    0.030    0.000    2.448    0.011 ImageEnhance.py:85(__init__)
    36633    0.028    0.000    0.051    0.000 _util.py:4(isStringType)
    36358    0.025    0.000    0.025    0.000 {math.sin}
    36983    0.024    0.000    0.025    0.000 {isinstance}
       25    0.022    0.001    0.022    0.001 {method 'draw_bitmap' of 'ImagingDraw' objects}
      225    0.019    0.000    0.121    0.001 Image.py:2104(merge)
       25    0.016    0.001    0.016    0.001 {method 'render' of 'Font' objects}
       25    0.012    0.000   17.805    0.712 captcha-old.py:77(gen_captcha)
      250    0.011    0.000    0.011    0.000 {method 'write' of 'file' objects}
       25    0.011    0.000    0.072    0.003 PngImagePlugin.py:493(_save)
       50    0.009    0.000    0.009    0.000 {method 'getsize' of 'Font' objects}
    37241    0.009    0.000    0.009    0.000 {len}
    37130    0.007    0.000    0.007    0.000 {method 'random' of '_random.Random' objects}
      225    0.006    0.000    2.418    0.011 Image.py:831(filter)
      450    0.004    0.000    4.502    0.010 Image.py:1338(rotate)
       25    0.003    0.000    0.003    0.000 {method 'close' of 'file' objects}
       36    0.002    0.000    0.003    0.000 captcha-old.py:131(try_pick_word)
       25    0.002    0.000    0.002    0.000 {method 'flush' of 'file' objects}
      250    0.002    0.000    0.803    0.003 Image.py:1765(new)
      675    0.002    0.000    2.117    0.003 ImageFilter.py:51(filter)
       25    0.002    0.000    0.002    0.000 {PIL._imagingft.getfont}
      751    0.002    0.000    0.002    0.000 {range}
      450    0.001    0.000    0.002    0.000 Image.py:300(getmodebands)
      150    0.001    0.000    0.001    0.000 {PIL._imaging.crc32}
      800    0.001    0.000    0.001    0.000 _binary.py:18(o8)
      225    0.001    0.000    0.449    0.002 Image.py:2048(blend)
     7075    0.001    0.000    0.001    0.000 {method 'append' of 'list' objects}
       25    0.001    0.000    0.048    0.002 ImageFile.py:435(_save)
      900    0.001    0.000    0.001    0.000 ImageMode.py:36(getmode)
      225    0.001    0.000    0.002    0.000 abc.py:128(__instancecheck__)
       75    0.001    0.000    0.010    0.000 PngImagePlugin.py:474(putchunk)
      125    0.001    0.000    0.002    0.000 _binary.py:50(o32be)
     1850    0.001    0.000    0.001    0.000 {method 'copy' of 'dict' objects}
      225    0.001    0.000    0.001    0.000 _weakrefset.py:70(__contains__)
      250    0.001    0.000    0.033    0.000 Image.py:864(getbbox)
       25    0.000    0.000    0.109    0.004 Image.py:1398(save)
      225    0.000    0.000    0.449    0.002 ImageEnhance.py:26(enhance)
      450    0.000    0.000    0.001    0.000 Image.py:273(getmodetype)
       97    0.000    0.000    0.000    0.000 random.py:173(randrange)
      800    0.000    0.000    0.000    0.000 {chr}
       25    0.000    0.000    0.001    0.000 ImageDraw.py:293(Draw)
       25    0.000    0.000    0.073    0.003 ImageOps.py:44(_lut)
       25    0.000    0.000    0.003    0.000 captcha-old.py:169(pick_word)
      250    0.000    0.000    0.000    0.000 {getattr}
      150    0.000    0.000    0.001    0.000 _binary.py:47(o16be)
       25    0.000    0.000    0.000    0.000 ImageDraw.py:61(__init__)
       25    0.000    0.000    0.073    0.003 Image.py:1130(point)
       25    0.000    0.000    0.042    0.002 ImageDraw.py:258(text)
       25    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
       25    0.000    0.000    0.020    0.001 ImageFont.py:151(getmask2)
       25    0.000    0.000    0.000    0.000 {method 'sort' of 'list' objects}
       36    0.000    0.000    0.000    0.000 {method 'search' of '_sre.SRE_Pattern' objects}
       25    0.000    0.000    0.002    0.000 ImageFont.py:200(truetype)
       25    0.000    0.000    0.000    0.000 genericpath.py:85(_splitext)
       25    0.000    0.000    0.000    0.000 posixpath.py:68(join)
       72    0.000    0.000    0.000    0.000 random.py:236(randint)
       25    0.000    0.000    0.000    0.000 Image.py:394(_getencoder)
       25    0.000    0.000    0.000    0.000 Image.py:316(preinit)
       25    0.000    0.000    0.002    0.000 ImageFont.py:123(__init__)
       75    0.000    0.000    0.000    0.000 _util.py:6(isPath)
       25    0.000    0.000    0.000    0.000 ImageDraw.py:137(_getink)
       25    0.000    0.000    0.000    0.000 {PIL._imaging.zip_encoder}
       25    0.000    0.000    0.000    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
       50    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
       25    0.000    0.000    0.005    0.000 ImageFont.py:142(getsize)
      225    0.000    0.000    0.000    0.000 ImageFilter.py:58(__init__)
       75    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
       25    0.000    0.000    0.000    0.000 Image.py:519(__getattr__)
       50    0.000    0.000    0.000    0.000 {max}
       50    0.000    0.000    0.000    0.000 {min}
       25    0.000    0.000    0.000    0.000 posixpath.py:104(splitext)
      150    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
       25    0.000    0.000    0.004    0.000 PngImagePlugin.py:490(write)
       25    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
       25    0.000    0.000    0.000    0.000 {method 'setimage' of 'ImagingEncoder' objects}
       50    0.000    0.000    0.000    0.000 {method 'draw_ink' of 'ImagingDraw' objects}
       25    0.000    0.000    0.000    0.000 PngImagePlugin.py:487(__init__)
       25    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
       25    0.000    0.000    0.000    0.000 {method 'upper' of 'str' objects}
       25    0.000    0.000    0.000    0.000 {PIL._imaging.draw}
       25    0.000    0.000    0.000    0.000 {callable}
       25    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       25    0.000    0.000    0.000    0.000 ImageFile.py:62(_tilesort)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


         927905 function calls in 17.939 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    37232    5.545    0.000    5.545    0.000 {method 'crop' of 'ImagingCore' objects}
      450    4.611    0.010    4.611    0.010 {method 'rotate' of 'ImagingCore' objects}
      675    2.080    0.003    2.080    0.003 {method 'filter' of 'ImagingCore' objects}
    37207    1.277    0.000    1.277    0.000 {method 'paste' of 'ImagingCore' objects}
      275    0.902    0.003    0.902    0.003 {PIL._imaging.fill}
      225    0.610    0.003   17.602    0.078 captcha-old.py:52(wobbly_copy)
    37232    0.561    0.000    6.128    0.000 Image.py:1729(load)
      225    0.418    0.002    0.418    0.002 {PIL._imaging.blend}
    37207    0.338    0.000    8.006    0.000 Image.py:1046(paste)
      675    0.196    0.000    0.196    0.000 {method 'getband' of 'ImagingCore' objects}
    76564    0.186    0.000    0.212    0.000 Image.py:628(load)
    37232    0.149    0.000    0.240    0.000 Image.py:1712(__init__)
    39332    0.097    0.000    0.097    0.000 Image.py:461(__init__)
       25    0.086    0.003    0.155    0.006 ImageOps.py:353(invert)
    37232    0.083    0.000    0.417    0.000 Image.py:785(crop)
   111871    0.082    0.000    0.082    0.000 {hasattr}
      675    0.065    0.000    0.065    0.000 {method 'putband' of 'ImagingCore' objects}
       25    0.060    0.002    0.060    0.002 {method 'point' of 'ImagingCore' objects}
    37882    0.058    0.000    0.066    0.000 random.py:356(uniform)
   111621    0.055    0.000    0.137    0.000 Image.py:104(isImageType)
   113796    0.048    0.000    0.048    0.000 {method 'pixel_access' of 'ImagingCore' objects}
      225    0.046    0.000    0.190    0.001 Image.py:2104(merge)
       25    0.046    0.002    0.046    0.002 {method 'encode' of 'ImagingEncoder' objects}
      250    0.041    0.000    0.041    0.000 {method 'write' of 'file' objects}
      225    0.036    0.000    0.036    0.000 {PIL._imaging.new}
      250    0.033    0.000    0.033    0.000 {method 'getbbox' of 'ImagingCore' objects}
    37482    0.026    0.000    0.049    0.000 _util.py:4(isStringType)
    37207    0.026    0.000    0.026    0.000 {math.sin}
    37832    0.024    0.000    0.025    0.000 {isinstance}
       25    0.018    0.001    0.018    0.001 {open}
      250    0.018    0.000    0.923    0.004 Image.py:1765(new)
       25    0.016    0.001    0.016    0.001 {method 'render' of 'Font' objects}
     1850    0.014    0.000    0.020    0.000 Image.py:472(_new)
       25    0.012    0.000   17.936    0.717 captcha-old.py:77(gen_captcha)
    38087    0.009    0.000    0.009    0.000 {len}
       50    0.009    0.000    0.009    0.000 {method 'getsize' of 'Font' objects}
    37977    0.008    0.000    0.008    0.000 {method 'random' of '_random.Random' objects}
      225    0.006    0.000    2.482    0.011 Image.py:831(filter)
       25    0.005    0.000    0.005    0.000 {method 'flush' of 'file' objects}
       25    0.005    0.000    0.005    0.000 {method 'draw_bitmap' of 'ImagingDraw' objects}
       25    0.004    0.000    0.004    0.000 {method 'close' of 'file' objects}
      450    0.004    0.000    4.623    0.010 Image.py:1338(rotate)
       35    0.002    0.000    0.002    0.000 captcha-old.py:131(try_pick_word)
      675    0.002    0.000    2.081    0.003 ImageFilter.py:51(filter)
       75    0.002    0.000    0.015    0.000 PngImagePlugin.py:474(putchunk)
       25    0.002    0.000    0.002    0.000 {PIL._imagingft.getfont}
      225    0.002    0.000    2.484    0.011 ImageEnhance.py:85(__init__)
      751    0.002    0.000    0.002    0.000 {range}
      900    0.001    0.000    0.001    0.000 ImageMode.py:36(getmode)
      150    0.001    0.000    0.001    0.000 {PIL._imaging.crc32}
      450    0.001    0.000    0.003    0.000 Image.py:300(getmodebands)
      250    0.001    0.000    0.035    0.000 Image.py:864(getbbox)
       25    0.001    0.000    0.123    0.005 Image.py:1398(save)
      225    0.001    0.000    0.421    0.002 Image.py:2048(blend)
     7075    0.001    0.000    0.001    0.000 {method 'append' of 'list' objects}
       25    0.001    0.000    0.056    0.002 ImageFile.py:435(_save)
       25    0.001    0.000    0.099    0.004 PngImagePlugin.py:493(_save)
      800    0.001    0.000    0.001    0.000 _binary.py:18(o8)
      225    0.001    0.000    0.002    0.000 abc.py:128(__instancecheck__)
     1850    0.001    0.000    0.001    0.000 {method 'copy' of 'dict' objects}
      225    0.001    0.000    0.001    0.000 _weakrefset.py:70(__contains__)
      125    0.001    0.000    0.001    0.000 _binary.py:50(o32be)
      225    0.000    0.000    0.422    0.002 ImageEnhance.py:26(enhance)
      450    0.000    0.000    0.001    0.000 Image.py:273(getmodetype)
       95    0.000    0.000    0.000    0.000 random.py:173(randrange)
       25    0.000    0.000    0.068    0.003 ImageOps.py:44(_lut)
       25    0.000    0.000    0.001    0.000 ImageDraw.py:293(Draw)
       25    0.000    0.000    0.003    0.000 captcha-old.py:169(pick_word)
      250    0.000    0.000    0.000    0.000 {getattr}
      800    0.000    0.000    0.000    0.000 {chr}
       25    0.000    0.000    0.000    0.000 ImageDraw.py:61(__init__)
      150    0.000    0.000    0.001    0.000 _binary.py:47(o16be)
       25    0.000    0.000    0.068    0.003 Image.py:1130(point)
       25    0.000    0.000    0.027    0.001 ImageDraw.py:258(text)
       25    0.000    0.000    0.021    0.001 ImageFont.py:151(getmask2)
       25    0.000    0.000    0.000    0.000 {method 'sort' of 'list' objects}
       25    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
       25    0.000    0.000    0.000    0.000 genericpath.py:85(_splitext)
       25    0.000    0.000    0.000    0.000 posixpath.py:68(join)
       35    0.000    0.000    0.000    0.000 {method 'search' of '_sre.SRE_Pattern' objects}
       25    0.000    0.000    0.002    0.000 ImageFont.py:200(truetype)
       75    0.000    0.000    0.000    0.000 _util.py:6(isPath)
       50    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
       25    0.000    0.000    0.000    0.000 Image.py:394(_getencoder)
       70    0.000    0.000    0.000    0.000 random.py:236(randint)
       25    0.000    0.000    0.000    0.000 {PIL._imaging.zip_encoder}
       25    0.000    0.000    0.000    0.000 ImageDraw.py:137(_getink)
       25    0.000    0.000    0.002    0.000 ImageFont.py:123(__init__)
       25    0.000    0.000    0.000    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
       25    0.000    0.000    0.005    0.000 ImageFont.py:142(getsize)
       25    0.000    0.000    0.000    0.000 Image.py:519(__getattr__)
       75    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
       50    0.000    0.000    0.000    0.000 {max}
       50    0.000    0.000    0.000    0.000 {min}
       25    0.000    0.000    0.000    0.000 posixpath.py:104(splitext)
       25    0.000    0.000    0.009    0.000 PngImagePlugin.py:490(write)
      225    0.000    0.000    0.000    0.000 ImageFilter.py:58(__init__)
       25    0.000    0.000    0.000    0.000 {method 'setimage' of 'ImagingEncoder' objects}
      150    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
       25    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
       50    0.000    0.000    0.000    0.000 {method 'draw_ink' of 'ImagingDraw' objects}
       25    0.000    0.000    0.000    0.000 PngImagePlugin.py:487(__init__)
       25    0.000    0.000    0.000    0.000 Image.py:316(preinit)
       25    0.000    0.000    0.000    0.000 {PIL._imaging.draw}
       25    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
       25    0.000    0.000    0.000    0.000 {callable}
       25    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       25    0.000    0.000    0.000    0.000 ImageFile.py:62(_tilesort)
       25    0.000    0.000    0.000    0.000 {method 'upper' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}



real	0m18.130s
user	0m27.776s
sys	0m2.972s
reedy@terbium:~$

I do note, python is python 2.7... I wonder what python3 (3.4) makes much difference...

Of course, terbium doesn't have python3-pil installed...

Back to my vm

Python 2.7.12+

$ time python 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '100' --dirs '0' --wordlist '/home/reedy/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/home/reedy/badwords'
Generating 100 CAPTCHA images separated in 100 image(s) per chunk run by 1 threads...
         4069456 function calls (4069180 primitive calls) in 29.519 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1800   13.110    0.007   13.110    0.007 {method 'transform2' of 'ImagingCore' objects}
     2700    4.672    0.002    4.672    0.002 {method 'filter' of 'ImagingCore' objects}
      900    1.460    0.002    1.460    0.002 {PIL._imaging.blend}
   149515    1.261    0.000    3.735    0.000 Image.py:1255(paste)
      900    1.195    0.001   28.680    0.032 captcha-old.py:52(wobbly_copy)
   149615    0.727    0.000    1.663    0.000 Image.py:1955(__init__)
   299233    0.614    0.000    0.614    0.000 {map}
   309430    0.544    0.000    0.692    0.000 Image.py:747(load)
   149615    0.473    0.000    0.473    0.000 {method 'crop' of 'ImagingCore' objects}
   149615    0.472    0.000    1.015    0.000 Image.py:1974(load)
     2700    0.407    0.000    0.407    0.000 {method 'getband' of 'ImagingCore' objects}
   149615    0.404    0.000    2.424    0.000 Image.py:1012(crop)
   159815    0.354    0.000    0.354    0.000 Image.py:499(__init__)
   451548    0.345    0.000    0.345    0.000 {hasattr}
     2700    0.339    0.000    0.339    0.000 {method 'putband' of 'ImagingCore' objects}
     2700    0.320    0.000    0.320    0.000 {PIL._imaging.new}
   448545    0.319    0.000    0.658    0.000 Image.py:136(isImageType)
      100    0.264    0.003    0.264    0.003 {method 'encode' of 'ImagingEncoder' objects}
   149515    0.233    0.000    0.233    0.000 {method 'paste' of 'ImagingCore' objects}
     1000    0.231    0.000    0.231    0.000 {method 'write' of 'file' objects}
   459045    0.218    0.000    0.218    0.000 {method 'pixel_access' of 'ImagingCore' objects}
   152215    0.216    0.000    0.257    0.000 random.py:358(uniform)
     1000    0.183    0.000    0.183    0.000 {method 'getbbox' of 'ImagingCore' objects}
154196/154195    0.134    0.000    0.148    0.000 {isinstance}
     1100    0.126    0.000    0.126    0.000 {PIL._imaging.fill}
   150615    0.125    0.000    0.253    0.000 _util.py:4(isStringType)
   153115    0.085    0.000    0.085    0.000 {math.sin}
      100    0.070    0.001    0.070    0.001 {method 'render' of 'Font' objects}
     7400    0.048    0.000    0.076    0.000 Image.py:519(_new)
   152563    0.041    0.000    0.041    0.000 {method 'random' of '_random.Random' objects}
156509/156468    0.041    0.000    0.041    0.000 {len}
      200    0.041    0.000    0.041    0.000 {method 'getsize' of 'Font' objects}
     7200    0.037    0.000    0.037    0.000 {round}
     1800    0.037    0.000   13.488    0.007 Image.py:1550(rotate)
      900    0.036    0.000    5.689    0.006 Image.py:1058(filter)
      900    0.025    0.000    0.508    0.001 Image.py:2394(merge)
     2800    0.018    0.000    0.390    0.000 Image.py:2013(new)
     1800    0.018    0.000   13.137    0.007 Image.py:1863(__transformer)
     1800    0.018    0.000   13.403    0.007 Image.py:1811(transform)
      100    0.017    0.000   29.499    0.295 captcha-old.py:77(gen_captcha)
      900    0.014    0.000    5.706    0.006 ImageEnhance.py:95(__init__)
      100    0.013    0.000    0.013    0.000 {method 'draw_bitmap' of 'ImagingDraw' objects}
      100    0.013    0.000    0.013    0.000 {open}
     3003    0.012    0.000    0.012    0.000 {range}
      100    0.012    0.000    0.012    0.000 {PIL._imagingft.getfont}
      100    0.011    0.000    0.047    0.000 ImageOps.py:357(invert)
      124    0.010    0.000    0.013    0.000 captcha-old.py:131(try_pick_word)
     2700    0.009    0.000    4.680    0.002 ImageFilter.py:51(filter)
      900    0.007    0.000    0.014    0.000 abc.py:128(__instancecheck__)
     1800    0.007    0.000    0.011    0.000 Image.py:333(getmodebands)
    29161    0.007    0.000    0.007    0.000 {method 'append' of 'list' objects}
      100    0.006    0.000    0.006    0.000 {method 'point' of 'ImagingCore' objects}
      900    0.006    0.000    1.482    0.002 Image.py:2338(blend)
      906    0.005    0.000    0.005    0.000 _weakrefset.py:70(__contains__)
      600    0.005    0.000    0.005    0.000 {PIL._imaging.crc32}
     4500    0.005    0.000    0.005    0.000 ImageMode.py:33(getmode)
      100    0.004    0.000    0.522    0.005 PngImagePlugin.py:668(_save)
      100    0.004    0.000    0.508    0.005 ImageFile.py:443(_save)
     1000    0.004    0.000    0.193    0.000 Image.py:1092(getbbox)
     3600    0.004    0.000    0.004    0.000 {math.cos}
     7400    0.004    0.000    0.004    0.000 {method 'copy' of 'dict' objects}
      300    0.004    0.000    0.241    0.001 PngImagePlugin.py:646(putchunk)
      100    0.003    0.000    0.568    0.006 Image.py:1612(save)
     1800    0.003    0.000    0.003    0.000 Image.py:1589(transform)
      900    0.003    0.000    1.485    0.002 ImageEnhance.py:26(enhance)
     1800    0.003    0.000    0.004    0.000 Image.py:306(getmodetype)
        1    0.003    0.003    0.021    0.021 JpegImagePlugin.py:35(<module>)
     1473    0.003    0.000    0.003    0.000 sre_parse.py:193(__next)
     46/7    0.002    0.000    0.008    0.001 sre_parse.py:395(_parse)
      348    0.002    0.000    0.002    0.000 random.py:175(randrange)
      900    0.002    0.000    0.003    0.000 Image.py:1082(getbands)
     1100    0.002    0.000    0.002    0.000 {_struct.pack}
      100    0.002    0.000    0.031    0.000 ImageOps.py:47(_lut)
      100    0.001    0.000    0.001    0.000 {method 'flush' of 'file' objects}
     1245    0.001    0.000    0.001    0.000 {getattr}
        2    0.001    0.001    0.002    0.001 collections.py:305(namedtuple)
     1800    0.001    0.000    0.001    0.000 {math.radians}
      100    0.001    0.000    0.003    0.000 ImageDraw.py:285(Draw)
      100    0.001    0.000    0.017    0.000 captcha-old.py:169(pick_word)
      100    0.001    0.000    0.002    0.000 ImageDraw.py:49(__init__)
      100    0.001    0.000    0.026    0.000 Image.py:349(preinit)
        1    0.001    0.001    0.015    0.015 fractions.py:4(<module>)
     1354    0.001    0.000    0.004    0.000 sre_parse.py:212(get)
      100    0.001    0.000    0.001    0.000 {method 'close' of 'file' objects}
      100    0.001    0.000    0.104    0.001 ImageDraw.py:220(text)
     86/7    0.001    0.000    0.002    0.000 sre_compile.py:64(_compile)
        1    0.001    0.001    0.001    0.001 ImagePalette.py:19(<module>)
      100    0.001    0.000    0.029    0.000 Image.py:1339(point)
      100    0.001    0.000    0.001    0.000 {_hashlib.openssl_md5}
        1    0.001    0.001    0.018    0.018 TiffImagePlugin.py:42(<module>)
        1    0.001    0.001    0.009    0.009 decimal.py:116(<module>)
      100    0.001    0.000    0.001    0.000 genericpath.py:93(_splitext)
      900    0.001    0.000    0.001    0.000 ImageFilter.py:58(__init__)
      100    0.001    0.000    0.013    0.000 ImageFont.py:119(__init__)
      500    0.001    0.000    0.002    0.000 _binary.py:73(o32be)
      600    0.001    0.000    0.001    0.000 _binary.py:69(o16be)
      100    0.001    0.000    0.001    0.000 {PIL._imaging.zip_encoder}
      100    0.001    0.000    0.001    0.000 {method 'sort' of 'list' objects}
      100    0.001    0.000    0.089    0.001 ImageFont.py:150(getmask2)
      100    0.001    0.000    0.002    0.000 Image.py:426(_getencoder)
      248    0.001    0.000    0.002    0.000 random.py:238(randint)
      100    0.001    0.000    0.014    0.000 ImageFont.py:216(truetype)
      100    0.001    0.000    0.001    0.000 {method 'hexdigest' of '_hashlib.HASH' objects}
      100    0.001    0.000    0.001    0.000 posixpath.py:61(join)
      124    0.000    0.000    0.000    0.000 {method 'search' of '_sre.SRE_Pattern' objects}
      100    0.000    0.000    0.002    0.000 posixpath.py:97(splitext)
      202    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
   113/42    0.000    0.000    0.001    0.000 sre_parse.py:151(getwidth)
        1    0.000    0.000    0.000    0.000 GifImagePlugin.py:27(<module>)
      100    0.000    0.000    0.024    0.000 ImageFont.py:140(getsize)
      331    0.000    0.000    0.001    0.000 sre_parse.py:141(__getitem__)
        1    0.000    0.000    0.002    0.002 BmpImagePlugin.py:27(<module>)
      100    0.000    0.000    0.001    0.000 ImageDraw.py:112(_getink)
      100    0.000    0.000    0.000    0.000 Image.py:617(__getattr__)
      200    0.000    0.000    0.001    0.000 _util.py:7(isPath)
      363    0.000    0.000    0.000    0.000 {min}
        1    0.000    0.000    0.001    0.001 PngImagePlugin.py:34(<module>)
     40/7    0.000    0.000    0.008    0.001 sre_parse.py:317(_parse_sub)
      100    0.000    0.000    0.236    0.002 PngImagePlugin.py:664(write)
      100    0.000    0.000    0.000    0.000 ImageDraw.py:209(_multiline_check)
      306    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
      865    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
      207    0.000    0.000    0.000    0.000 {max}
        1    0.000    0.000    0.001    0.001 TiffTags.py:343(_populate)
      111    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x5617ae60e280}
      100    0.000    0.000    0.000    0.000 {method 'setimage' of 'ImagingEncoder' objects}
       28    0.000    0.000    0.000    0.000 sre_compile.py:256(_optimize_charset)
      200    0.000    0.000    0.000    0.000 {method 'draw_ink' of 'ImagingDraw' objects}
        3    0.000    0.000    0.000    0.000 UserDict.py:91(get)
      100    0.000    0.000    0.000    0.000 PngImagePlugin.py:660(__init__)
      272    0.000    0.000    0.000    0.000 sre_parse.py:206(match)
      100    0.000    0.000    0.000    0.000 TiffTags.py:26(__new__)
       71    0.000    0.000    0.000    0.000 collections.py:349(<genexpr>)
        1    0.000    0.000    0.000    0.000 ImageFile.py:30(<module>)
       28    0.000    0.000    0.000    0.000 sre_compile.py:228(_compile_charset)
        7    0.000    0.000    0.011    0.002 re.py:230(_compile)
        9    0.000    0.000    0.000    0.000 _weakrefset.py:58(__iter__)
        1    0.000    0.000    0.000    0.000 <string>:1(DecimalTuple)
        4    0.000    0.000    0.001    0.000 abc.py:86(__new__)
        1    0.000    0.000    0.001    0.001 PpmImagePlugin.py:18(<module>)
      156    0.000    0.000    0.000    0.000 sre_parse.py:137(__len__)
      100    0.000    0.000    0.000    0.000 {method 'cleanup' of 'ImagingEncoder' objects}
      6/2    0.000    0.000    0.000    0.000 abc.py:148(__subclasscheck__)
      113    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
       35    0.000    0.000    0.000    0.000 sre_compile.py:428(_simple)
      230    0.000    0.000    0.000    0.000 {method 'upper' of 'str' objects}
      100    0.000    0.000    0.000    0.000 {PIL._imaging.draw}
      106    0.000    0.000    0.000    0.000 sre_parse.py:149(append)
      100    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
        1    0.000    0.000    0.002    0.002 TiffTags.py:20(<module>)
       19    0.000    0.000    0.000    0.000 sre_parse.py:227(isname)
        1    0.000    0.000    0.000    0.000 ascii.py:8(<module>)
      100    0.000    0.000    0.000    0.000 ImageFile.py:66(_tilesort)
      100    0.000    0.000    0.000    0.000 <string>:8(__new__)
        4    0.000    0.000    0.000    0.000 abc.py:89(<genexpr>)
        1    0.000    0.000    0.000    0.000 {__import__}
      112    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
        3    0.000    0.000    0.000    0.000 decimal.py:3782(__init__)
        1    0.000    0.000    0.000    0.000 GimpGradientFile.py:16(<module>)
        7    0.000    0.000    0.011    0.002 sre_compile.py:567(compile)
       86    0.000    0.000    0.000    0.000 sre_parse.py:92(__init__)
      100    0.000    0.000    0.000    0.000 {callable}
        7    0.000    0.000    0.008    0.001 sre_parse.py:706(parse)
       18    0.000    0.000    0.000    0.000 {method 'format' of 'str' objects}
        6    0.000    0.000    0.000    0.000 decimal.py:514(__new__)
       23    0.000    0.000    0.000    0.000 sre_parse.py:74(opengroup)
       30    0.000    0.000    0.000    0.000 sre_parse.py:268(_escape)
        7    0.000    0.000    0.001    0.000 sre_compile.py:433(_compile_info)
       10    0.000    0.000    0.000    0.000 collections.py:375(<genexpr>)
        1    0.000    0.000    0.000    0.000 locale.py:349(_replace_encoding)
        7    0.000    0.000    0.000    0.000 TiffImagePlugin.py:589(_register_basic)
       18    0.000    0.000    0.000    0.000 _weakrefset.py:36(__init__)
        1    0.000    0.000    0.000    0.000 TiffImagePlugin.py:378(ImageFileDirectory_v2)
      104    0.000    0.000    0.000    0.000 sre_parse.py:221(isident)
        1    0.000    0.000    0.000    0.000 GimpPaletteFile.py:17(<module>)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
       56    0.000    0.000    0.000    0.000 {method 'find' of 'bytearray' objects}
        1    0.000    0.000    0.000    0.000 decimal.py:505(Decimal)
        5    0.000    0.000    0.000    0.000 _weakrefset.py:16(__init__)
        5    0.000    0.000    0.000    0.000 TiffImagePlugin.py:575(decorator)
        1    0.000    0.000    0.000    0.000 fractions.py:44(Fraction)
       27    0.000    0.000    0.000    0.000 decimal.py:3809(<genexpr>)
       24    0.000    0.000    0.000    0.000 {setattr}
        7    0.000    0.000    0.003    0.000 sre_compile.py:552(_code)
       21    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 PaletteFile.py:16(<module>)
        1    0.000    0.000    0.000    0.000 TiffImagePlugin.py:257(IFDRational)
        1    0.000    0.000    0.000    0.000 locale.py:495(getdefaultlocale)
       24    0.000    0.000    0.000    0.000 {method 'replace' of 'str' objects}
        2    0.000    0.000    0.000    0.000 sre_compile.py:411(_mk_bitmap)
        7    0.000    0.000    0.011    0.002 re.py:192(compile)
        1    0.000    0.000    0.000    0.000 ImageSequence.py:19(<module>)
       12    0.000    0.000    0.000    0.000 Image.py:2472(register_extension)
       10    0.000    0.000    0.000    0.000 {all}
        2    0.000    0.000    0.000    0.000 locale.py:365(normalize)
       35    0.000    0.000    0.000    0.000 sre_parse.py:145(__setitem__)
        7    0.000    0.000    0.000    0.000 {_sre.compile}
       19    0.000    0.000    0.000    0.000 ImageMode.py:23(__init__)
       14    0.000    0.000    0.000    0.000 sre_compile.py:546(isstring)
       23    0.000    0.000    0.000    0.000 sre_parse.py:85(closegroup)
        6    0.000    0.000    0.000    0.000 Image.py:2423(register_open)
       27    0.000    0.000    0.000    0.000 decimal.py:3816(<genexpr>)
       27    0.000    0.000    0.000    0.000 {_sre.getlower}
       28    0.000    0.000    0.000    0.000 TiffImagePlugin.py:335(_delegate)
        1    0.000    0.000    0.000    0.000 __init__.py:71(search_function)
        7    0.000    0.000    0.000    0.000 _weakrefset.py:83(add)
        3    0.000    0.000    0.000    0.000 {method 'match' of '_sre.SRE_Pattern' objects}
        1    0.000    0.000    0.000    0.000 <string>:1(_TagInfo)
        5    0.000    0.000    0.000    0.000 fractions.py:280(_operator_fallbacks)
        1    0.000    0.000    0.000    0.000 JpegPresets.py:67(<module>)
        5    0.000    0.000    0.000    0.000 _weakrefset.py:26(__exit__)
        1    0.000    0.000    0.000    0.000 decimal.py:3763(Context)
     10/2    0.000    0.000    0.000    0.000 {issubclass}
       23    0.000    0.000    0.000    0.000 {method 'remove' of 'list' objects}
       10    0.000    0.000    0.000    0.000 collections.py:373(<genexpr>)
        5    0.000    0.000    0.000    0.000 _weakrefset.py:20(__enter__)
        1    0.000    0.000    0.000    0.000 __init__.py:1025(getLogger)
        7    0.000    0.000    0.000    0.000 sre_parse.py:189(__init__)
        2    0.000    0.000    0.000    0.000 {repr}
        6    0.000    0.000    0.000    0.000 Image.py:2449(register_save)
        8    0.000    0.000    0.000    0.000 sre_compile.py:101(fixup)
        1    0.000    0.000    0.000    0.000 __init__.py:1071(_fixupParents)
        6    0.000    0.000    0.000    0.000 {method 'split' of 'str' objects}
        7    0.000    0.000    0.000    0.000 {_struct.calcsize}
        1    0.000    0.000    0.000    0.000 threading.py:147(acquire)
        2    0.000    0.000    0.000    0.000 __init__.py:49(normalize_encoding)
        1    0.000    0.000    0.000    0.000 _abcoll.py:24(_hasattr)
        1    0.000    0.000    0.000    0.000 ImageChops.py:18(<module>)
       61    0.000    0.000    0.000    0.000 {method 'isalnum' of 'str' objects}
        1    0.000    0.000    0.000    0.000 TiffImagePlugin.py:796(ImageFileDirectory_v1)
        1    0.000    0.000    0.000    0.000 {method 'encode' of 'unicode' objects}
        1    0.000    0.000    0.000    0.000 TiffImagePlugin.py:889(TiffImageFile)
        7    0.000    0.000    0.000    0.000 sre_parse.py:67(__init__)
        1    0.000    0.000    0.000    0.000 codecs.py:92(__new__)
        1    0.000    0.000    0.000    0.000 threading.py:187(release)
        1    0.000    0.000    0.000    0.000 abc.py:105(register)
        1    0.000    0.000    0.000    0.000 __init__.py:1127(__init__)
        1    0.000    0.000    0.000    0.000 GifImagePlugin.py:53(GifImageFile)
       54    0.000    0.000    0.000    0.000 {ord}
        1    0.000    0.000    0.000    0.000 ascii.py:41(getregentry)
        5    0.000    0.000    0.000    0.000 Image.py:2438(register_mime)
        2    0.000    0.000    0.000    0.000 locale.py:449(_parse_localename)
        1    0.000    0.000    0.000    0.000 {method 'decode' of 'str' objects}
        1    0.000    0.000    0.000    0.000 locale.py:548(getlocale)
        6    0.000    0.000    0.000    0.000 {method 'translate' of 'str' objects}
       12    0.000    0.000    0.000    0.000 {method 'remove' of 'set' objects}
        5    0.000    0.000    0.000    0.000 _weakrefset.py:52(_commit_removals)
        1    0.000    0.000    0.000    0.000 __init__.py:1567(getLogger)
        1    0.000    0.000    0.000    0.000 {_codecs.utf_8_decode}
        1    0.000    0.000    0.000    0.000 BmpImagePlugin.py:61(BmpImageFile)
       10    0.000    0.000    0.000    0.000 {method '__contains__' of 'frozenset' objects}
        1    0.000    0.000    0.000    0.000 utf_8.py:15(decode)
        2    0.000    0.000    0.000    0.000 _abcoll.py:26(<genexpr>)
        1    0.000    0.000    0.000    0.000 _abcoll.py:128(__subclasshook__)
        5    0.000    0.000    0.000    0.000 TiffImagePlugin.py:574(_register_loader)
        1    0.000    0.000    0.000    0.000 ImageFile.py:75(ImageFile)
        5    0.000    0.000    0.000    0.000 TiffImagePlugin.py:584(decorator)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:280(PngStream)
        1    0.000    0.000    0.000    0.000 ImagePalette.py:26(ImagePalette)
        1    0.000    0.000    0.000    0.000 __init__.py:205(_acquireLock)
        1    0.000    0.000    0.000    0.000 ascii.py:13(Codec)
        4    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}
        1    0.000    0.000    0.000    0.000 ImageFile.py:304(Parser)
        3    0.000    0.000    0.000    0.000 {method 'strip' of 'str' objects}
        5    0.000    0.000    0.000    0.000 {method '__subclasshook__' of 'object' objects}
        1    0.000    0.000    0.000    0.000 {_locale.setlocale}
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:203(PngInfo)
       10    0.000    0.000    0.000    0.000 {method 'isdigit' of 'str' objects}
        5    0.000    0.000    0.000    0.000 TiffImagePlugin.py:583(_register_writer)
        1    0.000    0.000    0.000    0.000 JpegImagePlugin.py:282(JpegImageFile)
        1    0.000    0.000    0.000    0.000 {method 'acquire' of 'thread.lock' objects}
       10    0.000    0.000    0.000    0.000 {method 'group' of '_sre.SRE_Match' objects}
        1    0.000    0.000    0.000    0.000 __init__.py:214(_releaseLock)
        1    0.000    0.000    0.000    0.000 __init__.py:177(_checkLevel)
       20    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
        3    0.000    0.000    0.000    0.000 UserDict.py:103(__contains__)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:97(ChunkStream)
        1    0.000    0.000    0.000    0.000 __init__.py:976(append)
        1    0.000    0.000    0.000    0.000 GimpPaletteFile.py:24(GimpPaletteFile)
        2    0.000    0.000    0.000    0.000 {sys._getframe}
        1    0.000    0.000    0.000    0.000 ImageFile.py:274(StubImageFile)
        2    0.000    0.000    0.000    0.000 threading.py:64(_note)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:183(iTXt)
        5    0.000    0.000    0.000    0.000 {method '__subclasses__' of 'type' objects}
        1    0.000    0.000    0.000    0.000 ImageSequence.py:19(Iterator)
        1    0.000    0.000    0.000    0.000 decimal.py:3748(_ContextManager)
        1    0.000    0.000    0.000    0.000 Image.py:2460(register_save_all)
        1    0.000    0.000    0.000    0.000 decimal.py:5438(_WorkRep)
        1    0.000    0.000    0.000    0.000 {method 'index' of 'str' objects}
        1    0.000    0.000    0.000    0.000 TiffTags.py:23(TagInfo)
        1    0.000    0.000    0.000    0.000 UserDict.py:35(__getitem__)
        1    0.000    0.000    0.000    0.000 ascii.py:20(IncrementalEncoder)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:515(PngImageFile)
        1    0.000    0.000    0.000    0.000 {method 'setter' of 'property' objects}
        1    0.000    0.000    0.000    0.000 GimpGradientFile.py:61(GradientFile)
        1    0.000    0.000    0.000    0.000 decimal.py:310(Subnormal)
        1    0.000    0.000    0.000    0.000 decimal.py:234(DivisionByZero)
        1    0.000    0.000    0.000    0.000 {method 'lstrip' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {any}
        1    0.000    0.000    0.000    0.000 decimal.py:284(InvalidContext)
        1    0.000    0.000    0.000    0.000 GimpGradientFile.py:102(GimpGradientFile)
        1    0.000    0.000    0.000    0.000 decimal.py:160(DecimalException)
        1    0.000    0.000    0.000    0.000 decimal.py:5696(__init__)
        1    0.000    0.000    0.000    0.000 PpmImagePlugin.py:59(PpmImageFile)
        1    0.000    0.000    0.000    0.000 ascii.py:24(IncrementalDecoder)
        2    0.000    0.000    0.000    0.000 {thread.get_ident}
        1    0.000    0.000    0.000    0.000 decimal.py:261(DivisionUndefined)
        1    0.000    0.000    0.000    0.000 decimal.py:321(Overflow)
        1    0.000    0.000    0.000    0.000 decimal.py:224(ConversionSyntax)
        1    0.000    0.000    0.000    0.000 decimal.py:183(Clamped)
        1    0.000    0.000    0.000    0.000 PngImagePlugin.py:657(_idat)
        1    0.000    0.000    0.000    0.000 PaletteFile.py:22(PaletteFile)
        1    0.000    0.000    0.000    0.000 ascii.py:34(StreamConverter)
        1    0.000    0.000    0.000    0.000 decimal.py:272(Inexact)
        1    0.000    0.000    0.000    0.000 decimal.py:250(DivisionImpossible)
        3    0.000    0.000    0.000    0.000 {abs}
        1    0.000    0.000    0.000    0.000 ascii.py:31(StreamReader)
        1    0.000    0.000    0.000    0.000 __init__.py:587(__init__)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 decimal.py:5692(_Log10Memoize)
        1    0.000    0.000    0.000    0.000 decimal.py:195(InvalidOperation)
        1    0.000    0.000    0.000    0.000 decimal.py:359(Underflow)
        1    0.000    0.000    0.000    0.000 BmpImagePlugin.py:211(DibImageFile)
        1    0.000    0.000    0.000    0.000 decimal.py:298(Rounded)
        1    0.000    0.000    0.000    0.000 {method 'release' of 'thread.lock' objects}
        1    0.000    0.000    0.000    0.000 ascii.py:28(StreamWriter)



real	0m29.797s
user	0m28.404s
sys	0m0.376s

Python 3.5.2+

This script requires the Python Imaging Library - http://www.pythonware.com/products/pil/

...

python3-pil is already the newest version (3.3.1-1).
Reedy added a comment.EditedFeb 12 2017, 1:36 AM

Looks like python3 is no better as it stands on a single thread at least (though, this vm only has one core)

$ time python 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '100' --dirs '0' --wordlist '/home/reedy/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/home/reedy/badwords'
Generating 100 CAPTCHA images separated in 100 image(s) per chunk run by 1 threads...

real	0m26.087s
user	0m24.716s
sys	0m0.364s
$ time python3 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '100' --dirs '0' --wordlist '/home/reedy/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/home/reedy/badwords'
Generating 100 CAPTCHA images separated in 100 image(s) per chunk run by 1 threads...

real	0m26.845s
user	0m25.652s
sys	0m0.264s
Reedy added a comment.Feb 12 2017, 1:40 AM

Really needs testing on a machine with many cores...

$ time python 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '100' --dirs '0' --wordlist '/home/reedy/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/home/reedy/badwords' --threads 4
Generating 100 CAPTCHA images separated in 25 image(s) per chunk run by 4 threads...

real	0m28.953s
user	0m27.396s
sys	0m0.644s
$ time python3 'captcha-old.py' --key 'CHANGE_THIS_SECRET!' --output '/tmp/mw-fancycaptcha-1486771866-0b4ca9' --count '100' --dirs '0' --wordlist '/home/reedy/words' --font '/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf' --blacklist '/home/reedy/badwords' --threads 4
Generating 100 CAPTCHA images separated in 25 image(s) per chunk run by 4 threads...

real	0m27.708s
user	0m26.364s
sys	0m0.504s

Python 3.5.2+

This script requires the Python Imaging Library - http://www.pythonware.com/products/pil/

...

python3-pil is already the newest version (3.3.1-1).

Debian python3-pil package? If so, this isn't the PIL, but instead pillow, iirc. I remember, there was a change to convert the cpatcha.py code from PIL to pillow, as PIL isn't supported anymore.

... Oh no, it wasn't pilow, but a "more current version" of PIL: https://gerrit.wikimedia.org/r/#/c/231157/2

Reedy added a comment.Feb 12 2017, 3:02 PM

As per the comments after, and also T157888 and the patch in that... I got past that ;)

Hmm, I've a slightly different measure on my 2-core VM:

florian@florian-VirtualBox:/var/www/html/mediawiki/w/extensions/ConfirmEdit$ time python captcha.py --wordlist /usr/share/dict/american-english --font /usr/share/fonts/opentype/noto/NotoSansCJK-Black.ttc --count 100 --key fioegjfrp --output ../../../captchas/ --threads=2
Generating 100 CAPTCHA images separated in 50 image(s) per chunk run by 2 threads...

real	0m10.646s
user	0m13.780s
sys	0m0.648s
florian@florian-VirtualBox:/var/www/html/mediawiki/w/extensions/ConfirmEdit$ time python captcha.py --wordlist /usr/share/dict/american-english --font /usr/share/fonts/opentype/noto/NotoSansCJK-Black.ttc --count 100 --key fioegjfrp --output ../../../captchas/ --threads=1
Generating 100 CAPTCHA images separated in 100 image(s) per chunk run by 1 threads...

real	0m15.182s
user	0m14.676s
sys	0m0.216s
florian@florian-VirtualBox:/var/www/html/mediawiki/w/extensions/ConfirmEdit$ time python captcha.py --wordlist /usr/share/dict/american-english --font /usr/share/fonts/opentype/noto/NotoSansCJK-Black.ttc --count 100 --key fioegjfrp --output ../../../captchas/ --threads=2
Generating 100 CAPTCHA images separated in 50 image(s) per chunk run by 2 threads...

real	0m10.759s
user	0m14.180s
sys	0m0.528s
florian@florian-VirtualBox:/var/www/html/mediawiki/w/extensions/ConfirmEdit$ time python captcha.py --wordlist /usr/share/dict/american-english --font /usr/share/fonts/opentype/noto/NotoSansCJK-Black.ttc --count 100 --key fioegjfrp --output ../../../captchas/ --threads=1
Generating 100 CAPTCHA images separated in 100 image(s) per chunk run by 1 threads...

real	0m14.144s
user	0m13.680s
sys	0m0.184s

Running it now again with 1000 CAPTCHAS to see the difference there.

florian@florian-VirtualBox:/var/www/html/mediawiki/w/extensions/ConfirmEdit$ time python captcha.py --wordlist /usr/share/dict/american-english --font /usr/share/fonts/opentype/noto/NotoSansCJK-Black.ttc --count 1000 --key fioegjfrp --output ../../../captchas/ --threads=2
Generating 1000 CAPTCHA images separated in 500 image(s) per chunk run by 2 threads...

real	1m44.139s
user	2m24.104s
sys	0m5.696s
florian@florian-VirtualBox:/var/www/html/mediawiki/w/extensions/ConfirmEdit$ time python captcha.py --wordlist /usr/share/dict/american-english --font /usr/share/fonts/opentype/noto/NotoSansCJK-Black.ttc --count 1000 --key fioegjfrp --output ../../../captchas/ --threads=1
Generating 1000 CAPTCHA images separated in 1000 image(s) per chunk run by 1 threads...

real	2m23.425s
user	2m21.032s
sys	0m1.552s
demon added a subscriber: demon.Apr 25 2017, 12:01 AM

So based on this... Multi threading, at least, in it's current implementation, makes no improvement? :(

You'd probably want to use something like multiprocessing instead of threads. Threads really aren't all that useful--they use the same I/O scheduler and have the global interpeter lock. Threads technically have a slightly lower startup cost, but this is really negligible for tasks of any measurable length--most benchmarks you'll find here are pointless.

As best I can tell you're not actually needing to share any memory between these processes--everything is passed as a parameter to your function--you'd probably see a drastic improvement in moving from Thread() to Pool()

With multiprocessing:

time python captcha.py --font=/usr/share/fonts/truetype/freefont/FreeSans.ttf --wordlist=/usr/share/dict/words --key=FOO --output=../../../captchas --threads=2 --count=1000
Generating 1000 CAPTCHA images separated in 500 image(s) per chunk run by 2 threads...

real	0m57.557s
user	1m48.760s
sys	0m2.036s

Without:

time python captcha.py --font=/usr/share/fonts/truetype/freefont/FreeSans.ttf --wordlist=/usr/share/dict/words --key=FOO --output=../../../captchas --threads=1 --count=1000
Generating 1000 CAPTCHA images separated in 1000 image(s) per chunk run by 1 threads...

real	1m47.056s
user	1m45.784s
sys	0m0.764s
Reedy added a comment.Jun 12 2017, 4:31 PM

So... That looks like a decent improvement. Results confirmed locally

reedy@ubuntu64-web-esxi:/var/www/wiki/mediawiki/extensions/ConfirmEdit$ time python captcha.py --font=/usr/share/fonts/truetype/freefont/FreeSans.ttf --wordlist=/usr/share/dict/words --key=FOO --output=/tmp/captchas --count=1000

real	5m1.770s
user	5m0.188s
sys	0m1.168s

After pulling the patch...

reedy@ubuntu64-web-esxi:/var/www/wiki/mediawiki/extensions/ConfirmEdit$ time python captcha.py --font=/usr/share/fonts/truetype/freefont/FreeSans.ttf --wordlist=/usr/share/dict/words --key=FOO --output=/tmp/captchas --threads=1 --count=1000
Generating 1000 CAPTCHA images separated in 1000 image(s) per chunk run by 1 threads...

real	5m3.015s
user	5m0.752s
sys	0m1.920s
reedy@ubuntu64-web-esxi:/var/www/wiki/mediawiki/extensions/ConfirmEdit$ time python captcha.py --font=/usr/share/fonts/truetype/freefont/FreeSans.ttf --wordlist=/usr/share/dict/words --key=FOO --output=/tmp/captchas --threads=2 --count=1000
Generating 1000 CAPTCHA images separated in 500 image(s) per chunk run by 2 threads...

real	2m54.467s
user	5m40.848s
sys	0m5.144s

Change 337057 merged by jenkins-bot:
[mediawiki/extensions/ConfirmEdit@master] Add threads parameter to captcha.py for multithread CAPTCHA generation

https://gerrit.wikimedia.org/r/337057

Reedy closed this task as Resolved.Jun 12 2017, 4:44 PM

Thanks Florian!

*yay* Thanks for merging Reedy!