数字识别树莓派3+python3.5+opencv3.3+tensorflow1.7+keras
本文主要介绍如何利用树莓派3数字识别1-64数字图片,最近在做一个智能车的项目,通过识别赛场的数字来完成定位,在这里写一下自己的一些经验。
1.图片采集和标注
图像采集是通过opencv调用摄像头来采集图片的数据,场地的背景为蓝色,数字为白色。
通过opencv采集到的图像大小为320x240,对图像进行预处理,包括灰度化、平滑滤波、二值化。由于摄像头拍摄的角度是歪的,所以还做了一些透射变换,采集到的图像经过处理后效果如下:
为了方便数据的采集,这里写了一个小程序来采集数字为1-64的图像:
在采集图像之前,先输入数字号码,如10,然后调整好图片的角度,让数字尽量位于整个图片的中央,然后按下‘s’键采集图片,采集到的图片会保存在‘./num_data’文件夹下。
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
'''
author:Administrator
datetime:2018/5/11/011 19:49
software: PyCharm
'''
import cv2
import numpy as np
cap = cv2.VideoCapture(1)
cap.set(3,320)
cap.set(4,240)
ret, frame = cap.read()
rows, cols, channels = frame.shape
print(cols, rows, channels)
def get_point(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDBLCLK:
print(x,y)
cv2.namedWindow("image")
cv2.setMouseCallback("image", get_point)
# 图像预处理
def img_p(img):
# 灰度化
gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 平滑滤波
blur = cv2.blur(gray_img, (3,3))
# 二值化
ret1, th1 = cv2.threshold(blur, 190, 255, cv2.THRESH_BINARY)
# 透视变换
b = 50
pts1 = np.float32([[b, 0], [cols-b, 0], [0, rows], [cols, rows]])
pts2 = np.float32([[0, 0], [cols, 0], [0, rows], [cols, rows]])
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(th1, M, (cols, rows))
return dst
num = input("num:")
print(num)
while True:
# 读取图像
ret, frame = cap.read()
dst = img_p(frame)
k = cv2.waitKey(10)
if k == ord('q'):
break
elif k == ord('s'):
filename = r'./num_data/' + num + '.jpg'
cv2.imwrite(filename, dst)
print(filename)
num = input("num:")
print(num)
cv2.imshow("image", dst)
cap.release()
cv2.destroyAllWindows()
上面就是采集到的1-64的数字图片。
2.图片数据的扩增
采集到的数据数量有限,要用卷积神经网络来识别数字图片需要大量的数据,所以可以利用keras的图像预处理API对数据进行扩增,具体的API介绍可以参考keras中文文档https://keras.io/zh/preprocessing/image/。我写了一个数据扩增的小程序,每张图片生成了200张训练图片,100张测试图片,代码如下:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
'''
生成多个样本
author:Administrator
datetime:2018/3/24/024 18:46
software: PyCharm
'''
from keras.preprocessing.image import ImageDataGenerator,array_to_img, img_to_array, load_img
import os
# 生成文件夹
def ensure_dir(dir_path):
if not os.path.exists(dir_path):
try:
os.makedirs(dir_path)
except OSError:
pass
# 图片生成器ImageDataGenerator
pic_gen = ImageDataGenerator(
rotation_range=5,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.2,
zoom_range=0.2,
fill_mode='nearest')
# 生成图片
def img_create(img_dir, save_dir, img_prefix, num=100):
img = load_img(img_dir)
x = img_to_array(img)
x = x.reshape((1,) + x.shape)
img_flow = pic_gen.flow(
x,
batch_size=1,
save_to_dir=save_dir,
save_prefix=img_prefix,
save_format="jpg"
)
i = 0
for batch in img_flow:
i += 1
if i > num:
break
# 生成训练集
for i in range(1, 65, 1):
img_prefix = str(i)
img_dir = './num_data/' + img_prefix + '.jpg'
save_dir = './data/train/' + img_prefix
ensure_dir(save_dir)
img_create(img_dir, save_dir, img_prefix, num=200)
print("train: ", i)
# 生成测试集
for i in range(1, 65, 1):
img_prefix = str(i)
img_dir = './num_data/' + img_prefix + '.jpg'
save_dir = './data/validation/' + img_prefix
ensure_dir(save_dir)
img_create(img_dir, save_dir, img_prefix, num=100)
print("validation: ", i)
执行上面的代码,在工程目录下就会生成一些经过旋转平移缩放的数据图片
3.搭建神经网络训练样本
接下来就是通过keras来搭建一个简单的卷积神经网络来训练一个1-64的图片分类器,采用的tensorflow1.7作为后端,在GPU上训练,每次训练都会保存最佳的训练权重。代码如下:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
'''
训练数据
author:Administrator
datetime:2018/3/24/024 19:52
software: PyCharm
'''
# 对样本进行预处理
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Dropout, Flatten, Dense
from keras.callbacks import ModelCheckpoint
# 设置训练参数
nb_train_samples = 10000 # 训练样本数
nb_validation_samples = 1000 # 测试样本数
nb_epoch = 20 #训练轮数
batch_size = 32 #批次大小
# 图片尺寸
img_width, img_height, channels= 160, 120, 1
input_shape = (img_width, img_height, channels)
# 训练和测试数据路径
target = './data/'
train_data_dir = target + 'train'
validation_data_dir = target + 'validation'
# 图片生成器ImageDataGenerator
train_pic_gen = ImageDataGenerator(
rescale=1. / 255, # 对输入图片进行归一化到0-1区间
# 根据需求进行进一步调整
# rotation_range=5,
# width_shift_range=0.1,
# height_shift_range=0.1,
)
# 测试集不做变形处理,只需归一化。
validation_pic_gen = ImageDataGenerator(rescale=1. / 255)
# 按文件夹生成训练集流和标签,
train_flow = train_pic_gen.flow_from_directory(
train_data_dir,
target_size=(img_width, img_height), #调整图像大小
batch_size=batch_size,
color_mode='grayscale', #输入图片为灰度图片
# color_mode='rgb',
classes=[str(i) for i in range(1,65,1)],
class_mode='categorical')
# 按文件夹生成测试集流和标签,
validation_flow = validation_pic_gen.flow_from_directory(
validation_data_dir,
target_size=(img_width, img_height), #调整图像大小
batch_size=batch_size,
color_mode='grayscale', #输入图片为灰度图片
# color_mode='rgb',
classes=[str(i) for i in range(1, 65, 1)], # 标签
class_mode='categorical' #多分类
)
# 搭建模型
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(64, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer="adam", metrics=['accuracy'])
model.summary()
# 回调函数,保存最佳训练参数
checkpointer = ModelCheckpoint(filepath="./weights/weights.h5", verbose=1, save_best_only=True)
# 导入上次训练的权重
try:
model.load_weights('./weights/weights.h5')
print("load weights...")
except:
print("not weights")
pass
# 数据流训练API
model.fit_generator(
train_flow,
steps_per_epoch=nb_train_samples/batch_size,
epochs=nb_epoch,
validation_data=validation_flow,
validation_steps=nb_validation_samples/batch_size,
callbacks=[checkpointer]
)
经过了大概30分钟训练,训练集达到了96%以上的正确率,测试集达到了99.2%的正确率。
4.测试训练效果
我写了一个测试程序,从摄像头采集一张图片,经过图像预处理后保存到本地,然后输入到之前训练好的卷积神经网络进行预测,将预测的结果显示在原图上,测试的代码如下:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
'''
摄像头采集图片验证
author:Administrator
datetime:2018/3/25/025 9:27
software: PyCharm
'''
import cv2
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing import image
from keras.optimizers import SGD
import numpy as np
# 图片尺寸
img_width, img_height, channels= 160, 120, 1
input_shape = (img_width, img_height, channels)
# img_path = './prediction/pre0.png'
# 获取图片并进行预处理
def img_pre(img_path = './temp.png'):
img = image.load_img(img_path, grayscale=True, target_size=(img_width, img_height))
# plt.imshow(img)
# plt.show()
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x /= 255.0
# print(x.shape)
return x
# 搭建神经网络
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dense(64, activation='softmax'))
# 载入权重
model.load_weights('./weights/weights.h5')
# 图像预处理
def img_p(img):
rows, cols, channels = img.shape
# 灰度化
gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 平滑滤波
blur = cv2.blur(gray_img, (3,3))
# 二值化
ret1, th1 = cv2.threshold(blur, 190, 255, cv2.THRESH_BINARY)
# 透视变换
b = 50
pts1 = np.float32([[b, 0], [cols-b, 0], [0, rows], [cols, rows]])
pts2 = np.float32([[0, 0], [cols, 0], [0, rows], [cols, rows]])
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(th1, M, (cols, rows))
return dst
cap = cv2.VideoCapture(1) # 打开usb摄像头
cap.set(3, 320)
cap.set(4, 240)
ret, frame = cap.read() # 读取一帧图片
print(frame.shape)
num = ''
while True:
ret, frame = cap.read() # 读取一帧图片
# 判断按键按下
k = cv2.waitKey(1)
if k == ord('s'): # 如果‘s’键按下,截图保存图片到电脑
# 图片预处理
dst = img_p(frame)
cv2.imwrite('temp.png', dst)
cv2.imshow('dst', dst)
# 读取归一化处理过的图片
xr = img_pre()
# 进行预测,返回分类结果
classes = model.predict_classes(xr)[0]
num = str(classes + 1)
print("num:", num) # 打印结果
elif k == ord('q'):
break
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(frame, num, (10, 50), font, 2, (0, 0, 255), 4, cv2.LINE_AA) # 在图片上显示预测结果
cv2.imshow('frame', frame) # 显示图片
cv2.destroyAllWindows()
cap.release()
按下键盘上的‘s’键截取图片,测试的效果如上图所示。
5.在树莓派3上进行部署
首先要在树莓派3上搭建opencv3.3,tensorflow1.7和keras环境,可以参考tju_cc的博客:点击打开链接,有详细的教程。keras安装可以参考官方文档,也可以通过sudo pip3 install keras 来安装。
首先在树莓派home目录下 创建一个number的文件夹,通过FTP传输工具将权重文件weights和test.py文件上传到树莓派,,然后可以通过VNC远程桌面工具来连接树莓派,运行测试程序。
打开VNC终端,运行测试程序,加载权重文件有点慢。。。按下's'键截图保存,送到神经网络进行预测。(如果感觉树莓派运行卡的话,可以尝试将树莓派CPU超频到1.4GHz,GPU超频到500MHz,把显存调到256M)。
到此为止,就完成了摄像头对数字图片的采集和识别。
更多推荐
所有评论(0)