|
偶在网上发现有人用R语言研究验证码识别问题
特此转来分享
R作为最强(没有之一)的统计计算环境, 有着很好的数字图像处理支持, 很适合用来做验证码识别这类工作. 我们可以通过一个最简单的例子来实践一下.
一般来说, 要让机器"认出"验证码, 大致需要讨论3个问题:
1.提取字模: 将验证码中出现的单个字符的数字信息提取出来, 作为比较的依据, 可以认为每个字符对应一个数字矩阵;
2.二值化: 根据一定的准则, 将数字矩阵中的字符本身部分用1表示, 背景、干扰点用0表示(或反之);
3.抓取验证码, 二值化, 根据一定准则, 与字模比对, 取字模中与之最相似的字符作为识别结果.
首先, 我们需要一个提供验证码服务的站点. 几经选择, 决定使用国内某CMS练手. 在其"成功案例"中随机选个站:
http://gongxue.cn/Inc/Checkcode.asp
每刷新一次这个页面, 将返回一个新的验证码, 形式为6位的数字、小写字母混合.
为了方便, 建立一个文件夹用以保存字模和测试文件, 设置为工作目录:
dir.create("C:/checkcode/")
setwd("C:/checkcode/")
根据观察, 本例中需要提取35个字符(10个数字+25个小写字母, 不知为什么, 唯独缺了"z")的像素信息. 又观察得验证码使用的图片格式为bmp, 将其裁剪为单独的字符, 存为35个单独的bmp文件(见附件), 使用GraphicsMagick转换为R可读的pnm格式:
system("gm mogrify -format pnm C:/checkcode/*.bmp")
使用pixmap包中的read.pnm()函数读入图片, 结果存入列表pnm:
require(pixmap)
pnm = list()
length(pnm) = 35
for (i in 0:34) {
pnm[[i+1]] = read.pnm(paste(i, ".pnm", sep = ""))
}
(为了看得清楚, 全部使用显式循环)
使用pixmap包中的getChannels()函数提取RGB三个通道的数值矩阵, 结果存入列表channels:
channels = list()
length(channels) = 35
for (j in 1:35) {
channels[[j]] = getChannels(pnm[[j]])
}
通过简单计算, 将RGB转换为灰度, 结果存入列表graymat:
graymat = list()
length(graymat) = 35
for (k in 1:35) {
graymat[[k]] = 0.3 * channels[[k]][, , "red"] +
0.59 * channels[[k]][, , "green"] +
0.11 * channels[[k]][, , "blue"][1, 1]
}
使用biclust包中的binarize()函数对矩阵进行二值化, 存入列表binmat:
require(biclust)
binmat = list()
length(binmat) = 35
for (l in 1:35) {
binmat[[l]] = binarize(graymat[[l]][1:15, 1:10])
}
这里的情况是最简单的, 因为图像上没有任何干扰, 故二值化的效果极好. 大家可以输入binmat[[1]]观察字符"0"的二值化结果.
至此, 已经成功建立字模库. 下面就可以正式开始识别了:
## 建立下标与字符的对应关系
charTable = c(0:9, "a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y")
## 刷验证码
download.file("http://gongxue.cn/Inc/Checkcode.asp",
"test.bmp", quiet = TRUE)
## bmp转pnm
system("gm mogrify -format pnm C:/checkcode/test.bmp")
## 二值化计算过程 同上
testmat = getChannels(read.pnm("test.pnm"))
testmatgray = 0.3 * testmat[, , "red"] +
0.59 * testmat[, , "green"] +
0.11 * testmat[, , "blue"][1, 1]
char = list()
length(char) = 6
for (i in 1:6) {
char[[i]] = binarize(testmatgray[1:15, (i*10-9)i*10)])
}
## 将每个字符的比对结果存入result 比较过程仍可优化
result = rep(NA, 6)
for (i in 1:6) {
for (j in 1:35) {
if (isTRUE(all.equal(char[[i]], binmat[[j]])) == TRUE)
result[i] = charTable[j]
}
}
输出识别结果:
print(paste(result, collapse = ""))
## [1] "v54c0s"
本次不务正业就此结束. 这个例子的简单之处在于验证码中没有干扰, 字符也没有做变形处理, 属于非常传统非常标准的验证码. 那些更加bt的验证码, 要保持比较高的识别率, 就对二值化方法和比较方法上提出了更高的要求, 统计方法可以在这里施展身手. 相对那些单纯增加图像的复杂性让人肉都很难辨认的验证码, 我更欣赏的是被Google收购的reCAPTCHA, 属于那种很难得的idea.
转自http://cos.name/cn/topic/103528?bb_attachments=214077&bbat=867 |
|