验证码图片识别

因朋友需求,对某网站的验证码图片进行自动识别,原以为是个复杂的问题,后来查看了网上的一些资料,总体思路上参考了:

http://www.cr173.com/html/16871_1.html

尝试用Delphi做了Demo,过程如下

1、获取到验证码图片生成的URL,如http://www.aaa.bbb.cn/ValidateCode.aspx

2、使用TIdHTTP控件通过URL获取图片,由于URL获取验证码图片是以数据流形式传递过来的,所以处理非常方便,主要代码如下:

procedure TfrmMain.Button1Click(Sender: TObject); 
var 
  ms: TMemoryStream; 
  pi : TPngImage; 
begin 
  ms := TMemoryStream.Create; 
  pi := TPngImage.Create; 
  try 
    try 
      { 获取校验图片,存入数据流中 } 
      IdHTTP1.Get(Edit1.Text, ms); 
      { 从流中载入png图片 } 
      ms.Position := 0; 
      pi.LoadFromStream(ms);

     { 显示图片 } 
      Image1.Picture.Graphic := pi; 
  …

end;

识别主要分为三个步骤:第一步进行图片处理,最好形成二值化,第二步需要进行学习并保存特征码,第三步完成正常的识别功能。

3、图片处理

图片处理过程包括:

(1)将png图片转换为bmp图片,在Delphi下非常简单:

bmp1.Assign(Image1.Picture.Graphic);

(2)进行图片二值化

对于本次识别的图片,非常简单,如下图:

image

进行色彩分析,将相同颜色的像素进行计数后发现只有三种颜色:背景色、干扰色和字体蓝色。做二值化处理时就比较简单了,逐个处理像素点,将干扰色处理成背景色:

 for w := 0 to ABmpDesc.Width -1 do 
    for h := 0 to ABmpDesc.Height -1 do 
    begin 
      c := ABmpDesc.Canvas.Pixels[w, h];

      { 不是字体颜色的都处理成背景色 } 
      if c <> AFontColor then 
        ABmpDesc.Canvas.Pixels[w, h] := ABkColor; 
    end;

效果如下:

image

(3)进行图片分割

其实花时间最多的就是这步了,开始时考虑字符之间存在间隙,所以算法上以2个字符间的间隙为标准划分,结果发现存在多个字符连接在一起的情况,如下图:

image

2个TT实际是连在一起的,中间没有分割间隙,所以无法区分。后来又考虑采用等宽字符方式进行切割(网上常用算法),但是碰到了验证码图片中的字符是不等宽的,还是不行。后采用了字符轨迹法,由于都是英文字符和数字,单个字符或数字的笔画都是连在一起的,就把所有连在一起的笔画内容识别为同一个字符。采用递归算法,取左上第一个字符的第一个像素,然后对该像素周围8个像素进行扫描,记录相邻的像素,并对符合字体颜色的像素再次递归运算,直到没有符合字体颜色的像素为止。效果还是可以,但是仍然没有解决2个字符连在一起的情况,最后跳出这个思维圈子,干脆将连在一起的2个或多个字符共同识别为一个整体,即TT就当是一个字符处理,问题基本得到解决。

(4)进行分割后图片特征码提取

特征码非常简单,采用从上往下,从左向右依次获取每个像素,如果是字体颜色像素则为1,否则为0。当然也可以采用其他算法的特征码,只要能确定唯一性即可。然后将特征码与字符关联,并保存起来。

function GetbmpFlag(Abmp: TBitmap; AFontColor : TColor): String;
var
  w, h: Integer;
begin
  Result := '';
  { 获取图片特征码 }
  for h := 0 to Abmp.Height -1 do
    for w := 0 to Abmp.Width -1 do
      if Abmp.Canvas.Pixels[w, h] = AFontColor then
        Result := Result + '0'
      else
        Result := Result + '1';
end;

4、学习

反复获取验证码图片,进行上述处理,并填写人工识别后的验证码,以便软件将字符对应的图片特征码与字符关联起来,我的学习界面如下:

image

将学习好的特征码保存到文件,以便在下次识别时载入。

5、正常识别

特征码学习差不多后保存在磁盘文件上,当正式识别时,载入该特征码,按照上述步骤进行处理:

(1)获取验证码图片

(2)二值化处理

(3)图片分割

(4)获取图片特征码

(5)根据图片特征码,在已学习的特征码中进行查找,找到后返回其对应的字符,将所有分割后图片的特征码对应的字符组合起来就是验证码。

 

上述工作真正的难点在于2个:

1、二值化图片

本次试验的图片比较简单,如果遇到非常复杂的图片,如:

1

且字体颜色还是随机的,就比较麻烦了,必须对所有像素进行颜色统计,取其最多的4个颜色,我的统计图如下:

image

颜色最多的4个即为字体颜色。但是也有特殊情况,如下图:

2012090511294879164

其统计图就比较杂乱了,如下图:

image

字体颜色统计被背景颜色掩盖了,按照上文提示,应采用HSL对色彩进行变化,然后进行统计,这项工作待有时间再进行。

 

2、图片切割

本例中的图片切割还是比较简单的,对于复杂的、不规则的图片,其切割可能更复杂。尤其是遇到多个字符上下部分位置有交叉,字符有随机大小,字符与字符之间有粘连时,这种图片切割更加复杂,这里不再讨论,今后有时间再试试。

posted @ 2014-11-08 20:51  玻璃城市  阅读(4174)  评论(0编辑  收藏  举报