首页
社区
课程
招聘
[原创]浅谈完美、诛仙等游戏验证码的识别
发表于: 2009-4-13 22:49 14107

[原创]浅谈完美、诛仙等游戏验证码的识别

2009-4-13 22:49
14107

浅谈完美、诛仙等游戏验证码的识别
write by http://hi.baidu.com/weolar/blog/item/dd173a6d1cdbd9f342169446.html
最近老有人问我那个程序的识别原理是什么,虽然代码给出去了,不过我想这东西估计大家看着也有点痛苦,毕竟是自己想的代码,别人读起来比较费劲,所以我就根据程序算法的一些有意思的知识点简单讲讲吧~~希望不要被鄙视。
一,程序识别的预处理
我先把程序的一些预处理讲一下,在函数
CMyGraph::BmpToMatrix(LPCWCH lpDIBFilePath,PBmpMatrixInfo pOutBmpMatrixInfo,int Mode) 中,实现了将BMP图片的像素点阵提取出来,转换为二维数组形式。
typedef struct _BmpMatrixInfo{

  int lWidth;
  int lHight;
  int lNum;
  BYTE *Matrix;
}BmpMatrixInfo, *PBmpMatrixInfo;

这个结构体负责存储描述这个二维数组点阵的信息。其中BYTE *Matrix;
存放了二维数组,这样我们用
BOOL MyMatrixMath::SetByte(PBmpMatrixInfo BmpMatrix, int x, int y, BYTE Value)
{

  if (BmpMatrix->lHight < 0 || BmpMatrix->lWidth < 0)
  {
    return 0;
  }

  if (BmpMatrix->lHight < y || BmpMatrix->lWidth < x)
  {
    return 0;
  }
  *(PBYTE)(BmpMatrix->Matrix + BmpMatrix->lWidth *y + x) = Value;
  return TRUE;
}

BYTE MyMatrixMath::GetByte(PBmpMatrixInfo BmpMatrix, int x, int y)
{

  if (0 > y || 0 > x)
  {
    return 0;
  }

  if (BmpMatrix->lHight <= y || BmpMatrix->lWidth <= x)
  {
    return 0;
  }
  return *(PBYTE)(BmpMatrix->Matrix + BmpMatrix->lWidth *y + x); 
  //     *(PBYTE)(*(PBYTE)(BmpMatrix->Matrix+x)+y );
}

函数便能访问和设置其中任意一个坐标的元素。
在预处理中,我们先用
  
CMyGraph::ErosionDIB( (LPSTR)MatrixBmp.Matrix,  MatrixBmp.lWidth,  MatrixBmp.lHight,1, structure);
将整个图片进行膨胀处理,然后将连在一起的点全部取出来一次,再和原来的图像进行过滤,这样便得到了提取好了的单个字。
不过对于这种验证码,还有个非常不爽的地方,就是有时候两个字会连在一起。我的处理方案是判断一个连在一起的部分的长宽是否超出比例,而且四个角是否有一个有大量空白。
而对付干扰线,目前我的策略不太好,仅仅是根据线的长宽比例判定。这样有的汉字笔画也会被K掉。我下一个想法是根据干扰线的特征进行过滤。
二,汉字的相似度计算
    其实这个的原理非常简单(所以大家看了不要鄙视……)。大家看我一副图就知道了:


将标准字和待识别字重叠在一块大家就看出来了,两个字能重合在一起的点数非常的多!
我的想法就是将抽取出来的每个字和一个标准字进行对比,循环将待识别字的每个点与标准字每个点相应位置进行对比,如果相应坐标位置附件某个距离有标准字的点的话就记录下来,然后将所有记录的点数除以总点数便是相似度。

当然这样有个前提就是两副图的位置要摆好。我的解决方案是对齐宽度后将高度一点点的放大,再取相似度最大值,这样便很好的解决的位置问题。

当然,我们还可以加上更进一步的过滤工作。比如对于那种特别不符合的点也记录下来,让整个图的相似度降低。不过我发现这样对于识别貌似效果不大好,所以就去掉了。
其实还有一个可以利用的地方,大家仔细看图,可以发现虽然很多线都扭曲了,不过仍然有少部分的垂直和水平的线条。这个也能用来作为定位的标志。

点的相应最短距离计算

   在算一个坐标附近多远有一个点的时候如果用暴力遍历每个坐标的话会很耗cpu的。所以我想了个有意思的方式:使用一个函数
int MyMatrixMath::CreateDirArr(int i)
{
  float n = (float)((int)((SquareRootFloat((float)(1+4 * i)) - 1) / 2) + 1);
  n = n - (int)n > 0.01 ? n + 1: (int)n;
  n = n - 1;
  int k = (int)(i - (n *n + n) <= n + 1 ? (2 *n + 1): 2 *(n + 1));

  return k % 4;
}
产生一个数组。这个数组很有意思,产生的数为0--3的整数。如果把0-3对应上右下左的话,那么如果我们每一步都根据这个方向前进的话就会这样前进:


这样便能很快取到附近的点。(当然,这样会有一点小小的误差,不过这种误差可以忽略不记。)
平方根的计算

上面的产生数列操作中用到了开平方运算。这个如果我从网上找到了个很强大的计算函数:
/*
================
SquareRootFloat Quake III:code/common/cm_trace.c
================
*/
float MyMatrixMath::SquareRootFloat(float number)
{
  long i;
  float x, y;
  const float f = 1.5F;

  x = number * 0.5F;
  y = number;
  i = *(long*) &y;
  i = 0x5f3759df - (i >> 1);
  y = *(float*) &i;
  y = y *(f - (x *y * y));
  y = y *(f - (x *y * y));
  return number *y;
}

这个是 Quake III的代码里面的。很神奇的代码!下面一段话来自网上,相信大家看了能学到不少有意思的东西:
有人在Quake III的源代码里面发现这么一段用来求平方根的倒数的代码,经过测试  
 
它比直接算1/sqrt(n)快4倍,而误差最大为0.1%:  

算法的原理其实不复杂,就是牛顿迭代法,用x-f(x)/f'(x)来不断的逼近f(x)=a的根。 
 
简单来说比如求平方根,f(x)=x^2=a ,f'(x)= 2*x,f(x)/f'(x)=x/2,把f(x)代入 
 
x-f(x)/f'(x)后有(x+a/x)/2,现在我们选a=5,选一个猜测值比如2, 
那么我们可以这么算  
5/2 = 2.5; (2.5+2)/2 = 2.25; 5/2.25 = xxx; (2.25+xxx)/2 = xxxx ...  
这样反复迭代下去,结果必定收敛于sqrt(5),没错,一般的求平方根都是这么算的  
但是卡马克(quake3作者)真正牛B的地方是他选择了一个神秘的常数0x5f3759df 来计 
 
算那个猜测值 
就是我们加注释的那一行,那一行算出的值非常接近1/sqrt(n),这样我们只需要2次牛 
 
顿迭代就可以达到我们所需要的精度. 
好吧 如果这个还不算NB,接着看: 
普渡大学的数学家Chris Lomont看了以后觉得有趣,决定要研究一下卡马克弄出来的  
这个猜测值有什么奥秘。Lomont也是个牛人,在精心研究之后从理论上也推导出一个  
最佳猜测值,和卡马克的数字非常接近, 0x5f37642f。卡马克真牛,他是外星人吗?  
 
传奇并没有在这里结束。Lomont计算出结果以后非常满意,于是拿自己计算出的起始  
值和卡马克的神秘数字做比赛,看看谁的数字能够更快更精确的求得平方根。结果是  
卡马克赢了... 谁也不知道卡马克是怎么找到这个数字的。  
 
最后Lomont怒了,采用暴力方法一个数字一个数字试过来,终于找到一个比卡马克数  
字要好上那么一丁点的数字,虽然实际上这两个数字所产生的结果非常近似,这个暴  
力得出的数字是0x5f375a86。  
 
Lomont为此写下一篇论文,"Fast Inverse Square Root"。  

整个讲解完毕。虽然真正算法很简单……不过当时我也是试验了N次才验证出来的。开始我用了不少方法,甚至还进行了一部分的笔画识别。不过都没这个方法的速度快,效率高。
希望里面的一些东西对各位有用~~


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (22)
雪    币: 225
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ywb
2
精彩,我只看汉字,不看字母
2009-4-13 23:11
0
雪    币: 364
活跃值: (152)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
3
呵呵,管理员好快~
2009-4-13 23:19
0
雪    币: 210
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
先标记下~~
2009-4-13 23:27
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
只好收藏了现在没有能力搞到
2009-4-13 23:36
0
雪    币: 193
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
浮点指令现在很快的,用fsqrt吧。
2009-4-14 01:21
0
雪    币: 376
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
完美刚出文字图片的时候字还是分开的,现在大都是连体字,人眼都快识别不了了。。。
2009-4-14 08:17
0
雪    币: 364
活跃值: (152)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
8
写这个旨在和大家分享一些有意思的算法
2009-4-14 09:06
0
雪    币: 251
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
不错~~~

楼主的算法是拿图片上取的文本字,先进行放大处理然后和标准字进行比较
如果图片上是手写体或行书\草书,不知有没有好办法解决呢?
2009-4-15 14:10
0
雪    币: 138
活跃值: (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10
weolar ,用你的方法,下面的图片中的汉字能解不?


如果可以解,我去实现下。。。多谢帮助。
上传的附件:
  • a.png (1.14kb,805次下载)
2009-4-16 09:07
0
雪    币: 364
活跃值: (152)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
11
[QUOTE=rgbsky;607404]weolar ,用你的方法,下面的图片中的汉字能解不?


如果可以解,我去实现下。。。多谢帮助。[/QUOTE]

不行~~我的只是针对那种图试验出来的偷懒的猥琐方式。
2009-4-16 09:12
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
手写功能是不是用了类似的技术?
2009-4-16 14:24
0
雪    币: 254
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
真的很精彩 学习了···
2009-4-16 16:56
0
雪    币: 8102
活跃值: (1945)
能力值: ( LV8,RANK:122 )
在线值:
发帖
回帖
粉丝
14
有点不太明白呢
2009-4-16 19:49
0
雪    币: 213
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
有没有快速判断白色和黑色所占比例的方法?
2009-4-17 22:06
0
雪    币: 217
活跃值: (99)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
16
计算机识别能力还是和人工智能相差甚远的.
防止被识别的办法有 混合使用字体, 文字倾斜扭曲(google常用), 不规则区域反色处理. 这些对人工来说几乎无影响,但程序识别就复杂得多了.

商用的手写识别效率更高,当然算法也更复杂专业.
2009-4-17 22:18
0
雪    币: 364
活跃值: (152)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
17
嗯嗯~ dwing大侠所言甚是~~

其实我这个只是个小技巧
不过也是试验出来,发现就这个很管用
2009-4-17 22:39
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
GOOGLE开源的OCR核心,大家看过吗?目前到了2.03版。
非常牛。
虽然在说明中,提到对中文的识别效果不好,速度很慢。但那是针对全部中文而言,如果只是一个小了字符集(比如10个数字的大写),我相信识别效果会不错的。
http://code.google.com/p/tesseract-ocr/
2009-4-28 12:38
0
雪    币: 364
活跃值: (152)
能力值: ( LV12,RANK:450 )
在线值:
发帖
回帖
粉丝
19
[QUOTE=Cadenza;613175]GOOGLE开源的OCR核心,大家看过吗?目前到了2.03版。
非常牛。
虽然在说明中,提到对中文的识别效果不好,速度很慢。但那是针对全部中文而言,如果只是一个小了字符集(比如10个数字的大写),我相信识别效果会不错的。
http://code.google.com/p/tesseract-ocr/...[/QUOTE]

感谢分享~~
2009-4-28 19:21
0
雪    币: 100
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
思想比较不错,我一直以为,图像识别处理技术是解决此类问题的高效手段
2009-4-30 17:07
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
现在是连体字,不知道行不行
2009-4-30 17:10
0
雪    币: 101
活跃值: (12)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
22
qiang o
2009-5-1 14:48
0
雪    币: 198
活跃值: (13)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
23
楼主太牛B了,我看了一下,没有做旋转什么之类的处理,应该还不能解决旋转了的汉字,
2009-5-4 23:05
0
游客
登录 | 注册 方可回帖
返回
//