Bootstrap

OpenCV+TensorFlow 通过摄像头对手写数字进行识别

本文主要记录了通过TensorFlow对一个拥有2个卷积(池化)层和2个全连接1层的卷积神经网络对模型进行训练,再使用已训练好的模型通过OpenCV调用摄像头对手写数字进行识别的过程

本文使用的 MNIST_data 数据集如下
链接:https://pan.baidu.com/s/19QXgCJD5itp0fR6COvJO-A
提取码:acf2

准备 :

首先在有OpenCV及TensorFlow的基础上下载前文提到的数据集并将其与.py文件放置在同一文件夹下(不放的话可以像下面那样写好绝对路径),如下图 :
在这里插入图片描述
再创建一个文件夹用来保存模型
在这里插入图片描述

r"D:\anaconda\vscode-python\learn\OpenCV_and_TensorFlow\Net\Net_MNIST\

CNN训练

源码及训练结果如下 :

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# 载入数据集
mnist = input_data.read_data_sets("MNIST_data",one_hot=True)

# 每个批次的大小
batch_size = 100
# 计算一共有多少个批次
n_batch = mnist.train.num_examples // batch_size

# 权值初始化
def weight_variable(shape):
    initial = tf.truncated_normal(shape,stddev=0.1)
    # 生成一个截断的正态分布,其标准差为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") 
    # x为输入的tensor,其形状为[batch, in_height, in_width, in_channels],具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数]
    # W为卷积核(滤波器),其形状为[filter_height, filter_width, in_channels, out_channels],具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数]
    # strides[0]和strides[3]的两个1是默认值,strides[1]代表x方向的步长,strides[2]代表y方向的步长
    # padding决定卷积方式,SAME会在外面补0

# 池化层(最大值池化)
def max_pool_2x2(x):
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")
    # ksize[0]和ksize[3]默认为1,中间的2,2为池化窗口的大小
    # strides同conv2d,明显x,y方向的步长均为2

# 定义两个placeholder
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])

# 改变x格式为4D的向量
x_image = tf.reshape(x,[-1,28,28,1]) 
# 第二个参数 : [batch, in_height, in_width, in_channels] [训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数]
# 其中-1在程序运行后将会被赋值为100(即每批次中包含的图片的数量)

# 初始化第一个卷积层的权值和偏置
W_conv1 = weight_variable([5,5,1,32]) 
# 5*5的采样窗口,32个卷积核(32个平面/32个通道)从一个平面(一个通道)提取特征
b_conv1 = bias_variable([32]) 
# 每一个卷积核一个偏置值

# 把x_image和权值向量进行卷积,再加上偏置值,并用relu激活函数
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1)
# x_image和W_conv1进行2D卷积操作再加上权值,最后输入relu激活函数

# 将第一卷积层输出进行池化
h_pool1 = max_pool_2x2(h_conv1)

# 初始化第二个卷积层的权值和偏置
W_conv2 = weight_variable([5,5,32,64]) # 5*5的采样窗口,64个卷积核从32个平面提取特征
b_conv2 = bias_variable([64]) # 每一个卷积核一个偏置值

# 把h_pool1和权值向量进行卷积,再加上偏置值,并用relu激活函数
h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2) + b_conv2)

# 将第二卷积层输出进行池化
h_pool2 = max_pool_2x2(h_conv2)

# 说下这些卷积池化的过程(数据形状) : 
#   28*28的图片第一次卷积后还是28*28(SAME padding不会改变图片的大小),第一次池化后变为14*14
#   14*14的图片第二次卷积后还是14*14,第二次池化后变为7*7
#   经上述操作后最后获得64张7*7平面

# 初始化第一个全连接层的权值
W_fc1 = weight_variable([7*7*64,1024]) 
# 上一层共有7*7*64个像素点,全连接层1共1024个神经元
b_fc1 = bias_variable([1024])
# 每个神经元一个偏置值

# 把池化层2的输出扁平化为1维
h_pool2_flat = tf.reshape(h_pool2,[-1,7*7*64])
# h_pool2为形状为[100(批次),7,7(高宽),64(图片或者通道数)]
# -1为任意值,计算时会处理为100
# 实际上就是将其后三个维度转化为7*7*64这一个维度

# 求第一个全连接层的输出
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1) + b_fc1) 

# 用keep_prob来表示神经元的输出概率(dropout)
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)

# 初始化第二个全连接层
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])

# 计算输出
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2) + b_fc2)

# 交叉熵代价函数
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=prediction))
# 使用AdamOptimizer进行优化
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# 结果存放在一个bool型列表中
correct_prediction = tf.equal(tf.argmax(prediction,1),tf.argmax(y,1)) # argmax返回一维张量中最大值所在的位置
# 求准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

saver = tf.train.Saver()

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for epoch in range(11):
        for batch in range(n_batch):
            # 传入数据
            batch_xs,batch_ys =  mnist.train.next_batch(batch_size)
            # 用70%的神经元训练网络(dropout)
            sess.run(train_step,feed_dict={x:batch_xs,y:batch_ys,keep_prob:0.7})

        # 运行100%的神经元来检测网络准确率
        acc = sess.run(accuracy,feed_dict={x:mnist.test.images,y:mnist.test.labels,keep_prob:1.0})
        # 输出
        print ("Iter " + str(epoch) + ", Testing Accuracy= " + str(acc))
       
    saver.save(sess, r"D:\anaconda\vscode-python\learn\OpenCV_and_TensorFlow\Net\Net_MNIST\my_net.ckpt")


# 输出结果
'''
Iter 0, Testing Accuracy= 0.9488
Iter 1, Testing Accuracy= 0.9667
Iter 2, Testing Accuracy= 0.9757
Iter 3, Testing Accuracy= 0.9786
Iter 4, Testing Accuracy= 0.9837
Iter 5, Testing Accuracy= 0.9829
Iter 6, Testing Accuracy= 0.9851
Iter 7, Testing Accuracy= 0.9859
Iter 8, Testing Accuracy= 0.9889
Iter 9, Testing Accuracy= 0.9887
Iter 10, Testing Accuracy= 0.9896
'''

这里因为网络基本已经收敛且效果不错故而不再进行过多训练(CPU表示很赞)

关于CNN,提供下我之前的一篇博客: tensorflow学习笔记 + 程序 (五)CNN与MNIST数据集
里面还有该网络模型进行可视化的一些效果,另外关于Tensorboard可视化,我之前的一篇博客也进行了相关介绍 : tensorflow学习笔记 + 程序 (四)tensorboard可视化

收获

emmmm…确切说上一步的收获就是下面这4个文件(前文的路径下)
在这里插入图片描述
关于文件保存和载入提供两篇文章 :
tensorflow保存和恢复模型的两种方法介绍
Tensorflow学习笔记-模型保存与加载

OpenCV+TensorFlow联用

源码如下 :

import tensorflow as tf
from cv2 import cv2
import numpy as np

# 权值初始化
def weight_variable(shape):
    initial = tf.truncated_normal(shape,stddev=0.1)
    # 生成一个截断的正态分布,其标准差为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 max_pool_2x2(x):
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")

# 网络模型
def network(x):

    # 改变x格式为4D的向量
    x_image = tf.reshape(x,[-1,28,28,1]) 

    # 把x_image和权值向量进行卷积,再加上偏置值,并用relu激活函数
    h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1)

    # 将第一卷积层输出进行池化
    h_pool1 = max_pool_2x2(h_conv1)

    # 把h_pool1和权值向量进行卷积,再加上偏置值,并用relu激活函数
    h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2) + b_conv2)

    # 将第二卷积层输出进行池化
    h_pool2 = max_pool_2x2(h_conv2)

    # 把池化层2的输出扁平化为1维
    h_pool2_flat = tf.reshape(h_pool2,[-1,7*7*64])

    # 求第一个全连接层的输出
    h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1) + b_fc1) 

    # 使用drop
    h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)

    # 计算输出
    prediction = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2) + b_fc2)

    return prediction


# 定义两个placeholder
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])

# 初始化第一个卷积层的权值和偏置
W_conv1 = weight_variable([5,5,1,32]) 
b_conv1 = bias_variable([32]) 

# 初始化第二个卷积层的权值和偏置
W_conv2 = weight_variable([5,5,32,64]) 
b_conv2 = bias_variable([64]) 

# 初始化第一个全连接层的权值
W_fc1 = weight_variable([7*7*64,1024]) 
b_fc1 = bias_variable([1024])

# 用keep_prob来表示神经元的输出概率(dropout)
keep_prob = tf.placeholder(tf.float32)

# 初始化第二个全连接层
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])

# 开启会话并导入模型
sess = tf.InteractiveSession()
saver = tf.train.Saver()
saver.restore(sess, r"D:\anaconda\vscode-python\learn\OpenCV_and_TensorFlow\Net\Net_MNIST\my_net.ckpt")

# 开启摄像头
cap = cv2.VideoCapture(0)

while(1):

    # 读取摄像头
    ret, frame = cap.read()

    # 在图像上绘制矩形框(红色)
    cv2.rectangle(frame,(320,150),(390,220),(0,0,255),2)
    cv2.imshow("capture", frame)

    # 确定ROI
    roiImg = frame[150:320,220:390]

    # 将该区域图像改变为CNN输入格式
    img = cv2.resize(roiImg,(28,28))

    # 将3通道改为单通道
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 改为float32格式
    np_img = img.astype(np.float32)
	
    # 连接网络
    netoutput = network(np_img)
    
    # 获取网络输出
    prediction = sess.run(netoutput,feed_dict={keep_prob:1.0})
    
    # 转换为列表并取结果
    predicts = prediction.tolist() 
    label = predicts[0]
    result = label.index(max(label))
    print('result num:')
    print(result)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

效果

在这里插入图片描述
看上去还不错

参考链接 :
通过摄像头捕获图像用tensorflow做手写数字识别

;