处理像Discuz最新版那种比较棘手的验证码,确实要费点功夫。不过话说回来,核心原理和普通验证码识别其实没啥两样——无非是多了一道背景处理的工序。下面就以DZ论坛的动画验证码为例,把整个识别思路拆开来讲。

第一步要干的事,就是把背景去掉。这类验证码的背景往往没那么干净,用过去的老办法基本行不通。上面那张图还算客气的,实践中遇到过不少图片,背景色和字母色非常接近,而且字母颜色在变,背景颜色也在变,简直像一锅乱炖。





一个直观的思路是:找到图片中使用频率最高的颜色。具体做法就是用HSL表示每个点的颜色,然后统计所有点的亮度值累加,找出几个最大的峰值——这些就是图片中颜色最丰富的区域对应的亮度累计值。

剩下的基本都是噪音。接下来只要对每个峰值做分割,就能得到下面这样的图片。

你看,这样就把单个颜色的图片单独拎出来了。然后需要做的,是把那些除了黑色和白色以外的图片过滤掉。

接着进行灰化处理、阈值处理、降噪,结果就是下面这个样子。

再根据边界检测得到的最左侧x位置,就能把字母顺序排好。

到了这一步,后面的事就轻车熟路了:把图片转成标准模板,然后只需要少量学习,就能达到95%以上的识别率。
c:15 j:8 8:7 t:9 9:4 x:7 4:6 2:4 h:7 f:8 e:18 b:5 y:3 k:4 w:3 g:5 3:5 7:6 r:2 m:3 q:4 v:2 p:3 6:2
上面这串数据的意思是:字母c学习了15次,j学习了8次,以此类推。

只要字符之间不粘连,绝大多数验证码干扰技术其实都有办法应对。这也是为什么Google的验证码看起来很简单,但至今没人能破解得好的原因。
补充一下关于杂点的问题
有朋友在留言中提到,某些字符里混进了杂点。这种验证码虽然不算太普遍,但稍微研究了一下怎么处理。

比如“CY3E”这张图,字母3里面有杂点,其他的没有。按照上文的方法,怎么判断这个3不是和其他颜色杂点一样的图片呢?

这里需要加一个步骤:对每次过滤颜色生成出来的图片进行填充。
先找到字母3的杂点原图:

然后做一个算法填充:

要把这张图和那些全是杂点的图片区分开,可以考虑从以下两个维度入手:
1. 连贯点的宽度
2. 连贯点的个数
这样一来,剩下的就是“CY3E”过滤后的干净图片了。
至于字符倾斜的问题,完全可以在机器学习的过程中解决——把正在学习的图片自己旋转一定角度,比如从-10度到+10度。当然,学习库会因此大一些,但就10个数字这种验证码来说,这点性能损失基本可以忽略不计。
