2019-6-18车牌识别尝试-图像抗扭处理和SVM学习(opencv)抗扭曲函数deskew
利⽤opencv中svm算法学习图⽚和识别图⽚
nao开头的成语
抽取特征向量函数hot分析
抗扭曲函数deskew
训练数据中有些图像是扭曲的,需要做抗扭曲处理,也就是把歪了的图⽚摆正
以下函数是在⽹上搜来的
# 使⽤⽅向梯度直⽅图Histogram of Oriented Gradients (HOG)作为特征向量
def deskew(img): #对⼀个图像进⾏抗扭斜(deskew)处理,把歪了的图⽚摆正
m = s(img) # 计算图像中的中⼼矩(最⾼到三阶)
if abs(m['mu02']) < 1e-2:
py()
skew = m['mu11']/m['mu02']
M = np.float32([[1, skew, -0.5*SZ*skew], [0, 1, 0]])
# 图像的平移,参数:输⼊图像、变换矩阵、变换后的⼤⼩
img = cv2.warpAffine(img, M, (SZ, SZ), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR)
只谈钱不说爱
return img
测试⼀下
原图
抗扭曲处理后
cv2.imwrite('0-deskew.jpg', deskew(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)))
图像摆正了。
利⽤opencv中svm算法学习图⽚和识别图⽚
train_svm,训练识别图⽚中字符。
初始化SVM实例,设置属性。这是opencv⾃带的
依次读取train\chars2⽬录中训练⽤图⽚,转化为灰度图,保存到训练数据集合。
保存分类标签到对应的标签集合。每个字符图⽚保存在以该字符名称对应的⽬录中,所以把⽬录名称对应的ASCII码作为训练数据的分类。
调⽤deskew函数,对训练图⽚(灰度图)进⾏抗扭曲处理,摆正
调⽤hot函数,获得每张图⽚的特征向量(64个)
调⽤SVM实例的train⽅法,送⼊训练数据集合和标签集合进⾏机器学习。
学习结果(⽀持向量机)保存在svm.dat⽂件中,⽤于识别图⽚(车牌)时候使⽤。识别是利⽤svm的predict⽅法,也是opencv 中⾃带的。
hot,⽤于从图⽚中抽取特征向量(64个)。
计算图像 X ⽅向和 Y ⽅向的 Sobel 导数
计算得到每个像素的梯度⾓度angle和梯度⼤⼩magnitude
把这个梯度的⾓度转换成 0⾄16 之间的整数
将图像分为4个⼩的⽅块,对每⼀个⼩⽅块计算它们梯度⾓度的直⽅图histogram(16个 bin),使⽤梯度的⼤⼩做权重。每⼀个⼩⽅块都会得到⼀个含有16个值的向量,4 个⼩⽅块的4个向量就组成了这个图像的特征向量(包含64个值)。
直⽅图就是对图像中的像素点的值进⾏统计,得到⼀个统⼀的整体的灰度概念。
⼀般情况下直⽅图都是灰度图像,直⽅图x轴是灰度值(⼀般0~255),y轴就是图像中每⼀个灰度级对应的像素点的个数。
直⽅图的好处就在于可以清晰了解图像的整体灰度分布,这对于后⾯依据直⽅图处理图像来说⾄关重要。
直⽅图计算实现可以使⽤np.histogram和np.bincount()函数。bincount计算的速度更快。
SZ = 20 #训练图⽚长宽
class StatModel(object):
def load(lf, fn):
def save(lf, fn):
#利⽤OpenCV中的SVM进⾏机器学习
class SVM(StatModel):
def __init__(lf, C = 1, gamma = 0.5):
#属性设置
#训练svm
def train(lf, samples, respons): #SVM的训练函数
#字符识别
def predict(lf, samples):
石油化工专业r = lf.model.predict(samples)
return r[1].ravel()
#来⾃opencv的sample,⽤于svm训练
#获得数据的特征向量
def hog(digits):
samples = []
'''
step1.先计算图像 X ⽅向和 Y ⽅向的 Sobel 导数。
step1.先计算图像 X ⽅向和 Y ⽅向的 Sobel 导数。
step2.然后计算得到每个像素的梯度⾓度angle和梯度⼤⼩magnitude。
step3.把这个梯度的⾓度转换成 0⾄16 之间的整数。
step4.将图像分为 4 个⼩的⽅块,对每⼀个⼩⽅块计算它们梯度⾓度的直⽅图(16 个 bin),使⽤梯度的⼤⼩做权重。这样每⼀个⼩⽅块都会得到⼀个含有 16 个值的向量。
4 个⼩⽅块的 4 个向量就组成了这个图像的特征向量(包含 64 个值)。
这就是我们要训练数据的特征向量。
'''
for img in digits:
#plt.subplot(221)
#plt.imshow(img,'gray')
# step1.计算图像的 X ⽅向和 Y ⽅向的 Sobel 导数
gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
mag, ang = cv2.cartToPolar(gx, gy) # step2.笛卡尔坐标(直⾓/斜⾓坐标)转换为极坐标, → magnitude, angle
bin_n = 16
bin = np.int32(bin_n*ang/(2*np.pi)) #step3. quantizing binvalues in (0...16)。2π就是360度。
#step4. Divide to 4 sub-squares
bin_cells = bin[:10,:10], bin[10:,:10], bin[:10,10:], bin[10:,10:]
mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:]
#zip() 函数⽤于将可迭代的对象作为参数,将对象中对应的元素打包成⼀个个元组,然后返回由这些元组组成的列表。 #a = [1,2,3];b = [4,5,6];zipped = zip(a,b) 结果[(1, 4), (2, 5), (3, 6)]
hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
hist = np.hstack(hists) # hist is a 64 bit vector孝敬父母手抄报
#plt.subplot(223)
#plt.plot(hist)
# transform to Hellinger kernel
eps = 1e-7
hist /= hist.sum() + eps
hist = np.sqrt(hist)
hist /= norm(hist) + eps
#plt.subplot(224)
#plt.plot(hist)
#plt.show()
samples.append(hist)
return np.float32(samples)
def train_svm():
#识别英⽂字母和数字
model = SVM(C=1, gamma=0.5)
if ists("svm.dat"):
model.load("svm.dat") #如果存在,不再训练,直接导⼊训练好的结果
el:
chars_train = []
chars_label = []
start_time = w()
for root, dirs, files in os.walk("train\\chars2"):
"""
root:所指的是当前正在遍历的这个⽂件夹的本⾝的地址
dirs:是⼀个 list ,内容是该⽂件夹中所有的⽬录的名字(不包括⼦⽬录)
files:同样是 list , 内容是该⽂件夹中所有的⽂件(不包括⼦⽬录)
train\chars2⽬录下保存有数字和⼤写字母图⽚,⽤于训练
"""
#os.path.baname(),返回path最后的⽂件名
#例:root=train\chars2\7,那么os.path.baname(root)=7
if len(os.path.baname(root)) > 1:#⽬录是单个字母或者数字
if len(os.path.baname(root)) > 1:#⽬录是单个字母或者数字
continue
root_int = ord(os.path.baname(root)) #转化为ASCII字符
for filename in files:
filepath = os.path.join(root,filename)
digit_img = cv2.imread(filepath)
#www.aiuai/aifarm365.html
#把图⽚转化为灰度图
#print 'filename: '+filename
digit_img = cv2.cvtColor(digit_img, cv2.COLOR_BGR2GRAY)
#print digit_img.shape #打印测试⼀下,可以看到是单通道的灰度图。(20L, 20L)
#采⽤PIL库可视化显⽰⼀下灰度图
#img_pil = Image.fromarray(digit_img); #Image.fromarray实现array到image的转换
#img_pil.show()
#print '***************************************\n'
#print digit_img #打印⼀下转化的灰度图矩阵
chars_train.append(digit_img) #训练样本集合
#chars_label.append(1)
chars_label.append(root_int) #训练样本标签,这⾥⽤字符的ASCII表⽰
end_time = w()
print '----------'
print start_time,end_time,(end_time - start_time).conds #116秒
#map() 会根据提供的函数对指定序列做映射。
#第⼀个参数 function 以参数序列中的每⼀个元素调⽤ function 函数,返回包含每次 function 函数返回值的新列表。 #把灰度图的训练样本集合中每个元素逐⼀送⼊deskew函数进⾏抗扭斜处理--也就是把图⽚摆正
chars_train = list(map(deskew, chars_train))
chars_train = hog(chars_train)#获得特征向量
#print '---chars_label---'
#print chars_label
#chars_train = shape(-1, 20, 20).astype(np.float32)
chars_label = np.array(chars_label)
print(chars_train.shape) #(13161L, 64L),13161个图⽚⽂件-训练数据,每个数据有64个特征值
print chars_train[1,:] #打印第⼀⾏,看⼀下数据
'''
[0.24786814 0.1749242 0.08778299 0.00753186 0.01336179 0.00632773
0.00311627 0.19113976 0.17932086 0.10021677 0.16336742 0.00659011
0.04025392 0.00959834 0.13348056 0.14601281 0.22560228 0.11686099
0.0091089 0.04411138 0.11550057 0.10916684 0.07274474 0.25117615
0.15250775 0.00763328 0. 0. 0.03396014 0.04497053
0.13273223 0.2175272 0.16252412 0. 0. 0.
0. 0.07764773 0.18924566 0.23582923 0.19936399 0.00881415
0. 0.03948144 0.10984423 0.08513948 0.11275985 0.22381228
0.25372562 0.07138055 0.11284041 0.11512272 0.01229106 0.
0.00824488 0.17906618 0.31182316 0.04935603 0.03621576 0.
0. 0. 0. 0.16095926]
'''
print (chars_label.shape) #⼀维矩阵,长度13161L。每个训练数据的标签(分类)
if not ists("svm.dat"):
model.save("svm.dat")
#测试⼀下训练结果
img = cv2.imread('test-0.jpg')
img = cv2.imread('test-0.jpg')
img = size(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), (SZ, SZ), interpolation=cv2.INTER_AREA) #print img
resp = model.predict(hog([img]))
心向远方
charactor = chr(resp[0])
print '--result---'+charactor #--result---0
代码中使⽤的测试图⽚如下
结果正确。
抽取特征向量函数hot分析
少年英语尝试把测试数据test-0.jpg在hot中中间变量逐⼀打印出来,可以理解转化和抽取特征变量过程
1.把梯度的⾓度转化为0⾄16之间的整数
bin = np.int32(bin_n*ang/(2 *np.pi))模仿
print bin,打印结果,20x20。因为图⽚是20x20像素的。
[[ 0 0 8 8 0 0 0 0 8 8 0 0 8 8 8 8 8 0 0 0]
[ 0 0 6 6 1 0 0 0 9 11 12 12 14 6 6 6 8 0 0 0]
[ 0 15 12 8 14 0 0 0 9 10 12 13 14 7 6 7 7 0 0 0]
[ 4 0 12 11 12 0 0 8 9 9 12 15 15 9 7 7 9 0 0 0]
[12 8 1 3 5 15 15 8 8 9 9 15 0 0 8 8 9 0 0 0]
[12 9 0 0 4 0 15 8 7 7 1 0 0 15 8 8 6 0 0 0]
[ 0 1 1 11 14 0 0 7 8 8 14 15 15 15 8 8 8 0 0 0]
[12 14 9 7 14 15 0 7 8 8 0 15 15 0 7 8 9 0 0 0]
[ 0 14 12 14 4 0 0 7 7 7 12 15 0 15 8 7 6 0 0 0]
[ 0 0 0 0 0 0 0 8 7 7 4 0 15 15 7 7 8 0 0 0]
[ 0 0 0 0 0 0 0 8 8 8 6 15 15 0 7 8 8 0 0 0]
[ 0 0 0 0 0 0 0 7 7 8 10 0 0 0 8 8 6 0 0 0]
[ 0 0 0 0 0 0 0 7 8 8 4 0 15 15 7 8 8 0 0 0]
[ 0 0 0 0 0 0 0 8 8 8 0 0 15 15 7 7 9 0 0 0]
[ 0 0 0 0 0 0 0 7 7 8 0 0 0 1 8 8 0 0 0 0]
贵港美食[ 4 3 4 1 0 15 15 7 7 6 0 0 0 6 8 8 0 0 0 0]
[ 4 1 4 8 0 15 15 7 6 6 2 1 0 9 9 8 6 0 0 0]
[ 4 2 7 4 2 15 15 15 6 5 2 2 1 9 9 9 8 0 0 0]
[12 15 8 0 14 15 14 14 5 4 3 1 9 8 8 6 8 0 0 0]
[ 0 0 8 0 0 0 0 0 0 8 0 0 8 8 8 8 8 0 0 0]]
2.将图像分为 4 个⼩的⽅块
bin_cells = bin[:10,:10], bin[10:,:10], bin[:10,10:], bin[10:,10:],经过转化的⾓度
说明:获得矩阵中的⼦矩阵,[⾏开始:⾏结束,列开始:列结束]
print bin_cells,打印结果