├── README.md ├── facecnn.rar └── facecnn ├── 2.mp4 ├── data.rar ├── hls.gif ├── model_cnn_train.py ├── saved_models └── keras_face_trained_model.h5 └── video_face_sign.py /README.md: -------------------------------------------------------------------------------- 1 | # ML 2 | 深度学习 3 | 4 | 转载请注明:https://blog.csdn.net/wyx100/article/details/80428424 5 | 6 | 效果展示 7 | 8 | 9 | 10 | 未完待续。。。 11 | 环境配置 12 | 13 | win7sp1 14 | 15 | python 3.6.3 16 | dlib 19.7.0 17 | tensorflow 1.3.0rc0 18 | keras 2.1.5 19 | opencv-python 3.4.1+contrib 20 | pillow 4.2.1 21 | numpy 1.14.1+mkl 22 | numpy 1.12.1 23 | 24 | 软件下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/ 25 | 26 | 项目实现步骤 27 | 28 | 1.整理人脸图片(格式:jpg,150x150) 29 | 30 | 每个人物100张(80张训练(train),20张验证(validation)) 31 | 32 | 完整项目下载 33 | 34 | 为方便没积分童鞋,请加企鹅,共享文件夹。 35 | 36 | 包括:代码、数据集合(图片)、已生成model、安装库文件等。 37 | 38 | data 39 | 40 | train 41 | 42 | fsm 43 | 44 | 0.jpg 45 | 46 | 1.jpg 47 | 48 | 。。。 49 | 50 | 79.jpg 51 | 52 | gje 53 | 54 | qyy 55 | 56 | validation 57 | 58 | fsm 59 | 60 | 80.jpg 61 | 62 | 81.jpg 63 | 64 | 。。。 65 | 66 | 99.jpg 67 | 68 | gje 69 | 70 | qyy 71 | 72 | 2.训练CNN(tensorflow、keras)模型 73 | 74 | 3.基于2训练的model(dlib检测人脸)识别视频某张图片中的人脸,并标记姓名 75 | 76 | 1)打开视频,截取一帧图片 77 | 78 | 2)检测1)中图片的人脸(1张或多张),未检测到人脸则结束本次循环 79 | 80 | 3)基于2训练的model识别图片中的人脸,并标记姓名 81 | 82 | 4)输出框出人脸并标记姓名的图片。 83 | 84 | 代码 85 | 86 | 1. model_cnn_train.py 87 | 88 | 使用卷积神经网络训练人脸识别(不是检测)模型(模型结构见文章末尾) 89 | 90 | 根据keras2.1.6中example(点击下载)下cifar10_cnn.py修改 91 | 92 | 英文文档 93 | 94 | https://keras.io/ 95 | 96 | https://pypi.org/project/Keras/ 97 | 98 | https://pypi.org/project/Keras/#files 99 | 100 | https://github.com/keras-team/keras.git (代码下载) 101 | 102 | 中文文档 103 | 104 | http://keras-cn.readthedocs.io/en/latest/ 105 | 106 | 6个周期可以达到99%的准确率。 107 | 108 | 2. video_face_sign.py 109 | 110 | 使用dlib检测视频中的人脸,调用1中的训练的模型判断对应人(是谁)并标记中文姓名。 111 | 112 | 备注:通过模型(可以使用leNet、vgg16等网络)、样本质量、样本数量、样本多样性调整可优化实际识别效果。 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /facecnn.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbusr/ML/07f4dd156287ccb8b95a8bf76c602d6a36ae0ce6/facecnn.rar -------------------------------------------------------------------------------- /facecnn/2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbusr/ML/07f4dd156287ccb8b95a8bf76c602d6a36ae0ce6/facecnn/2.mp4 -------------------------------------------------------------------------------- /facecnn/data.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbusr/ML/07f4dd156287ccb8b95a8bf76c602d6a36ae0ce6/facecnn/data.rar -------------------------------------------------------------------------------- /facecnn/hls.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbusr/ML/07f4dd156287ccb8b95a8bf76c602d6a36ae0ce6/facecnn/hls.gif -------------------------------------------------------------------------------- /facecnn/model_cnn_train.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | import keras 6 | from keras.datasets import cifar10 7 | from keras.preprocessing.image import ImageDataGenerator 8 | from keras.models import Sequential 9 | from keras.layers import Dense, Dropout, Activation, Flatten 10 | from keras.layers import Conv2D, MaxPooling2D 11 | import os 12 | 13 | batch_size = 32 # 训练时每个批次的样本数 训练样本数/批次样本数 = 批次数(每个周期) 14 | # num_classes = 10 15 | num_classes = 3 # 3类别 16 | # epochs = 100 17 | epochs = 50 # 训练50周期,训练集所有样本(数据、记录)参与训练一次为一个周期 18 | # data_augmentation = True 19 | # num_predictions = 20 20 | save_dir = os.path.join(os.getcwd(), 'saved_models') 21 | model_name = 'keras_face_trained_model.h5' 22 | 23 | img_w = 150 24 | img_h = 150 25 | 26 | # LossHistory类,保存loss和acc 27 | class LossHistory(keras.callbacks.Callback): 28 | def on_train_begin(self, logs={}): 29 | self.losses = {'batch':[], 'epoch':[]} 30 | self.accuracy = {'batch':[], 'epoch':[]} 31 | self.val_loss = {'batch':[], 'epoch':[]} 32 | self.val_acc = {'batch':[], 'epoch':[]} 33 | 34 | def on_batch_end(self, batch, logs={}): 35 | self.losses['batch'].append(logs.get('loss')) 36 | self.accuracy['batch'].append(logs.get('acc')) 37 | self.val_loss['batch'].append(logs.get('val_loss')) 38 | self.val_acc['batch'].append(logs.get('val_acc')) 39 | 40 | def on_epoch_end(self, batch, logs={}): 41 | self.losses['epoch'].append(logs.get('loss')) 42 | self.accuracy['epoch'].append(logs.get('acc')) 43 | self.val_loss['epoch'].append(logs.get('val_loss')) 44 | self.val_acc['epoch'].append(logs.get('val_acc')) 45 | 46 | def loss_plot(self, loss_type): 47 | iters = range(len(self.losses[loss_type])) 48 | plt.figure() 49 | # acc 50 | plt.plot(iters, self.accuracy[loss_type], 'r', label='train acc') 51 | # loss 52 | plt.plot(iters, self.losses[loss_type], 'g', label='train loss') 53 | if loss_type == 'epoch': 54 | # val_acc 55 | plt.plot(iters, self.val_acc[loss_type], 'b', label='val acc') 56 | # val_loss 57 | plt.plot(iters, self.val_loss[loss_type], 'k', label='val loss') 58 | plt.grid(True) 59 | plt.xlabel(loss_type) 60 | plt.ylabel('acc-loss') 61 | plt.legend(loc="upper right") 62 | plt.show() 63 | 64 | ''' 65 | # The data, shuffled and split between train and test sets: 66 | (x_train, y_train), (x_test, y_test) = cifar10.load_data() 67 | print('x_train shape:', x_train.shape) 68 | print(x_train.shape[0], 'train samples') 69 | print(x_test.shape[0], 'test samples') 70 | 71 | # Convert class vectors to binary class matrices. 72 | y_train = keras.utils.to_categorical(y_train, num_classes) 73 | y_test = keras.utils.to_categorical(y_test, num_classes) 74 | ''' 75 | 76 | model = Sequential() 77 | model.add(Conv2D(32, (3, 3), padding='same', 78 | # input_shape=x_train.shape[1:])) 79 | input_shape=(150, 150, 3))) # 输入数据是图片转换的矩阵格式,150(行)x 150(列) x 3 (通道)(每个像素点3个单位,分别表示RGB(红绿蓝)) 80 | model.add(Activation('relu')) 81 | model.add(Conv2D(32, (3, 3))) 82 | model.add(Activation('relu')) 83 | model.add(MaxPooling2D(pool_size=(2, 2))) 84 | model.add(Dropout(0.25)) 85 | 86 | model.add(Conv2D(64, (3, 3), padding='same')) 87 | model.add(Activation('relu')) 88 | model.add(Conv2D(64, (3, 3))) 89 | model.add(Activation('relu')) 90 | model.add(MaxPooling2D(pool_size=(2, 2))) 91 | model.add(Dropout(0.25)) 92 | 93 | model.add(Flatten()) 94 | model.add(Dense(512)) 95 | model.add(Activation('relu')) 96 | model.add(Dropout(0.5)) 97 | model.add(Dense(num_classes)) 98 | model.add(Activation('softmax')) 99 | 100 | # initiate RMSprop optimizer 101 | opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6) 102 | 103 | # Let's train the model using RMSprop 104 | model.compile(loss='categorical_crossentropy', 105 | optimizer=opt, 106 | metrics=['accuracy']) 107 | 108 | # x_train = x_train.astype('float32') 109 | # x_test = x_test.astype('float32') 110 | # x_train /= 255 111 | # x_test /= 255 112 | 113 | # 创建history实例 114 | history = LossHistory() 115 | ''' 116 | if not data_augmentation: 117 | print('Not using data augmentation.') 118 | model.fit(x_train, y_train, 119 | batch_size=batch_size, 120 | epochs=epochs, 121 | validation_data=(x_test, y_test), 122 | shuffle=True) 123 | else: 124 | print('Using real-time data augmentation.') 125 | # This will do preprocessing and realtime data augmentation: 126 | datagen = ImageDataGenerator( 127 | featurewise_center=False, # set input mean to 0 over the dataset 128 | samplewise_center=False, # set each sample mean to 0 129 | featurewise_std_normalization=False, # divide inputs by std of the dataset 130 | samplewise_std_normalization=False, # divide each input by its std 131 | zca_whitening=False, # apply ZCA whitening 132 | rotation_range=0, # randomly rotate images in the range (degrees, 0 to 180) 133 | width_shift_range=0.1, # randomly shift images horizontally (fraction of total width) 134 | height_shift_range=0.1, # randomly shift images vertically (fraction of total height) 135 | horizontal_flip=True, # randomly flip images 136 | vertical_flip=False) # randomly flip images 137 | 138 | # Compute quantities required for feature-wise normalization 139 | # (std, mean, and principal components if ZCA whitening is applied). 140 | datagen.fit(x_train) 141 | 142 | # Fit the model on the batches generated by datagen.flow(). 143 | model.fit_generator(datagen.flow(x_train, y_train, 144 | batch_size=batch_size), 145 | epochs=epochs, 146 | validation_data=(x_test, y_test), 147 | workers=4) 148 | ''' 149 | train_datagen = ImageDataGenerator( 150 | rescale=1. / 255, 151 | shear_range=0.2, 152 | zoom_range=0.2, 153 | horizontal_flip=True) 154 | 155 | test_datagen = ImageDataGenerator(rescale=1. / 255) 156 | 157 | # 训练样本初始化处理:长宽调整,批次大小调整,数据打乱排序(shuffle=True),分类区分(binary:2分类、categorical:多分类) 158 | train_generator = train_datagen.flow_from_directory( 159 | './data/train', # 本例,提供80 x 3 = 240 个训练样本 160 | target_size=(img_w, img_h), # 图片格式调整为 150x150 161 | batch_size=batch_size, 162 | shuffle=True, 163 | class_mode='categorical') # matt,多分类 164 | 165 | validation_generator = test_datagen.flow_from_directory( 166 | './data/validation',# 本例,提供20 x 3 = 60 个验证样本 167 | target_size=(img_w, img_h), 168 | batch_size=batch_size, 169 | shuffle=True, 170 | class_mode='categorical') # matt,多分类 171 | 172 | # 模型适配生成 173 | model.fit_generator( 174 | train_generator, # 训练集 175 | samples_per_epoch=2400, # 训练集总样本数,如果提供样本数量不够,则调整图片(翻转、平移等)补足数量(本例,该函数补充2400-240个样本) 176 | nb_epoch=epochs, 177 | validation_data=validation_generator, # 验证集 178 | nb_val_samples=800, # 验证集总样本数,如果提供样本数量不够,则调整图片(翻转、平移等)补足数量(本例,该函数补充800-60个样本) 179 | callbacks=[history]) # 回调函数,绘制批次(epoch)和精确度(acc)关系图表函数 180 | 181 | # Save model and weights 182 | if not os.path.isdir(save_dir): # 没有save_dir对应目录则建立 183 | os.makedirs(save_dir) 184 | model_path = os.path.join(save_dir, model_name) 185 | model.save(model_path) 186 | print('Saved trained model at %s ' % model_path) 187 | 188 | # 显示批次(epoch)和精确度(acc)关系图表 189 | history.loss_plot('epoch') 190 | 191 | # 模型结构图 192 | from keras.utils import plot_model 193 | plot_model(model, to_file='model.png', show_shapes=True) 194 | 195 | # Score trained model. 196 | # scores = model.evaluate(x_test, y_test, verbose=1) 197 | # print('Test loss:', scores[0]) 198 | # print('Test accuracy:', scores[1]) 199 | 200 | ''' 201 | 202 | ''' -------------------------------------------------------------------------------- /facecnn/saved_models/keras_face_trained_model.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbusr/ML/07f4dd156287ccb8b95a8bf76c602d6a36ae0ce6/facecnn/saved_models/keras_face_trained_model.h5 -------------------------------------------------------------------------------- /facecnn/video_face_sign.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import numpy as np 5 | import sys 6 | import time 7 | import cv2 8 | import dlib 9 | 10 | from keras.preprocessing import image as imagekeras 11 | from keras.models import load_model 12 | from PIL import Image, ImageDraw, ImageFont 13 | 14 | size = 150 15 | save_dir = os.path.join(os.getcwd(), 'saved_models') 16 | model_name = 'keras_face_trained_model.h5' 17 | 18 | # 类别编码转换为中文名称返回 19 | def return_name(codelist): 20 | names = ['樊胜美', '关雎尔', '邱莹莹'] 21 | for it in range(0, len(codelist), 1): 22 | if int(codelist[it]) == 1.0: 23 | return names[it] 24 | 25 | # 类别编码转换为英文名称返回 26 | def return_name_en(codelist): 27 | names = ['fsm', 'gje', 'qyy'] 28 | for it in range(0, len(codelist), 1): 29 | if int(codelist[it]) == 1.0: 30 | return names[it] 31 | 32 | # 区分和标记视频中截图的人脸 33 | def face_rec(): 34 | global image_ouput 35 | model = load_model(os.path.join(save_dir, model_name)) 36 | camera = cv2.VideoCapture("2.mp4") # 视频 37 | # camera = cv2.VideoCapture(0) # 摄像头 38 | 39 | while (True): 40 | read, img = camera.read() 41 | try: 42 | # 未截取视频图片结束本次循环 43 | if not (type(img) is np.ndarray): 44 | continue 45 | gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 图片转为灰度图 46 | except: 47 | print("Unexpected error:", sys.exc_info()[0]) 48 | break 49 | 50 | # 使用detector进行人脸检测 51 | # 使用dlib自带的frontal_face_detector作为我们的特征提取器 52 | detector = dlib.get_frontal_face_detector() 53 | dets = detector(gray_img, 1) # 提取截图中所有人脸 54 | 55 | facelist = [] 56 | for i, d in enumerate(dets): # 依次区分截图中的人脸 57 | x1 = d.top() if d.top() > 0 else 0 58 | y1 = d.bottom() if d.bottom() > 0 else 0 59 | x2 = d.left() if d.left() > 0 else 0 60 | y2 = d.right() if d.right() > 0 else 0 61 | 62 | img = cv2.rectangle(img, (x2, x1), (y2, y1), (255, 0, 0), 2) # 人脸画框 63 | 64 | face = img[x1:y1, x2:y2] 65 | face = cv2.resize(face, (size, size)) 66 | 67 | x_input = np.expand_dims(face, axis=0) 68 | prey = model.predict(x_input) 69 | print(prey, 'prey') 70 | 71 | facelist.append([d, return_name(prey[0])]) # 存储一张图中多张人脸坐标和标记(姓名) 72 | 73 | cv2_im = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # cv2和PIL中颜色的hex码的储存顺序不同 74 | pil_im = Image.fromarray(cv2_im) 75 | 76 | draw = ImageDraw.Draw(pil_im) # 括号中为需要打印的cqanvas,这里就是在图片上直接打印 77 | font = ImageFont.truetype("simhei.ttf", 20, encoding="utf-8") # 第一个参数为字体文件路径,第二个为字体大小 78 | 79 | try: 80 | for i in facelist: 81 | # 人脸标记写入图片,第一个参数为打印的坐标,第二个为打印的文本,第三个为字体颜色,第四个为字体 82 | draw.text((i[0].left() + int((i[0].right() - i[0].left()) / 2 - len(i[1]) * 10), i[0].top() - 20), i[1], 83 | (255, 0, 0), font=font) 84 | except: 85 | print("Unexpected error:", sys.exc_info()[0]) 86 | continue 87 | 88 | # PIL图片转换为cv2图片 89 | cv2_char_img = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR) 90 | # 显示图片 91 | cv2.imshow("camera", cv2_char_img) 92 | if cv2.waitKey(1) & 0xff == ord("q"): 93 | break 94 | cv2.destroyAllWindows() 95 | 96 | if __name__ == "__main__": 97 | face_rec() 98 | --------------------------------------------------------------------------------