本推文主要识别的验证码是这种:
![1470684-20180828143522407-303431333.png](https://i-blog.csdnimg.cn/blog_migrate/b8893cc7b1d9f9c309b8cc05f3419e2c.png)
![1470684-20180828143522703-2001089869.png](https://i-blog.csdnimg.cn/blog_migrate/124647d303eecccd96690f842ad0c8b8.png)
![1470684-20180828143523143-1221740234.png](https://i-blog.csdnimg.cn/blog_migrate/ff86982c097a0db62228afbae1ef20c8.png)
第一步: 二值化
所谓二值化就是把不需要的信息通通去除,比如背景,干扰线,干扰像素等等,只剩下需要识别的文字,让图片变成2进制点阵。
![1470684-20180828143523426-816729256.png](https://i-blog.csdnimg.cn/blog_migrate/96886a92f7ee0b9d80defa8742e1e37d.png)
![1470684-20180828143523711-2122411520.png](https://i-blog.csdnimg.cn/blog_migrate/71720a294e90b154177ae988a43cafa5.png)
![1470684-20180828143523954-1514888213.png](https://i-blog.csdnimg.cn/blog_migrate/41ec0481c3841da9879df58548e9cfde.png)
第二步: 文字分割
为了能识别出字符,需要对要识别的文字图图片进行分割,把每个字符作为单独的一个图片看待。
![1470684-20180828143524236-2056586278.png](https://i-blog.csdnimg.cn/blog_migrate/93597f056bb0dea341b58f4e7d2eda07.png)
![1470684-20180828143524446-350602146.png](https://i-blog.csdnimg.cn/blog_migrate/53950164cb7d5b370106d75c52b12faf.png)
![1470684-20180828143524686-1197823720.png](https://i-blog.csdnimg.cn/blog_migrate/22445269ef73864d661a39f9e596c621.png)
![1470684-20180828143524909-1816514472.png](https://i-blog.csdnimg.cn/blog_migrate/b26cb0a33ae5d714fc9dd8db6a819ab9.png)
![1470684-20180828143525130-168638340.png](https://i-blog.csdnimg.cn/blog_migrate/7b9a84847e69e2764582908a17009090.png)
![1470684-20180828143525355-1024259094.png](https://i-blog.csdnimg.cn/blog_migrate/a8b395b8e0bfbbab173118489dbb5f9c.png)
![1470684-20180828143525560-1617479366.png](https://i-blog.csdnimg.cn/blog_migrate/24bf073c57b10edf63f9fb643c185a80.png)
![1470684-20180828143525740-269142558.png](https://i-blog.csdnimg.cn/blog_migrate/b5517cc3ea70086e108cd8d86d27b883.png)
![1470684-20180828143525982-2050552431.png](https://i-blog.csdnimg.cn/blog_migrate/95ce60471527011d00c069fa28454820.png)
![1470684-20180828143526178-497842496.png](https://i-blog.csdnimg.cn/blog_migrate/0305fb762847667025f50eba8b25236b.png)
![1470684-20180828143526369-1839088430.png](https://i-blog.csdnimg.cn/blog_migrate/e4cbb388ba4f6a9207aaa356d7df3827.png)
第三步: 标准化
对于部分特殊的验证码,需要对分割后的图片进行标准化处理,也就是说尽量把每个相同的字符都变成一样的格式,减少随机的程度。最简单的比如旋转还原,复杂点的比如扭曲还原等等。比如本文中分割后的数字1和8宽度不一致,把他们的宽度填充一致,就是标准化的一种。
可以看到上面切割后的字符1最右边一列像素都为0。
第四步: 学习 & 识别
这一步可以用很多种方法,最简单的就是模板对比,对每个出现过的字符进行处理后把点阵变成字符串,标明是什么字符后,通过字符串对比来判断相似度。
在文章的后半部分会详细解释我采用的算法。
训练集学习tran.m
width = 132; height = 20;
%共10张验证码 x 11个数字 共分割出 110张字符图片
%每个字符图片 高度20 x 宽度9 共 180个像素
data = zeros(110, 180);
chars = zeros(180, 10); %用于存储10个数字字符的特征值 每个字符大小为20x9
for name = 0:9
im = imread(sprintf('%d.jpg', name)); %读取图片
im = im2bw(im) == 0; %第一步:二值化 黑色1 白色0
%第二步: 分割
black = sum(im) ~= 0; %20x132矩阵 从上向下求和为 1x132 不等于0 则横坐标对应的一列有字符像素
white = sum(im) == 0; %20x132矩阵 从上向下求和为 1x132 等于0 则横坐标对应的一列没有字符像素
lower = find(min([black 0],[1 white])); %获取11个字符的开始下标
upper = find(min([0 black],[white 1])) - 1; %获取11个字符的结束下标
for i=1:11
ch = im(:,lower(i):upper(i)); %截取单个字符
ch(20, 9) = 0; %第三步: 字符二值化矩阵大小标准化为20x9
data(name*11 + i ,:) = ch(:); %字符图片数据存入data
end
end
%第四步: 学习 & 识别
class = clusterdata(data, 10); %将110个字符图片分为10类
%各个分类号对应的实际数字(人工识别后写进去的- -)
num = [5 3 6 8 9 0 7 2 1 4];
for i = 1:10
%各类中的字符图片取均值
im = mean(data(class == i, :)) > 0.5;
chars(:, num(i) + 1) = im; %存储
end
验证码识别ocr.m
function ret = ocr(filename)
load;
ret = zeros(1, 11);
im = imread(filename);
im = im2bw(im) == 0; %第一步: 二值化
%第二步: 分割
black = sum(im) ~= 0;
white = sum(im) == 0;
lower = find(min([black 0],[1 white]));
upper = find(min([0 black],[white 1])) - 1;
for i=1:11
ch = im(:,lower(i):upper(i));
ch = ch(:);
ch(180) = 0; %第三步标准化
%第四步: 识别
[~, num] = max(sum(min(repmat(ch, 1, 10), chars)));
ret(i) = num-1;
end
end