话说上篇说到是预处理图片不给力引起的问题,本篇调整方向探索。
预处理图片:
- 之前处理的图片是二值化了,就算是把切割偏移排除在外,切割出来的数字点阵不一。 之前的方法:im.convert(‘1’)太简单暴力了 改进后的方法:
def two_convert(im, threshold): #给定阈值,返回二值图像
Lim = im.convert('L')
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(255)
#table.append(1)
# convert to binary image by the table
bim = Lim.point(table, '1')
return bim
im = two_convert(im,180)
使用新方法之后,该清晰的加强清晰了,该淡化的也淡化了,it works!
- 切割单组图片 之前判断有多少组数字的函数依然可用,现在主要是改变切割每组数字和切割单个数字的方法,最终的目的是每个单个数字都不留白边。
切割每组数字的方法,参考下面代码片段和注释:
def splitdigits(im,num):
char_width = 55 + 2#每个字符宽5,最多有5个字符,再给2个像素的冗余
white = 255
black = 0
w, h = im.size
data = list(im.getdata())
emptywidth = (w - numchar_width) / num - 10#减掉10,留点冗余
x,y = 0,0 loop = 0 whitenum = 0
#从左向右枚举 while x < w: y = 0 while y < h: #当遇到第一个黑点,就切割,同时修改x if data[y*w +x] == black: x2 = x+char_width if x2 > w: x2 = w#避免越界 box = (x,0,x2,h)#确定切割范围 newim = im.crop(box)#切割单组数字 getsingle(newim)#切割识别单组数字 loop += 1 if loop >= num: return x += char_width + emptywidth#加大步长,加快收缩范围枚举 break else: y += 1 x += 1
- 切割单个数字 顶部坐标和高度是固定的、切割目标:左右边界都没有白边,如果有白边则认为是另外字符。
切割每个单独数字的方法,参考下面代码片段和注释:
def getsingle(im):
w, h = im.size
data = list(im.getdata())
rec = []#用来记录数组的从左向右的断连情况
lt = 0#每个字符的左边界
numstr = ’’
for x in xrange(w):
s=0
for y in xrange(h):
if data[y*w +x] == 0:
s += 1
rec.append(s)
if s == 0 or x == w-1:#如果遇到一个空白 if rec[x-1] > 0:#如果前一列不是空白,那就可以切割了 box = (lt,0,x,h)#切割单个字符 newim = im.crop(box) numstr = ‘%s%s’ % (numstr,ocrsingle(newim,rec[lt:x]))#识别单个字符 elif x > 0 and rec[x-1] == 0:#如果遇到的列是有黑点的,并且前一列是空白,那这个就是lt lt = x #重新计算切割的左边界 print numstr
- 字模提取 在切割单个字符中,有一个办法(这句话是帅帅看喜羊羊后的口头禅之一)。 思路:在上面代码的rec中,记录了每个字符的的每一列包含的黑点个数,这些数据基本就可以用来做匹配了。
在处理了几张图后,采集到的数据如下: dot = [1] num0 = [6, 2, 2, 3, 2] num0a = [6, 2, 2, 4, 1] num0b = [6, 2, 2, 4, 2] num1 = [1, 1, 8] num1a = [1, 8] num2 = [2, 3, 3, 4] num2a = [2, 3, 3, 4, 1] num3 = [2, 3, 4, 5] num3a = [1, 3, 4, 6] num4 = [1, 3, 2, 3, 8, 1] num4a = [1, 3, 2, 2, 8, 1] num4b = [1, 2, 2, 2, 8, 1] num5 = [5, 3, 3, 4] num6 = [6, 3, 3, 4, 1] num6a = [6, 3, 3, 4] num6b = [6, 2, 3, 5, 1] num7 = [1, 3, 4, 3] num7a = [1, 3, 3, 4, 1] num7b = [1, 3, 3, 3] num8 = [6, 3, 3, 4, 1] num8a = [6, 4, 4, 6] num8b = [5, 3, 3, 6] num9 = [4, 3, 3, 5, 2] num9a = [5, 3, 3, 6, 1] num9b = [5, 3, 3, 6]
发现8和9有重合的,于是又采集了字符从上到下每一行黑点的个数列表。 8和9的区别在于倒数第二行和倒数第三行的黑点个数,9是1个,8是2个。 dot_h = [0, 0, 0, 0, 0, 0, 0, 1] num0_h = [2, 2, 1, 2, 2, 1, 2, 3] #num0_hb = [3, 2, 1, 2, 2, 1, 2, 3] num1_h = [1, 3, 1, 1, 1, 1, 1, 1] num2_h = [2, 1, 1, 1, 1, 1, 1, 4] #num2_ha = [2, 1, 1, 1, 1, 1, 1, 5] num3_h = [3, 1, 1, 2, 2, 1, 1, 3] #num3_ha = [2, 1, 1, 2, 2, 1, 1, 4] num4_h = [2, 2, 2, 2, 2, 6, 1, 1] #num4_ha = [1, 2, 2, 1, 2, 6, 1, 1] num5_h = [4, 1, 1, 3, 1, 1, 1, 3] num6_h = [3, 2, 1, 2, 2, 2, 2, 3] #num6_hb = [2, 2, 1, 3, 2, 2, 2, 3] num7_h = [4, 1, 1, 1, 1, 1, 1, 1] #num7_ha = [4, 1, 1, 0, 1, 1, 1, 1] num8_h = [2, 2, 2, 2, 4, 2, 2, 4] num9_h = [2, 2, 2, 3, 4, 1, 1, 3] #num9_ha = [2, 2, 2, 2, 4, 1, 1, 3]
- 识别函数
[code]def ocrsingle2(im,rec = []): if rec == dot: return '.' if rec == num0 or rec == num0a or rec == num0b: return '0' if rec == num1 or rec == num1a: return '1' if rec == num2 or rec == num2a: return '2' if rec == num3 or rec == num3a: return '3' if rec == num4 or rec == num4a or rec == num4b: return '4' if rec == num5: return '5' if rec == num6 or rec == num6a or rec == num6b: return '6' if rec == num7 or rec == num7a or rec == num7b: return '7' #8和9的变形太多,纵向比较不好区分,通过扫描每一行的黑点特征比较 rech = []#用来记录数组的从上向下的断连情况 w, h = im.size data = list(im.getdata()) for y in xrange(h): s=0 for x in xrange(w): if data[y*w +x] == 0: s += 1 rech.append(s) print rec print rech if rech[5] == 2 and rech[6] == 2: return '8' if rech[5] == 1 and rech[6] == 1: return '9' return '?'[/code]
经过试验,识别率应该在99%以上了,至少我下载的10涨图片,每张图片大平均应该有10组数字以上,都可以全部正确识别出来了。
最后引用总结一下图片识别的大致方法,以下文字来自:http://www.cnblogs.com/cntlis/archive/2009/01/14/1375694.htm l 1.图像二值化 二值化实现方法有 1.1图像灰度化-中值滤波等,同时可以实现背景的去除 1.2图像灰度化-根据灰度值 1.3根据图像色系范围进行二值化处理 2.去除噪点 去除噪点也可以分为如下: 2.1根据噪点周围的8个点的数目去除 2.2根据噪点周围的4个正向点去除 2.3根据有效链接点的数目去除 3.去除干扰线 一般干扰线都为1像素宽度,纵向或者横向的 4.图像修补,填充一些误删除的点,主要有以下几种方式: 4.1根据正向有效点的数目进行填充 4.2根据是否属于某直线的一部分进行填充 5.图像分割 查找连续的点,并组合数组,然后根据分割出来的长度,对字符进行组合或者分割 6.字符图像大小归一 将图像大小都设为一致 7.其他调整 抽骨、梯度等 8.查找特征点(譬如说图像的有效坐标等,根据具体情况具体分析),并确立图像对比方案,可以通过神经网络或者欧氏距离的方式 9.图片学习 做字库 10.图片识别!
...