前面说要写关于图像的文章,虽然很久没写,但我决不食言的;另外网页那部分文章有一个最大的缺憾——验证码识别,虽然经过再三考虑,还是决定从网络安全的角度出发,说一下简单的识别技术和反识别技术,当然了,计算机视觉这个东西有点理论化,不适合我的风格,咱们还是拿代码说话。
同时说起游戏外挂和验证码可能有点奇怪,实际上一点都不奇怪,图像识别技术是两者都可应用的,在这一份教程当中,使用够级游戏为示例,当然,这主要是因为不会影响到别人赚钱和拼技术,图像识别版的有一些弊端,但是有些大虾从封包入手就做的很好,虽然以前也做了对应分析,但是终由VB拦截封包速度太慢而放弃……
闲话少说,就此开始:如何取图像,如何取句柄,如何检测是谁出牌等的实现这里不提,如果还真有写的,建议用SPY++观察一下眼睛的消息。在适当的代码下,我们可以得到如下图片(牌的纵坐标是不变的,对吧?) 接下来的思路是这样的:
1、去掉左边的背景,去掉右面的背景及牌面,只留下中间的字符部分
2、字符分割
3、模式识别——这个地方我们使用BP(反向传播)人工神经网络来实现
4、输出、记录,重绘界面
说一下关于2、3、4这几步我们为了得到还有什么牌没有出更是为了完成前面的文章,所以识别字符,实际上一个简单的外挂可以不识别字符,而直接复制图像到输出,如果需要知道每个人都还有多少牌,也只需加入第二步,所以说啊——写一个外挂是非常简单的事情,简单到你都不敢想!
接下来再次分析一下我们的思路,从分割字符开始一直到ANN识别这个部分,我们能否简单的实现呢?这里简单叙述一下ANN的代码实现过程,理论咱不提,主要说说理论到实际应用过程的困难。ANN识别需要一个网络设计过程,BP算法的设计一般可以简化为:
确定输入数组(特征提取,很多用感知器的,我们也用,但简单的多)
输入层处理(实现激励函数和误差计算权值更新即可,无其他内容)
确定中间层(根据前人经验,1层足矣……,个数嘛,嘿嘿,10个8个就很够用)
确定输出个数(一般来说,一个输出对应一个神经元,我们这里几个呢?待会说,不过绝对不是13、14、15)
我们要输入的特征是什么?很多用字符拐角啊,笔顺啊,四向啊,随机感知啊等等等等,多到你看哪个都觉得不错,实现起来都不是一般的麻烦!我们这里结合字符分割时得到的数组来作为特征。
基于我们的图片非常简单:向下投影就可分离它们,或者……按照牌露出部分大小(这个我们不采用,因为要提取特征)
分析呢,先到这里给出一段代码:
Dim maxX, minX As Integer
Dim bmp As Bitmap = PictureBox1.Image
Dim gr As Graphics = PictureBox1.CreateGraphics
'绘制一条参考线
gr.DrawLine(New Pen(Color.Black), 0, CInt(bmp.Height / 3), bmp.Width, CInt(bmp.Height / 3))
For x As Integer = bmp.Width - 1 To 0 Step -1 '检测最右面的牌
Dim clr As Color = bmp.GetPixel(x, bmp.Height / 3)
If clr.R < 20 AndAlso clr.G < 20 AndAlso clr.B < 20 Then
maxX = x - 55 : Exit For
End If
Next
For x As Integer = 0 To bmp.Width - 1 '检测最左面的牌
Dim clr As Color = bmp.GetPixel(x, bmp.Height / 3)
If clr.R > 200 AndAlso clr.G > 200 AndAlso clr.B > 200 Then
minX = x : Exit For
End If
Next
检测最右面的牌那里,-55是减去了牌面多出来的部分;利用RGB分量来判断时,我们不区分红黑,而是把红黑作为一类,把背景色和白作为一类。
至此,我们得到了第一张牌和最后一张牌的位置,接下来,我们进行向下投影,这个投影呢,有一个效率问题,想效率高,我们就隔行扫描,可我们又想提取特征用于识别,隔的太多了,特征就隔掉了……暂时取1吧
Dim sum(maxX - minX) As Integer
Dim outgr As Graphics = PictureBox2.CreateGraphics
For y As Integer = 1 To bmp.Height - 1 Step 2 '采用STEP值控制隔行检测
For x As Integer = minX To maxX
Dim clr As Color = bmp.GetPixel(x, y)
If clr.B < 200 Then sum(x - minX) += 1
Next
Next
循环结束后,Sum中保存的就是投影结果,我们吧它输出出来观察一下:
For x As Integer = minX To maxX '显示投影结果
outgr.DrawLine(New Pen(Color.Black), x, 40, x, 40 - sum(x - minX))
Next
'显示检测线——横向检测范围
outgr.DrawLine(New Pen(Color.Black), minX, 40, minX, 0)
outgr.DrawLine(New Pen(Color.Black), maxX, 40, maxX, 0)
程序运行的结果就是这样的: 对应各个牌下方,输出了垂直投影的结果,每个字符都不一样啊…………看清楚了,呵呵,那么我们把sum分割成对应个数个数组保存起来吧,序列化一下,形成一些“标本”恩恩,学习样品,训练样品怎么叫都好了。
关于我们没有区分红黑的做法是不是无法区分大小王,增加区分代码是不是会降低效率的问题,我想不用深入讨论了,当取得图片以后,直接识别,若发现是王,则只需要对此时的BMP对象调用一次Getpixel。下一篇,简单的写一写神经网络,当然了,既然是用.NET编程序,就是面向对象编程。
哦,对对对,还没说为什么说不是13、14、15个输出的问题呢,因为我们要同时识别牌是黑、红、方、草中的哪一种,所以Sun应该用一个简单的结构或者类来代替——添加一个黑红方草的属性。这个属性的识别和我们上面的字符识别是完全一样一样一样…………一样的,所以我们一起传输两幅图片进来,一幅是上面那个,另一个就是对应的花式了,ANN将一共管理14+5个类别,19个。这样写也许更好理解管理14+4+1个类别。应该就可以满足我们的需要了!
把完整代码贴到这里:(自行绘制2个Picturebox,一个Button,名称均默认)
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'检测牌面区域
Dim maxX, minX As Integer
Dim bmp As Bitmap = PictureBox1.Image
Dim gr As Graphics = PictureBox1.CreateGraphics '绘制一条参考线
gr.DrawLine(New Pen(Color.Black), 0, CInt(bmp.Height / 3), bmp.Width, CInt(bmp.Height / 3))
For x As Integer = bmp.Width - 1 To 0 Step -1 '检测最右面的牌
Dim clr As Color = bmp.GetPixel(x, bmp.Height / 3)
If clr.R < 20 AndAlso clr.G < 20 AndAlso clr.B < 20 Then
maxX = x - 55 : Exit For
End If
Next
For x As Integer = 0 To bmp.Width - 1 '检测最左面的牌
Dim clr As Color = bmp.GetPixel(x, bmp.Height / 3)
If clr.R > 200 AndAlso clr.G > 200 AndAlso clr.B > 200 Then
minX = x : Exit For
End If
Next
Dim sum(maxX - minX) As Integer
Dim outgr As Graphics = PictureBox2.CreateGraphics
For y As Integer = 1 To bmp.Height - 1 Step 2
'采用STEP值控制隔行检测
For x As Integer = minX To maxX
Dim clr As Color = bmp.GetPixel(x, y)
If clr.B < 200 Then sum(x - minX) += 1
Next
Next
For x As Integer = minX To maxX '显示投影结果
outgr.DrawLine(New Pen(Color.Black), x, 40, x, 40 - sum(x - minX))
Next
'显示检测线——横向检测范围
outgr.DrawLine(New Pen(Color.Black), minX, 40, minX, 0)
outgr.DrawLine(New Pen(Color.Black), maxX, 40, maxX, 0)
'此时sum已经是检测范围内的有效数据,接下来的操作方法很多,这里直接用作BP网络训练
End Sub
End Class
最后再说一下,该死的编辑器又把代码里的换行弄没了,手动换行。。。。看起来不容易,可读性下降很多,不规范了,复制到代码编辑器里自己就好了,要是没好,在结尾处按一下回车,要不就全选,然后按一下TAB。