搜索
查看: 11590|回复: 6
打印 上一主题 下一主题

动态验证码字符完美分割(附算法)

  [复制链接]

8

主题

18

帖子

194

积分

一级会员

Rank: 2

积分
194
跳转到指定楼层
楼主
发表于 2013-5-31 13:31:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 wulitoux 于 2013-5-31 14:43 编辑

先来看看我们今天要处理的验证码图片。该图片为gif格式,图片共有八帧,前四帧分别和后四帧类似,只是字符的位置稍有变化。每一帧图片中只有一个字符是清晰的,其余三个字符模糊,


对于动态图片,当然首先要提取出静态图像。aardio的gdip源码中有四个函数:
ImageGetFrameDimensionsCount = Gdiplus.api("GdipImageGetFrameDimensionsCount","int(POINTER Image,int& count)")
ImageGetFrameDimensionsList = Gdiplus.api("GdipImageGetFrameDimensionsList","int(POINTER Image,struct& dimensionIDs,int count)")
ImageGetFrameCount = Gdiplus.api("GdipImageGetFrameCount","int(POINTER Image,struct& dimensionID,int& count)")
ImageSelectActiveFrame = Gdiplus.api("GdipImageSelectActiveFrame","int(POINTER Image,struct& dimensionID,int frameIndex)")
可用于提取gif中的静态图片,提取后的前四帧图片如下:





第n张图片对应第n个字符是清晰的,接下来就是利用投影算法将第n张图片中的第n个字符提取出来。把分割后的图片存在工程目录下,算法如下,各位根据实际情况可自行调整:
  1. for(filename=1;4;1){
  2.         io.print("开始处理"++filename++"个图片")
  3.         bmp=gdip.bitmap(""++filename++".bmp")
  4.         bmp=binarization(bmp,,true)//二值化
  5.         //初始化统计数组
  6.         stas={};
  7.         for(j=0;bmp.width-1;1){
  8.                 stas[j]=0;
  9.         }
  10.         //生成直方图
  11.         for(i=0;bmp.height-1;1){
  12.                 for(j=0;bmp.width-1;1){
  13.                         if(bmp.getPixel(j,i)==-16777216){
  14.                                 stas[j]+=1;
  15.                         }
  16.                 }
  17.         }
  18.         //找出最大值
  19.         max=0
  20.         for(j=0;bmp.width-1;1){
  21.                 if(stas[j]>max){
  22.                         max=stas[j]
  23.                 }
  24.         }
  25.         //分割个数
  26.         splitNum=4;
  27.         superGroup={};//超级大组,用来装大组。
  28.         for(charSize=6;15;1){//字符宽度
  29.                 //用一条y轴线从0扫到max
  30.                 for(y=0;max;1){
  31.                         g={};//将数据存入数组后用于分出相似组
  32.                         for(j=0;bmp.width-1;1){
  33.                                 if(stas[j]<=y){
  34.                                         table.push(g,j);
  35.                                 }
  36.                         }
  37.                         //先把数组排序
  38.                         table.sort(g);
  39.                         bigGroup={};//大组,用来装小分组
  40.                         for(i=1;#g;1){
  41.                                 group={}//小分组
  42.                                 for(e=i+1;#g;1){
  43.                                         if(g[e]-g[e-1]>charSize){
  44.                                                 i=e-1;
  45.                                                 table.push(group,g[i]);
  46.                                                 break;
  47.                                         }
  48.                                         else {
  49.                                                 table.push(group,g[e-1]);
  50.                                         }
  51.                                         if(e==#g){
  52.                                                 isStop=true;
  53.                                         }
  54.                                 }
  55.                                 table.push(bigGroup,group)//把小分组装进大分组里面
  56.                                 if(isStop){
  57.                                         break ;
  58.                                 }
  59.                         }
  60.                         isStop=false
  61.                         //io.print("第"++y++"行",#bigGroup-1,"组")
  62.                         table.push(superGroup,bigGroup);
  63.                         if(#bigGroup<=1){
  64.                                 break ;//当小组数等于1说明图片已经分不开,不用继续再上移y轴扫描
  65.                         }
  66.                 }
  67.         }
  68.         isFounded=false;//判断是否已经找到
  69.         //找出分成splitNum组的大组
  70.         for(s=1;#superGroup;1){
  71.                 if(splitNum==#superGroup[s]-1){//刚好
  72.                         //找出大组的分割点坐标
  73.                         avers={};//保存各组平均值的数组
  74.                         for(b=1;#superGroup[s];1){
  75.                                 total=0;//每个分组的总值,用于计算每个分组的平均值
  76.                                 for(l=1;#superGroup[s][b];1){
  77.                                         total+=superGroup[s][b][l];
  78.                                 }
  79.                                 aver=math.round(total/#superGroup[s][b]);
  80.                                 table.push(avers,aver)
  81.                         }
  82.                         for(a=1;#avers;1){
  83.                                 io.print("分割点"++a++"为:"++avers[a])
  84.                         }
  85.                         isFounded=true
  86.                         break ;//找到一个分组后先,以后再区分这些分组
  87.                 }
  88.                 elseif(splitNum>#superGroup[s]-1){//分组太少
  89.                        
  90.                 }
  91.                 elseif(splitNum<#superGroup[s]-1){//分组太多
  92.                
  93.                 }
  94.         }
  95.         if(!isFounded){
  96.                 io.print("抱歉,实在是找不到合适的切割点了")
  97.                 continue ;
  98.         }
  99.         //把avers数组的最后一个数设置为bmp的宽
  100.         avers[#avers]=bmp.width-1;
  101.         newbmp=gdip.bitmap(avers[filename+1]-avers[filename]+1,bmp.height)
  102.         for(i=0;bmp.height-1;1){
  103.                 for(j=avers[filename];avers[filename+1];1){
  104.                         newbmp.setPixel(j-avers[filename],i,bmp.getPixel(j,i))
  105.                 }       
  106.         }
  107.         newbmp.save(""++filename++"-1.bmp")
  108. }
复制代码
分割后效果如下图:






统计了一下,成功分割率90%以上(未粘连),分割效果挺完美。当然,以上的算法还是有缺陷的,对找不到合适分割点的情况未作处理,并且并不一定通用。希望高手们将能够优化。

评分

参与人数 2银币 +90 收起 理由
aifuqiang + 30 真棒 希望能看到更多相关验证码或识别相关.
coder + 60 很给力!

查看全部评分

回复

使用道具 举报

8

主题

97

帖子

635

积分

三级会员

Rank: 4

积分
635
沙发
发表于 2013-5-31 14:08:19 | 只看该作者

很好,多谢分享

很好,多谢分享
回复

使用道具 举报

12

主题

177

帖子

1062

积分

四级会员

Rank: 6Rank: 6

积分
1062
QQ
板凳
发表于 2013-6-1 04:19:01 | 只看该作者

回复

使用道具 举报

0

主题

12

帖子

168

积分

一级会员

Rank: 2

积分
168
地板
发表于 2013-6-1 09:18:37 | 只看该作者

{:3_53:}有用的哦

有用的哦
回复

使用道具 举报

7

主题

302

帖子

1893

积分

四级会员

Rank: 6Rank: 6

积分
1893
QQ
5#
发表于 2013-6-1 16:39:25 | 只看该作者

感谢分享!

感谢分享!
回复

使用道具 举报

1

主题

38

帖子

281

积分

二级会员

Rank: 3Rank: 3

积分
281
6#
发表于 2013-6-13 22:06:19 | 只看该作者

把动态的静态化,把组合的分割化!做程序的好思路

把动态的静态化,把组合的分割化!做程序的好思路
回复

使用道具 举报

0

主题

8

帖子

57

积分

一级会员

Rank: 2

积分
57
7#
发表于 2018-4-10 18:45:30 | 只看该作者
很有用,感谢分享源码                                       
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

手机版|未经许可严禁引用或转载本站文章|站长邮箱|aardio.com|aardio官方社区 ( 皖ICP备09012014号 )

GMT+8, 2018-4-22 10:53 , Processed in 0.062503 second(s), 24 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表