数据集和代码出处:https://gitee.com/zzhzwh/Opencv-project
软件:pycharm 解释器: python3.7
目录
1、添加相关库
方法:pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple
numpy==1.21.6
scikit-learn
matplotlib==3.5.2
tensorflow==2.0.0
opencv-python==4.2.0.34
keras==2.3.1
2、主要代码
2.1 训练部分training.py
2.1.1 需要的相关库
import numpy as np
import cv2
import os
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from keras.preprocessing.image import ImageDataGenerator
from keras.utils.np_utils import to_categorical
from keras.models import Sequential
from keras.layers.convolutional import Conv2D,MaxPooling2D
from keras.layers import Dropout,Flatten
from keras.layers import Dense
from keras.optimizers import Adam
import pickle
2.1.2 需要的变量区域
#########################################
path = 'myData'
testRation = 0.2
valRation = 0.2
imageDimensions = (32, 32, 3)
batchSizeVal = 50
epochsVal = 10
stepsPerEpochVal = 2000
##########################################
2.1.3 导入图片和标签
images = [] # 存放图片
classNo = [] # 存放标签
myList = os.listdir(path)
print("Total Number of Classes Detected:", len(myList))
noOfClasses = len(myList)
print("Importing Classes.......")
for x in range (0, noOfClasses): # 遍历mydata下面名称为0-9的文件夹
myPicList = os.listdir(path+"/"+str(x))
for y in myPicList: # 遍历每个文件夹的照片
curImg = cv2.imread(path+"/"+str(x)+"/"+y)
# 原图是128*128的,太耗费计算机了,就统一成32*32大小的图片
curImg = cv2.resize(curImg, (imageDimensions[0], imageDimensions[0]))
images.append(curImg) # images里面存放了所有的图片
classNo.append(x) # classNo存放了所有的图片所对应的标签
print(x, end=" ") # end=" "会让输出x的值在控制台横向显示
print(" ")
print("Total Images in Images List = ", len(images)) # 打印总共有多少张图片
print("Total IDs in classNo List = ", len(classNo)) # 打印总共有多少图片的标签
此时的输出结果:
2.1.4 将图片及标签变为numpy数组形式
images = np.array(images) # 将图片转化成numpy数组
classNo = np.array(classNo) # 将标签转化成numpy数组
print(images.shape) # 此处输出为(10160, 32, 32, 3),每张图片为32*32*3
2.1.5 划分数据集:X里放的图片,y放的是标签
# 划分训练集和测试集,testRation代表将多少划分为测试集。此处值为0.2,即数据集中的20%为测试集
X_train, X_test, y_train, y_test = train_test_split(images, classNo, test_size=testRation)
# 划分训练集和验证集
X_train, X_validation, y_train, y_validation = train_test_split(X_train, y_train, test_size=valRation)
print(X_train.shape) # 获得训练集的数量:6502
print(X_test.shape) # 获得测试集的数量:2032
print(X_validation.shape) # 获得验证集的数量:1626
此处输出结果:
第一行:总图片的shape
第二行:训练集的shape
第三行:测试集的shape
第四行:验证集的shape
2.1.6 查看十个类别的训练集分布是否均匀(画图)
numOfSamples = []
for i in range(0, noOfClasses):
# print(len(np.where(y_train==i)[0]))
numOfSamples.append(len(np.where(y_train==i)[0]))
print("No of Train:",numOfSamples) # 查看训练集里ID为0-9的图各有多少
# 画图
plt.figure(figsize=(10, 5))
plt.bar(range(0, noOfClasses), numOfSamples)
plt.title("No of Images for each Class")
plt.xlabel("Class ID")
plt.ylabel("Number of Images")
plt.show()
此处输出结果:
2.1.7 预处理图像
普通预处理函数、预处理所有的图像、重塑图像、图像生成器、标签转换为独热向量形式
#### 定义预处理的函数
def preProcessing(img):
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #灰度化
img = cv2.equalizeHist(img) #均衡化
img = img/255 #归一化
return img
#### 预处理所有的图像
# map使我们可以在一个列表或元素数组上运行一个函数,处理好之后放进一个列表里
X_train = np.array(list(map(preProcessing, X_train)))
X_test = np.array(list(map(preProcessing, X_test)))
X_validation = np.array(list(map(preProcessing, X_validation)))
#### 为图像增加深度。原来图像的为(32,32),重塑后变为(32,32,1)
X_train = X_train.reshape(X_train.shape[0], X_train.shape[1], X_train.shape[2], 1)
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], X_test.shape[2], 1)
X_validation = X_validation.reshape(X_validation.shape[0], X_validation.shape[1], X_validation.shape[2], 1)
#### 图像生成器
# 对每批次的训练图片适时进行数据增强(可增强模型的泛化能力),
# ImageDataGenerator()来创建一个图像生成器
dataGen = ImageDataGenerator(width_shift_range=0.1, # 随机水平位移10%
height_shift_range=0.1, # 随机垂直位移10%
zoom_range=0.2, # 随机缩放20%
shear_range=0.1, # 随机剪裁10%
rotation_range=10) # 随机旋转角度
# 调用fit()我们的图像生成器的功能,这将逐批地应用到图像的变化,
# 默认情况下,这些修改将被随机应用,所以并不是每一个图像都会被改变
dataGen.fit(X_train)
#### 标签转换为独热向量表示
y_train = to_categorical(y_train, noOfClasses)
y_test = to_categorical(y_test, noOfClasses)
y_validation = to_categorical(y_validation, noOfClasses)
2.1.8 定义模型
使用全连接的神经网络,使用序列模型Sequential进行搭建
#定义模型
def myModel():
#卷积核的数量,多个卷积核叠加起来组成一个滤波器 卷积核是二维的,滤波器是三维的
noOfFilters = 60
sizeOfFilter1 = (5, 5) #第一个卷积核的大小
sizeOfFilter2 = (3, 3) #第二个卷积核的大小
sizeOfPool = (2, 2) #池化层的大小
noOfNode = 500 #节点数
# 序列模型Sequential,是实现全连接网络的最好方式
model = Sequential() #搭建神经网络
model.add((Conv2D(noOfFilters,sizeOfFilter1,input_shape=(imageDimensions[0],
imageDimensions[1],
1),activation='relu'
)))
model.add((Conv2D(noOfFilters,sizeOfFilter1,activation='relu')))
model.add(MaxPooling2D(pool_size=sizeOfPool))
model.add((Conv2D(noOfFilters//2, sizeOfFilter2, activation='relu')))
model.add((Conv2D(noOfFilters//2, sizeOfFilter2, activation='relu')))
model.add(MaxPooling2D(pool_size=sizeOfPool))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(noOfNode,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(noOfClasses,activation='softmax'))
# 在使用sequential模型的时候要定义损失函数,优化器。
# lr:学习率 loss:损失函数
# 精确度,评估模型在训练和测试时的网络性能的指标
model.compile(Adam(lr=0.001),loss='categorical_crossentropy',
metrics=['accuracy'])
return model
model = myModel()
print(model.summary()) #打印模型
模型结构大概形式:
输出大小计算公式:
卷积层:
池化层:
此处的输出结果:
2.1.9 训练模型
# batch_size 整数,指定进行梯度下降时每个批次包含的样本数训练时一个批次的样本,
# 批量大小,即每批用batchSizeVal个样本(每经过batchSizeVal个样本更新一次权重)
# steps_per_epoch 每轮的步数,即每次迭代执行的批次
history = model.fit_generator(dataGen.flow(X_train,y_train,
batch_size=batchSizeVal),
steps_per_epoch=stepsPerEpochVal,
epochs=epochsVal, # 迭代次数
validation_data=(X_validation,y_validation),
shuffle=1) # 打乱(洗牌)
模型训练中:
2.1.10 对模型进行评估
score = model.evaluate(X_test,y_test,verbose=0)
print('Test Score = ', score[0]) # 打印测试的分数
print('Test Accuracy = ', score[1]) # 打印测试的准确率
输出结果:
2.1.11 保存模型
pickle_out = open("model_trained.p", "wb")
pickle.dump(model,pickle_out)
pickle_out.close()
训练结束后会出现一个“model_trained.p”的文件
2.2 应用(预测)
2.2.1 导入相关库
import numpy as np import cv2 import pickle
2.2.2 所有需要用到的变量
#################################################
# width = 640
# height = 480
threshold = 0.7
pickle_in = open("model_trained.p","rb") # 导入训练好的模型
model = pickle.load(pickle_in)
#################################################
2.2.3 预处理
def preProcessing(img):
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #灰度化
img = cv2.equalizeHist(img) #均衡化
img = img/255 #归一化
return img
2.2.4 单张图片读入进行识别,结果出现在控制台
img = cv2.imread("7.jpg")
img = np.asarray(img)
img = cv2.resize(img, (32, 32))
img = preProcessing(img)
img = img.reshape(1, 32, 32, 1) #这一步骤的目的,因为神经网络输入的就要是(32,32,1)
classIndex = int(model.predict_classes(img))
predictions = model.predict(img)
probVal = np.amax(predictions)
print("识别结果为:",classIndex)
print("概率为:", probVal)
此时输出结果:
完整代码:
import numpy as np
import cv2
import pickle
#################################################
threshold = 0.7
pickle_in = open("model_trained_10.p","rb")
model = pickle.load(pickle_in)
#################################################
##################################################
def preProcessing(img):
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #灰度化
img = cv2.equalizeHist(img) #均衡化
img = img/255 #归一化
return img
###################################################
## 单张图片读入进行识别,结果出现在控制台
img = cv2.imread("7.png")
img = np.asarray(img)
img = cv2.resize(img, (32, 32))
img = preProcessing(img)
img = img.reshape(1, 32, 32, 1) #这一步骤的目的,因为神经网络输入的就要是(32,32,1)
classIndex = int(model.predict_classes(img))
predictions = model.predict(img)
probVal = np.amax(predictions)
print("识别结果为:",classIndex)
print("概率为:", probVal)
另外:启用摄像头实时进行文本识别
使用的库和2.2.1一样
需要的变量:
#################################################
width = 640
height = 480
threshold = 0.7
pickle_in = open("model_trained.p","rb") # 导入训练好的模型
model = pickle.load(pickle_in)
#################################################
预处理代码和2.2.3一样
启用摄像头:
# 使用摄像头进行识别
#cap = cv2.VideoCapture(1) # 启用摄像头,如果是外接摄像头则为“1”,如果是笔记本自带的则为“0”
#cap.set(3,width) # 设置摄像头的宽度
cap.set(4,height) # 设置摄像头的高度
预测:
while True:
success, imgOriginal = cap.read()
img = np.asarray(imgOriginal)
img = cv2.resize(img, (32, 32))
img = preProcessing(img)
img = img.reshape(1, 32, 32, 1)
# 识别。和上面识别图片的代码一样
classIndex = int(model.predict_classes(img))
predictions = model.predict(img)
probVal = np.amax(predictions)
print(classIndex, probVal)
# 如果大于阈值,那么在视频上面显示出来识别结果和概率
if probVal>threshold:
cv2.putText(imgOriginal, str(classIndex)+" "+str(probVal), (50, 50), cv2.FONT_HERSHEY_COMPLEX,
1, (255, 0, 0),1)
cv2.imshow("Original Image", imgOriginal)
# 摁下键盘上的“q”键即可关闭摄像头
if cv2.waitKey(1) & 0xFF == ord('q'):
break
完整代码:
import numpy as np
import cv2
import pickle
#################################################
width = 640
height = 480
threshold = 0.7
pickle_in = open("model_trained_10.p","rb")
model = pickle.load(pickle_in)
#################################################
##################################################
def preProcessing(img):
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #灰度化
img = cv2.equalizeHist(img) #均衡化
img = img/255 #归一化
return img
###################################################
# 使用摄像头进行识别
cap = cv2.VideoCapture(1)
cap.set(3,width)
cap.set(4,height)
while True:
success, imgOriginal = cap.read()
img = np.asarray(imgOriginal)
img = cv2.resize(img, (32, 32))
img = preProcessing(img)
# cv2.imshow("processed Image", img)
img = img.reshape(1, 32, 32, 1)
#Predict
classIndex = int(model.predict_classes(img))
predictions = model.predict(img)
probVal = np.amax(predictions)
print(classIndex, probVal)
if probVal>threshold:
cv2.putText(imgOriginal, str(classIndex)+" "+str(probVal), (50, 50), cv2.FONT_HERSHEY_COMPLEX,
1, (255, 0, 0),1)
cv2.imshow("Original Image", imgOriginal)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 图片显示
if probVal>threshold:
cv2.putText(img, str(classIndex)+" "+str(probVal), (100, 100), cv2.FONT_HERSHEY_COMPLEX,
2, (0, 0, 255),2)
cv2.imshow("Original Image", img[0])
cv2.waitKey(0)
3、总结与收获
- 库的版本并不是越新越好,考虑兼容性,根据项目,下载相对应的版本。
- 图像生成器能够在一定程度上解决训练集不够的问题。
- 这个项目主要的收获是明白训练的步骤,神经网络的搭建。测试准确率虽然可以达到99%,但如果要应用实际中,还是需要更强大的神经网络。
- 在模型训练的过程中, 这个简单的神经网络,十轮,两千次,差不多也需要半个小时左右。所以在学习的过程中,拥有一个好的设备可能会减少一定的烦躁。