mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/ConfirmEdit
synced 2024-11-11 17:00:49 +00:00
Improve FancyCaptcha resistance to OCR
Tesseract is a popular open source OCR package. Running it on FancyCaptcha images, with no training or configuration, yielded a 56% break rate. By restricting the character set, the OCR break rate was improved to 66%. So: * Reduce k, increase wob scale, increase rr fuzz. The net effect of these three changes is to more reliably bend the baseline. In the old captcha, the baseline would often be bent by chance, but when it wasn't bent, it provided a very easy challenge for the OCR engine. This reduced the break rate from 66% to around 40%. * Introduce additive noise, based on a bilinear upscale of a random greyscale image. This, combined with the above change, reduces the Tesseract break rate to 6%. Change-Id: I05b5bb6475de9378cd89cce13b1b2f28b32cd405
This commit is contained in:
parent
f552e05158
commit
df4806c64c
24
captcha.py
24
captcha.py
|
@ -39,6 +39,7 @@ try:
|
|||
import ImageDraw
|
||||
import ImageEnhance
|
||||
import ImageOps
|
||||
import ImageMath
|
||||
except:
|
||||
sys.exit("This script requires the Python Imaging Library - http://www.pythonware.com/products/pil/")
|
||||
|
||||
|
@ -49,7 +50,7 @@ def wobbly_copy(src, wob, col, scale, ang):
|
|||
x, y = src.size
|
||||
f = random.uniform(4*scale, 5*scale)
|
||||
p = random.uniform(0, math.pi*2)
|
||||
rr = ang+random.uniform(-30, 30) # vary, but not too much
|
||||
rr = ang+random.uniform(-10, 10) # vary, but not too much
|
||||
int_d = Image.new('RGB', src.size, 0) # a black rectangle
|
||||
rot = src.rotate(rr, Image.BILINEAR)
|
||||
# Do a cheap bounding-box op here to try to limit work below
|
||||
|
@ -86,8 +87,8 @@ def gen_captcha(text, fontname, fontsize, file_name):
|
|||
x, y = im.size
|
||||
# add the text to the image
|
||||
d.text((x/2-dim[0]/2, y/2-dim[1]/2), text, font=font, fill=fgcolor)
|
||||
k = 3
|
||||
wob = 0.20*dim[1]/k
|
||||
k = 2
|
||||
wob = 0.09*dim[1]
|
||||
rot = 45
|
||||
# Apply lots of small stirring operations, rather than a few large ones
|
||||
# in order to get some uniformity of treatment, whilst
|
||||
|
@ -102,9 +103,24 @@ def gen_captcha(text, fontname, fontsize, file_name):
|
|||
bbox = im.getbbox()
|
||||
bord = min(dim[0], dim[1])/4 # a bit of a border
|
||||
im = im.crop((bbox[0]-bord, bbox[1]-bord, bbox[2]+bord, bbox[3]+bord))
|
||||
|
||||
# Create noise
|
||||
nblock = 4
|
||||
nsize = (im.size[0] / nblock, im.size[1] / nblock)
|
||||
noise = Image.new('L', nsize, bgcolor)
|
||||
data = noise.load()
|
||||
for x in range(nsize[0]):
|
||||
for y in range(nsize[1]):
|
||||
r = random.randint(0, 100)
|
||||
data[x, y] = r
|
||||
# Turn speckles into blobs
|
||||
noise = noise.resize(im.size, Image.BILINEAR)
|
||||
# Add to the image
|
||||
im = ImageMath.eval('convert(convert(a, "L") / 2 + b, "RGB")', a=im, b=noise)
|
||||
|
||||
# and turn into black on white
|
||||
im = ImageOps.invert(im)
|
||||
|
||||
|
||||
# save the image, in format determined from filename
|
||||
im.save(file_name)
|
||||
|
||||
|
|
Loading…
Reference in a new issue