1 制作数据集合
1.1 在word上输入一行数字,我用的是Calibri字体,已经比较接近发票数字了。网友们可以自行定义字体。
1.2 读入图片为灰度图,threshold化;并将图片颜色反转:字体为白背景为黑;对图像进行各种类型的膨胀,多样化数据。
-
# encoding: utf-8
-
import cv2
-
import numpy as np
-
import os
-
img0 = cv2.imread("./number.jpg",0)
-
_, img0 = cv2.threshold(img0, 100, 255, cv2.THRESH_BINARY)
-
img0 = 255 - img0 #反转:文字置为白色,背景置为黑色
-
#进行三种尺度的膨胀
-
element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 1))
-
element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 3))
-
element3 = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
-
img1 = cv2.dilate(img0, element1, iterations = 1)
-
img2 = cv2.dilate(img0, element2, iterations = 1)
-
img3 = cv2.dilate(img0, element3, iterations = 1)
-
#保存原始图和膨胀图,共四张
-
img = [img0,img1,img2,img3]
1.3 计算垂直直方图,用投影法分割出各个数字。
-
#对img0、img1、img2、img3进行分割,并保存分割后的图片
-
img_seg = []
-
for img_ in img:
-
img_seg.append(seg_num(img_))
编写的函数如下:
-
def cal_hist(img_,flag = 0):
-
return np.sum(img_,axis = flag)
-
class Find_num_region:
-
def __init__(self,img,hist):
-
self.cursor = -1
-
self.hist = hist
-
self.img = img
-
def next(self):
-
#将游标的位置前移一步,并返回所在检索位的矩形框
-
self.cursor = self.cursor+1
-
return self.hist[self.cursor]
-
def hasNext(self):
-
#判断是否已经检查完了所有矩形框
-
return len(self.hist)> self.cursor + 1
-
def find_start(self):
-
while(self.hasNext()):
-
hist_num = self.next()
-
if hist_num > 0:
-
return self.cursor
-
def find_end(self):
-
while(self.hasNext()):
-
hist_num = self.next()
-
if hist_num == 0:
-
return self.cursor
-
def get_num_region(self,flag = 0):
-
start = self.find_start()
-
end = self.find_end()
-
if flag == 0:
-
return self.img.copy()[:,start:end]
-
else:
-
return self.img.copy()[start:end,:]
-
def seg_num(img_):
-
hist_1 = cal_hist(img_,1)
-
find_num_ = Find_num_region(img_,hist_1)
-
img = find_num_.get_num_region(1)
-
hist_2 = cal_hist(img_)
-
find_num = Find_num_region(img_,hist_2)
-
img_number = []
-
for i in range(10):
-
print i
-
img_number.append( find_num.get_num_region() )
-
return img_number
1.4 图片的size设置为(28,28)对图片进行,进行缩放、旋转(仿射变换),然后增加随机噪声。
1.5 将数据保存npy格式,共10000组,每个数字满足“粗细、旋转角度、缩放比例、噪声分布”的多样化。数据集制作完毕。(数据量大的话,建议使用tfrecord格式)
-
img_arr = np.zeros((10000,28,28))
-
label_arr = np.zeros((10000,10))
-
for num in range(250): # 随机角度、缩放、噪声,200次
-
for i in range(10): #“0-9”共10个数
-
for j in range(4): #不同膨胀比
-
angle_ = np.random.uniform(-20,20)
-
scale_ = np.random.uniform(0.9,1.2)
-
img_c = change_(img_seg[j][i],angle_,scale_)
-
noise_num = np.random.randint(8,38)#噪声点个数
-
img_c = add_noise(img_c,noise_num)
-
img_arr[40*num+4*i+j] = img_c
-
label_arr[40*num+4*i+j][i] = 1
-
np.save("img.npy",np.array(img_arr))
-
np.save("label.npy",np.array(label_arr))
编写的函数如下:
-
#将图像设置为28×28,不拉伸,全0填充
-
def change_(img,angle_,scale_):
-
length = 28
-
h,w = img.shape
-
H = np.float32([[1,0,(length-w)/2],[0,1,(length-h)/2]])
-
img = cv2.warpAffine(img,H,(length,length))
-
M = cv2.getRotationMatrix2D((length/2,length/2),angle_,scale_)
-
return cv2.warpAffine(img,M,(length,length))
-
def add_noise(img,amout):
-
length = 28
-
for i in range(amout):
-
rand_ = int(np.random.rand()*length*length)
-
row = int(rand_/length)
-
col = int(rand_%length)
-
if img[row,col] == 0:
-
img[row,col] = 255
-
else:
-
img[row,col] = 0
-
return img
2 cnn训练
2.1 使用网络上的一个常见数字识别网络结构:2层卷积,2层全连接。
-
#encoding:utf-8
-
import tensorflow as tf
-
import numpy as np
-
def weight_variable(shape):
-
initial = tf.truncated_normal(shape, stddev=0.1)
-
return tf.Variable(initial)
-
def bias_variable(shape):
-
initial = tf.constant(0.1, shape=shape)
-
return tf.Variable(initial)
-
def conv2d(x, W):
-
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
-
def pool(x):
-
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
-
def inference(x_,keep_prob):
-
with tf.variable_scope("layer_conv1"): #卷积
-
W_conv1 = weight_variable([5, 5, 1, 32])
-
b_conv1 = bias_variable([32])
-
layer_conv1 = tf.nn.relu(conv2d(x_, W_conv1) + b_conv1)
-
layer_pool1 = pool(layer_conv1)
-
with tf.variable_scope("layer_conv2"):#卷积
-
W_conv2 = weight_variable([5, 5, 32, 64])
-
b_conv2 = bias_variable([64])
-
layer_conv2 = tf.nn.relu(conv2d(layer_pool1, W_conv2) + b_conv2)
-
layer_pool2 = pool(layer_conv2)
-
with tf.variable_scope("layer_fc3"): #全连接
-
W_fc3 = weight_variable([7 * 7 * 64, 1024])
-
b_fc3 = bias_variable([1024])
-
reshape_pool3 = tf.reshape(layer_pool2, [-1, 7*7*64])
-
layer_fc3 = tf.nn.relu(tf.matmul(reshape_pool3, W_fc3) + b_fc3)
-
with tf.variable_scope("dropout4"):
-
fc_drop4 = tf.nn.dropout(layer_fc3, keep_prob)
-
with tf.variable_scope("layer_fc5"): #全连接
-
W_fc5 = weight_variable([1024, 10])
-
b_fc5 = bias_variable([10])
-
predict_ = tf.nn.softmax(tf.matmul(fc_drop4, W_fc5) + b_fc5)
-
return predict_
-
def train(x_train,y_train,x_test,y_test):
-
batch_size = 230 #每个批次的大小
-
all_size = y_train.shape[0] #train集的大小
-
x_ = tf.placeholder("float", shape=[None,28,28,1],name='x_input')
-
y_ = tf.placeholder("float", shape=[None,10],name='y_input')
-
keep_prob = tf.placeholder("float")
-
predict_ = inference(x_,keep_prob)
-
cross_entropy = -tf.reduce_sum(y_*tf.log(predict_))
-
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
-
#测试准确率
-
correct_prediction = tf.equal(tf.argmax(predict_,1),tf.argmax(y_,1))
-
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
-
saver = tf.train.Saver()
-
with tf.Session() as sess:
-
sess.run(tf.initialize_all_variables())
-
for i in range(500):
-
start = (i*batch_size)%(all_size-1)
-
if start <(all_size-batch_size):
-
end1 = start+batch_size
-
end2 = 0
-
else:
-
end1 = (all_size-1)
-
end2 = start+batch_size-(all_size-1)
-
in_x = np.concatenate((x_train[start:end1],x_train[0:end2]),axis=0)
-
in_y = np.concatenate((y_train[start:end1],y_train[0:end2]),axis=0)
-
sess.run(train_step, feed_dict={x_:in_x,y_:in_y,keep_prob:0.5})
-
if i%50 == 0:
-
print "第",i,"次迭代:"
-
print "test集精度:", sess.run(accuracy, feed_dict={x_:x_test,y_:y_test,keep_prob:1.0})
-
print "train集精度:",sess.run(accuracy, feed_dict={x_:in_x,y_:in_y,keep_prob:1.0})
-
saver.save(sess,"./Model/model.ckpt") #
-
def main():
-
x_input = np.load("img.npy") #读入图片
-
y_input = np.load("label.npy") #读入标签
-
x_input = x_input/255.0 #原二值化图像的像素值分别为“0”,“255”。将“255”置为“1”
-
x_train,y_train,x_test,y_test = x_input[0:8000,:],y_input[0:8000,:],x_input[8000:10000,:],y_input[8000:10000,:]
-
train(x_train,y_train,x_test,y_test)
-
if __name__ == "__main__":
-
main()
2.2 模型很快收敛,精度100%。