找回密码
 注册
查看: 3967|回复: 1

简单验证码识别问题-转

[复制链接]
发表于 2011-7-15 10:53:57 | 显示全部楼层 |阅读模式
偶在网上发现有人用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
回复

使用道具 举报

发表于 2011-11-15 18:52:52 | 显示全部楼层
回复 支持 反对

使用道具 举报

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

本版积分规则

手机版|小黑屋|生物统计家园 网站价格

GMT+8, 2024-11-24 12:13 , Processed in 0.026279 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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