Skip to content

Commit 57daf48

Browse files
committed
finish the final example of model 3
1 parent a766707 commit 57daf48

20 files changed

+654
-53
lines changed

.vscode/chapter3.md

+31
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,37 @@ waitKey()与waitKey(0)代表窗口无线等待,直到有按键按下。waitKey
159159
160160
我们在看openCV 的材料时会经常看到```cv2.waitKey(1) & 0xFF == ord('q')```这样的写法来获取用户按键操作的。其意思为:**0xff** 是一个16进制的数,转换成二进制就 **1111 1111** 占八个位,这样和waitKey()进行与运算后,就将其八位之前的所有数都变成了0。**ord('q')**是q转换成ASCII码值,和刚刚算出来的后八位进行比较。因此我们可以获得用户的按键信息从而控制。
161161
162+
#### 1.3 视频人脸检测性别
163+
164+
除了使用openCv已有的模型进行人脸的识别,我们可以引入一些训练好的数据模型来扩展我们的功能。这个案例中,我们可以体验一下检测视频中出现人的性别。
165+
166+
1. 首先我们需要安装**TensorFlow** 的包。在terminal中输入```pip install tensorflow``` 点击回车就可以安装python下的TensorFlow了。
167+
168+
**TensorFlow**
169+
170+
171+
#### 1.4 训练一个模型
172+
##### 1.4.1 Keras
173+
Keras 是一个用 Python 编写的高级神经网络 API,它能够以 TensorFlow, CNTK, 或者 Theano 作为后端运行。Keras 的开发重点是支持快速的实验。能够以最小的时延把你的想法转换为实验结果,是做好研究的关键。[来自官方文档](https://keras.io/zh/)
174+
175+
Keras 的适用范围:
176+
+ 允许简单而快速的原型设计(由于用户友好,高度模块化,可扩展性)。
177+
+ 同时支持卷积神经网络和循环神经网络,以及两者的组合。
178+
+ 在 CPU 和 GPU 上无缝运行。
179+
180+
Keras 的模块结构图:
181+
<img src= "../mdSrc/kerasModelStruct.png" width="800"></img>
182+
183+
184+
Keras 神经网络结构图:
185+
<img src = "../mdSrc/kerasNeuralNetworkModel.png">
186+
187+
**以上两图均来自于网络**
188+
189+
190+
191+
192+
162193
---
163194
164195
```

__pycache__/catch_face.cpython-37.pyc

1.37 KB
Binary file not shown.
577 Bytes
Binary file not shown.

__pycache__/face_train.cpython-37.pyc

5.49 KB
Binary file not shown.

__pycache__/load_data.cpython-37.pyc

1.5 KB
Binary file not shown.
1.55 KB
Binary file not shown.

catch_face.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import cv2
2+
3+
4+
def CatchFace(window_name, catch_pic_num, path_name):
5+
cv2.namedWindow(window_name)
6+
cap = cv2.VideoCapture(0)
7+
classfier = cv2.CascadeClassifier(
8+
r"./openCv/opencv/data/haarcascades/haarcascade_frontalface_alt2.xml"
9+
)
10+
color = (115, 233, 86)
11+
num = 1
12+
while cap.isOpened():
13+
try:
14+
ok, frame = cap.read()
15+
grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
16+
faceRects = classfier.detectMultiScale(
17+
grey, scaleFactor=1.2, minNeighbors=3, minSize=(80, 80)
18+
)
19+
if len(faceRects) > 0:
20+
for (x, y, w, h) in faceRects:
21+
img_name = '%s/%d.jpg' % (path_name, num) # 定义图片存储路径+图片名称
22+
image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
23+
cv2.imwrite(img_name, image) # 将当前帧保存为图片
24+
num += 1
25+
if num > (catch_pic_num): # 成功捕捉超过1000次突出循环
26+
break
27+
cv2.rectangle(
28+
frame, (x - 10, y - 10), (
29+
x + w + 10, y + h + 10
30+
), color, 2
31+
) # 画矩形
32+
cv2.putText(
33+
frame, 'num:%d' % (num), (
34+
x + 30, y + 30
35+
), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 4
36+
) # 显示当前捕捉人脸图片是第几个
37+
# 超过指定最大保存数量结束程序
38+
if num > (catch_pic_num):
39+
break
40+
cv2.imshow(window_name, frame)
41+
c = cv2.waitKey(10)
42+
if c & 0xFF == ord('q'):
43+
break
44+
except BaseException:
45+
continue
46+
cap.release()
47+
cv2.destroyAllWindows()
48+
49+
50+
if __name__ == '__main__':
51+
CatchFace("CatchFace", 1000, './faceData/posFaceData')

clear_folder.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import os
2+
3+
4+
def clearFolder(folderPath):
5+
for i in os.listdir(folderPath):
6+
path_file = os.path.join(folderPath, i)
7+
if os.path.isfile(path_file):
8+
os.remove(path_file)
9+
else:
10+
for f in os.listdir(path_file):
11+
path_file2 = os.path.join(path_file, f)
12+
if os.path.isfile(path_file2):
13+
os.remove(path_file2)
14+
print("Folder has been cleared")
15+
16+
17+
if __name__ == '__main__':
18+
clearFolder('./faceData/posFaceData')

faceData/negFaceData/1.jpg

Loading

faceData/posFaceData/1.jpg

Loading

face_train.py

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import random
2+
# import numpy as np
3+
from sklearn.model_selection import train_test_split
4+
from keras.preprocessing.image import ImageDataGenerator
5+
from keras.models import Sequential
6+
from keras.layers import Dense, Dropout, Activation, Flatten
7+
from keras.layers import Convolution2D, MaxPooling2D
8+
from keras.optimizers import SGD
9+
from keras.utils import np_utils
10+
from keras.models import load_model
11+
from keras import backend as K
12+
from load_data import load_dataset, resize_image, IMAGE_SIZE
13+
14+
15+
class Dataset:
16+
def __init__(self, path_name):
17+
# 训练集
18+
self.train_images = None
19+
self.train_labels = None
20+
# 验证集
21+
self.valid_images = None
22+
self.valid_labels = None
23+
# 测试集
24+
self.test_images = None
25+
self.test_labels = None
26+
# 数据集加载路径
27+
self.path_name = path_name
28+
# 当前库采用的维度顺序
29+
self.input_shape = None
30+
31+
# 加载数据集并按照交叉验证的原则划分数据集并进行相关预处理工作
32+
def load(self, img_rows=IMAGE_SIZE, img_cols=IMAGE_SIZE,
33+
img_channels=3, nb_classes=2):
34+
# 加载数据集到内存
35+
images, labels = load_dataset(self.path_name)
36+
train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size=0.3, random_state=random.randint(0, 100))
37+
_, test_images, _, test_labels = train_test_split(
38+
images, labels, test_size=0.5, random_state=random.randint(0, 100)
39+
)
40+
# 当前的维度顺序如果为'channels_first',则输入图片数据时的顺序为:channels,rows,cols,否则:rows,cols,channels
41+
# 这部分代码就是根据keras库要求的维度顺序重组训练数据集
42+
if K.image_data_format() == 'channels_first':
43+
train_images = train_images.reshape(
44+
train_images.shape[0], img_channels, img_rows, img_cols
45+
)
46+
valid_images = valid_images.reshape(
47+
valid_images.shape[0], img_channels, img_rows, img_cols
48+
)
49+
test_images = test_images.reshape(
50+
test_images.shape[0], img_channels, img_rows, img_cols
51+
)
52+
self.input_shape = (img_channels, img_rows, img_cols)
53+
else:
54+
train_images = train_images.reshape(
55+
train_images.shape[0], img_rows, img_cols, img_channels
56+
)
57+
valid_images = valid_images.reshape(
58+
valid_images.shape[0], img_rows, img_cols, img_channels
59+
)
60+
test_images = test_images.reshape(
61+
test_images.shape[0], img_rows, img_cols, img_channels
62+
)
63+
self.input_shape = (img_rows, img_cols, img_channels)
64+
# 输出训练集、验证集、测试集的数量
65+
print(train_images.shape[0], 'train samples')
66+
print(valid_images.shape[0], 'valid samples')
67+
print(test_images.shape[0], 'test samples')
68+
# 我们的模型使用categorical_crossentropy作为损失函数,因此需要根据类别数量nb_classes将
69+
# 类别标签进行one-hot编码使其向量化,在这里我们的类别只有两种,经过转化后标签数据变为二维
70+
train_labels = np_utils.to_categorical(train_labels, nb_classes)
71+
valid_labels = np_utils.to_categorical(valid_labels, nb_classes)
72+
test_labels = np_utils.to_categorical(test_labels, nb_classes)
73+
# 像素数据浮点化以便归一化
74+
train_images = train_images.astype('float32')
75+
valid_images = valid_images.astype('float32')
76+
test_images = test_images.astype('float32')
77+
# 将其归一化,图像的各像素值归一化到0~1区间
78+
train_images /= 255
79+
valid_images /= 255
80+
test_images /= 255
81+
self.train_images = train_images
82+
self.valid_images = valid_images
83+
self.test_images = test_images
84+
self.train_labels = train_labels
85+
self.valid_labels = valid_labels
86+
self.test_labels = test_labels
87+
88+
89+
# CNN网络模型类
90+
class Model:
91+
def __init__(self):
92+
self.model = None
93+
94+
# 建立模型
95+
def build_model(self, dataset, nb_classes=2):
96+
# 构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型
97+
self.model = Sequential()
98+
# 以下代码将顺序添加CNN网络需要的各层,一个add就是一个网络层
99+
self.model.add(Convolution2D(
100+
32, 3, 3, border_mode='same', input_shape=dataset.input_shape
101+
)) # 1 2维卷积层
102+
self.model.add(Activation('relu')) # 2 激活函数层
103+
self.model.add(Convolution2D(32, 3, 3)) # 3 2维卷积层
104+
self.model.add(Activation('relu')) # 4 激活函数层
105+
self.model.add(MaxPooling2D(pool_size=(2, 2))) # 5 池化层
106+
self.model.add(Dropout(0.25)) # 6 Dropout层
107+
self.model.add(Convolution2D(64, 3, 3, border_mode='same')) # 7 2维卷积层
108+
self.model.add(Activation('relu')) # 8 激活函数层
109+
self.model.add(Convolution2D(64, 3, 3)) # 9 2维卷积层
110+
self.model.add(Activation('relu')) # 10 激活函数层
111+
self.model.add(MaxPooling2D(pool_size=(2, 2))) # 11 池化层
112+
self.model.add(Dropout(0.25)) # 12 Dropout层
113+
self.model.add(Flatten()) # 13 Flatten层
114+
self.model.add(Dense(512)) # 14 Dense层,又被称作全连接层
115+
self.model.add(Activation('relu')) # 15 激活函数层
116+
self.model.add(Dropout(0.5)) # 16 Dropout层
117+
self.model.add(Dense(nb_classes)) # 17 Dense层
118+
self.model.add(Activation('softmax')) # 18 分类层,输出最终结果
119+
# 输出模型概况
120+
self.model.summary()
121+
122+
# 训练模型
123+
def train(
124+
self, dataset, batch_size=20, nb_epoch=10, data_augmentation=True
125+
):
126+
sgd = SGD(lr=0.01, decay=1e-6,
127+
momentum=0.9, nesterov=True)
128+
# 采用SGD+momentum的优化器进行训练,首先生成一个优化器对象
129+
self.model.compile(loss='categorical_crossentropy',
130+
optimizer=sgd,
131+
metrics=['accuracy']) # 完成实际的模型配置工作
132+
# 不使用数据提升,所谓的提升就是从我们提供的训练数据中利用旋转、翻转、加噪声等方法创造新的
133+
# 训练数据,有意识的提升训练数据规模,增加模型训练量
134+
if not data_augmentation:
135+
self.model.fit(dataset.train_images,
136+
dataset.train_labels,
137+
batch_size=batch_size,
138+
nb_epoch=nb_epoch,
139+
validation_data=(
140+
dataset.valid_images, dataset.valid_labels
141+
),
142+
shuffle=True)
143+
# 使用实时数据提升
144+
else:
145+
# 定义数据生成器用于数据提升,其返回一个生成器对象datagen,datagen每被调用一
146+
# 次其生成一组数据(顺序生成),节省内存,其实就是python的数据生成器
147+
datagen = ImageDataGenerator(
148+
featurewise_center=False, # 是否使输入数据去中心化(均值为0),
149+
samplewise_center=False, # 是否使输入数据的每个样本均值为0
150+
featurewise_std_normalization=False, # 是否数据标准化(输入数据除以数据集的标准差)
151+
samplewise_std_normalization=False, # 是否将每个样本数据除以自身的标准差
152+
zca_whitening=False, # 是否对输入数据施以ZCA白化
153+
rotation_range=20, # 数据提升时图片随机转动的角度(范围为0~180)
154+
width_shift_range=0.2, # 数据提升时图片水平偏移的幅度(单位为图片宽度的占比,0~1之间的浮点数)
155+
height_shift_range=0.2, # 同上,只不过这里是垂直
156+
horizontal_flip=True, # 是否进行随机水平翻转
157+
vertical_flip=False) # 是否进行随机垂直翻转
158+
# 计算整个训练样本集的数量以用于特征值归一化、ZCA白化等处理
159+
datagen.fit(dataset.train_images)
160+
# 利用生成器开始训练模型
161+
self.model.fit_generator(datagen.flow(
162+
dataset.train_images, dataset.train_labels,
163+
batch_size=batch_size
164+
),
165+
samples_per_epoch=dataset.train_images.shape[0],
166+
nb_epoch=nb_epoch, validation_data=(
167+
dataset.valid_images, dataset.valid_labels
168+
)
169+
)
170+
MODEL_PATH = './my.face.model.h5'
171+
172+
def save_model(self, file_path=MODEL_PATH):
173+
self.model.save(file_path)
174+
175+
def load_model(self, file_path=MODEL_PATH):
176+
self.model = load_model(file_path)
177+
178+
def evaluate(self, dataset):
179+
score = self.model.evaluate(
180+
dataset.test_images, dataset.test_labels, verbose=1
181+
)
182+
print("%s: %.2f%%" % (self.model.metrics_names[1], score[1] * 100))
183+
184+
# 识别人脸
185+
def face_predict(self, image):
186+
# 依然是根据后端系统确定维度顺序
187+
if K.image_data_format() == 'channels_first' and image.shape != (
188+
1, 3, IMAGE_SIZE, IMAGE_SIZE
189+
):
190+
image = resize_image(image)
191+
# 尺寸必须与训练集一致都应该是IMAGE_SIZE x IMAGE_SIZE
192+
image = image.reshape((1, 3, IMAGE_SIZE, IMAGE_SIZE))
193+
# 与模型训练不同,这次只是针对1张图片进行预测
194+
elif K.image_data_format() == 'channels_last' and image.shape != (
195+
1, IMAGE_SIZE, IMAGE_SIZE, 3
196+
):
197+
image = resize_image(image)
198+
image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 3))
199+
200+
# 浮点并归一化
201+
image = image.astype('float32')
202+
image /= 255
203+
# 给出输入属于各个类别的概率,我们是二值类别,则该函数会给出输入图像属于0和1的概率各为多少
204+
result = self.model.predict_proba(image)
205+
# 给出类别预测:0或者1
206+
result = self.model.predict_classes(image)
207+
# 返回类别预测结果
208+
return result[0]
209+
210+
211+
def faceTrainMain(myName):
212+
dataset = Dataset('./faceData/')
213+
dataset.load()
214+
model = Model()
215+
model.build_model(dataset)
216+
model.train(dataset)
217+
model.save_model(file_path='./faceData/' + myName + '.face.model.h5')
218+
model.evaluate(dataset)
219+
220+
221+
if __name__ == '__main__':
222+
dataset = Dataset('./faceData/')
223+
dataset.load()
224+
model = Model()
225+
model.build_model(dataset)
226+
# 先前添加的测试build_model()函数的代码
227+
model.build_model(dataset)
228+
# 测试训练函数的代码
229+
model.train(dataset)
230+
if __name__ == '__main__':
231+
dataset = Dataset('./faceData/')
232+
dataset.load()
233+
model = Model()
234+
model.build_model(dataset)
235+
model.train(dataset)
236+
model.save_model(file_path='./faceData/my.face.model.h5')
237+
238+
if __name__ == '__main__':
239+
dataset = Dataset('./faceData/')
240+
dataset.load()
241+
# 评估模型
242+
model = Model()
243+
model.load_model(file_path='./faceData/my.face.model.h5')
244+
model.evaluate(dataset)

0 commit comments

Comments
 (0)