因为看到学校的饭卡网站坑爹,而我的技术又不到家黑不了这个网站,而他只有6位密码,所以想暴力试试(捉弄一下小伙伴)。因为有验证码的关系,暴力的期望时间大概为14个小时。完全可以接受啊。但首先是要搞定这验证码啊!话说这事很久以前我就想做了,因为之前就听说过tesseract这吊炸玩意。所以这里讲的不是如何去写个神经网络来训练,也不是opencv来解决。如果感兴趣的话可以参考最下面的链接。
初步尝试
怎么安装我就不说了,参考主页。我要使用python,网上也有封装好的利用tesseract的库,但是我死活搞不定所以索性就不用了。毕竟只要执行一个命令就行了。先来说我要破解的验证码长什么样,如下图(原验证码),毕竟需求不同。但是原理大同小异。然后输入 tesseract captcha.jpg 2 -psm 6 digits 。这命令含义就是假定是一块数据(-psm 6),而且全是数字(digits),输出到2.txt。这里的digits是配置文件。在/usr/share/tessdata/configs/digits,内容只有一行: tessedit_char_whitelist 0123456789-. 但是2.txt里居然什么也没有。网上查了查,好像是因为DPI不够,这不是坑爹吗。确实图片比较小。根据他的ImproveQuqlity,我发现我需要做两件事。一是去掉边框,二是去掉噪点。
手动去噪
用GIMP对原图来了一发。效果如下图(GIMP处理后)。同样的命令,结果就出来了。但是好像哪里不对。我这是要暴力登录啊,这样处理图片的话还不如手动输入验证码。所以还得自己写。
Python去噪
很简单的思路,就是把边框去掉,然后把单独的黑点去掉。这个已经差不多了,要考虑4这个奇葩的数字,还有这可以的dpi。我走了不少弯路,其实质的提高是对图片分割。而且不能分开后一张张算,要在原图中分割。经过观察,这些数字的位置基本不变,在像素点1,10,19,28的位置加一条白线即可。效果如下(python处理)。这个程序的准确率居然是精准的2/3。。可以接受。程序代码如下,都没有100行,很简单啊。我已经破了两个小伙伴的密码了。很可惜,都是身份证后六位,没改过不好玩。



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | import sys import os import re from PIL import Image def erase_block(filename): """Make little black block """ img = Image.open(filename) pixdata = img.load() for y in xrange(img.size[1]): pixdata[0, y] = (255, 255, 255) pixdata[10, y] = (255, 255, 255) pixdata[19, y] = (255, 255, 255) pixdata[28, y] = (255, 255, 255) pixdata[img.size[0]-1,y] = (255, 255, 255) pixdata[img.size[0]-2,y] = (255, 255, 255) pixdata[img.size[0]-3,y] = (255, 255, 255) pixdata[img.size[0]-4,y] = (255, 255, 255) pixdata[img.size[0]-5,y] = (255, 255, 255) for x in xrange(img.size[0]): pixdata[x, 0] = (255, 255, 255) pixdata[x, 1] = (255, 255, 255) pixdata[x, 2] = (255, 255, 255) pixdata[x, img.size[1]-1] = (255, 255, 255) pixdata[x, img.size[1]-2] = (255, 255, 255) pixdata[x, img.size[1]-3] = (255, 255, 255) for y in xrange(1,img.size[1]-1): for x in xrange(1,img.size[0]-1): if pixdata[x, y][0] > 50 or pixdata[x,y][1] > 50 or pixdata[x,y][2] > 50: # make dark color black pixdata[x, y] = (255, 255, 255) else: # make light color white num = 0 if pixdata[x+1, y][0] > 50 or pixdata[x+1,y][1] > 50 or pixdata[x+1,y][2] > 50: num += 1 if pixdata[x-1, y][0] > 50 or pixdata[x-1,y][1] > 50 or pixdata[x-1,y][2] > 50: num += 1 if pixdata[x, y+1][0] > 50 or pixdata[x,y+1][1] > 50 or pixdata[x,y+1][2] > 50: num += 1 if pixdata[x, y-1][0] > 50 or pixdata[x,y-1][1] > 50 or pixdata[x,y-1][2] > 50: num += 1 if (num == 4): pixdata[x, y] = (255, 255, 255) else: pixdata[x, y] = (0,0,0) img.save("erase_" + filename) def tesseract(image, args): """Decode image with Tesseract """ # perform OCR output_filename = image + ".txt" # easiest way to call tesseract os.system('tesseract ' + image + ' ' + output_filename + args ) # read in result from output file result = open(output_filename+".txt").read() os.remove(output_filename+".txt") os.remove(image) return clean(result) def clean(s): """Standardize the OCR output """ # remove non-alpha numeric text return re.sub('[\W]', '', s) def getstr(filename): erase_block(filename) #threshold("erase_" + filename) return tesseract("erase_" + filename, " -psm 6 digits") if __name__ == '__main__': filename = sys.argv[1] print getstr(filename) |
训练数据
所以说写博客还是很重要的。写的时候,我发现tesseract还自带训练。但是用在验证码处理就不太合适了。这是为没有特定字符的文字训练的,比如中文。而且感觉还比较复杂,不过有GUI工具。我就不去深入研究了。
tesseract 弄 jaccount 的验证码也是各种无压力。。前段时间准备写个刷 toefl 的,用 tesseract 还是不理想。。toefl 的验证码噪音太大
我靠。。你怎么知道我更新了。。 jaccount验证码无力吐槽,不过tesseract真牛啊