tensorflow实现人脸检测及识别(简单版)

7977
3

大哥很IT 数据老手Lv5

发表于2018-5-29 10:15

楼主
本教程主要是对人脸检测及识别python实现系列碉堡了!程序员用深度学习写了个老板探测器(付源码) 的实现。主要实现的功能是用网络摄像头自动识别在工位通道走过的人脸,如果确认是老板的话,就用一张图片覆盖到整个屏幕上。虽然原教程已经写的很好,但是我们在实现的时候仍然踩了很多坑。本着让后来者少走点弯路的原则,我们特将最详细的步骤记录如下,以期读者朋友只花最少的时间便能体验用tensorflow实现人脸检测及识别。硬件要求:一台电脑和一个普通的USB摄像头(或电脑自带摄像头),U盘(用来制作Ubuntu安装盘)。

环境搭建:

1.Ubuntu 16.04

2.python 3.5 +

3.tensorflow-gpu 1.2 或着cpu版本的也可以的

4.opencv :pip3 install opencv-python

5.安装keras、sklearn等 :pip3 install --user scikit-learn



获取并显示摄像头视频

检查摄像头是否正常工作


  • 安装cheese:<sudo apt-get install cheese >

  • shell输入cheese,如果弹出摄像头窗口,说明摄像头正常。
  • 检查摄像头id:<ls  /dev/video*>,一般id为0。
利用OpenCV获取摄像头的视频流并展示
  • 创建名为catch_usb_video.py的python文件,将下列代码copy进去,保存,退出。



[python] view plain copy



  • import cv2
  • import sys
  • from PIL import Image
  • def CatchUsbVideo(window_name, camera_idx):
  •     cv2.namedWindow(window_name)
  •     #视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
  •     cap = cv2.VideoCapture(camera_idx)
  •     while cap.isOpened():
  •         ok, frame = cap.read() #读取一帧数据
  •         if not ok:
  •             break
  •         #显示图像并等待10毫秒按键输入,输入‘q’退出程序
  •         cv2.imshow(window_name, frame)
  •         c = cv2.waitKey(10)
  •         if c & 0xFF == ord('q'):
  •             break
  •     #释放摄像头并销毁所有窗口
  •     cap.release()
  •     cv2.destroyAllWindows()
  • if __name__ == '__main__':
  •     if len(sys.argv) != 2:
  •         print("Usage:%s camera_id\r\n" % (sys.argv[0]))
  •     else:
  •         CatchUsbVideo("截取视频流", int(sys.argv[1]))

  • 在command窗口输入:<python3 catch_usb_video.py 0>运行上述程序。
  • 按q关闭Capturing Video窗口

识别出人脸
  • 创建名为recognise_face.py的文件夹,存入下述代码:

[python] view plain copy



  • #-*- coding: utf-8 -*-
  • import cv2
  • import sys
  • from PIL import Image
  • def CatchUsbVideo(window_name, camera_idx):
  •     cv2.namedWindow(window_name)
  •     #视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
  •     cap = cv2.VideoCapture(camera_idx)
  •     #告诉OpenCV使用人脸识别分类器
  •     classfier = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml")
  •     #识别出人脸后要画的边框的颜色,RGB格式
  •     color = (0, 255, 0)
  •     while cap.isOpened():
  •         ok, frame = cap.read() #读取一帧数据
  •         if not ok:
  •             break
  •         #将当前帧转换成灰度图像
  •         grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  •         #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
  •         faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
  •         if len(faceRects) > 0:            #大于0则检测到人脸
  •             for faceRect in faceRects:  #单独框出每一张人脸
  •                 x, y, w, h = faceRect
  •                 cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
  •         #显示图像
  •         cv2.imshow(window_name, frame)
  •         c = cv2.waitKey(10)
  •         if c & 0xFF == ord('q'):
  •             break
  •     #释放摄像头并销毁所有窗口
  •     cap.release()
  •     cv2.destroyAllWindows()
  • if __name__ == '__main__':
  •     # if len(sys.argv) != 2:
  •     #    print("Usage:%s camera_id\r\n" % (sys.argv[0]))
  •     #else:
  •      #   CatchUsbVideo("Recognising Face", int(sys.argv[1]))
  •     CatchUsbVideo("Recognising Face", 0 )


运行上述代码,跳出的视频窗口中,人脸区域会被方框圈起来。

关于代码有几个地方需要重点交代,首先就是人脸分类器这行:

[python] view plain copy



  • #告诉OpenCV使用人脸识别分类器
  • classfier = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml")

这行代码指定OpenCV选择使用哪种分类器(注意,一定习惯分类这个说法,ML的监督学习研究的就是各种分类问题),OpenCV提供了多种分类器:



上图为我的电脑上安装的OpenCV3.2提供的所有分类器,有识别眼睛的(甚至包括左右眼),有识别身体的,有识别笑脸的,甚至还有识别猫脸的,有兴趣的可以逐个试试。关于人脸识别,OpenCV提供多个分类器选择使用,其中haarcascade_frontalface_alt_tree.xml是最严格的分类器,光线、带个帽子都有可能识别不出人脸。其它的稍微好点,default那个识别最宽松,某些情况下我家里的灯笼都会被识别成人脸;)。另外安装环境不同,分类器的安装路径也有可能不同,请在安装完OpenCV后根据分类器的实际安装路径修改代码。另外再多说一句,如果我们想构建自己的分类器,比如检测火焰(火灾报警)、汽车(确定路口汽车数量),我们依然可以使用OpenCV训练构建,详细说明参见OpenCV的官方文档。

接下来解释如下几行代码:

[python] view plain copy



  • #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
  • faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
  • if len(faceRects) > 0:            #大于0则检测到人脸
  •    for faceRect in faceRects:  #单独框出每一张人脸
  •        x, y, w, h = faceRect
  •        cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)


其中classfier.detectMultiScale()即是完成实际人脸识别工作的函数,该函数参数说明如下:

grey:要识别的图像数据(即使不转换成灰度也能识别,但是灰度图可以降低计算强度,因为检测的依据是哈尔特征,转换后每个点的RGB数据变成了一维的灰度,这样计算强度就减少很多)

scaleFactor:图像缩放比例,可以理解为同一个物体与相机距离不同,其大小亦不同,必须将其缩放到一定大小才方便识别,该参数指定每次缩放的比例

minNeighbors:对特征检测点周边多少有效点同时检测,这样可避免因选取的特征检测点太小而导致遗漏

minSize:特征检测点的最小值


对同一个画面有可能出现多张人脸,因此,我们需要用一个for循环将所有检测到的人脸都读取出来,然后逐个用矩形框框出来,这就是接下来的for语句的作用。Opencv会给出每张人脸在图像中的起始坐标(左上角,x、y)以及长、宽(h、w),我们据此就可以截取出人脸。其中,cv2.rectangle()完成画框的工作,在这里我有意识的外扩了10个像素以框出比人脸稍大一点的区域。cv2.rectangle()函数的最后两个参数一个用于指定矩形边框的颜色,一个用于指定矩形边框线条的粗细程度。



为模型训练准备人脸数据

这部分采集两个人脸数据,分别存到data/me和data/other 文件夹里。
  * 在主目录下,创建data文件夹,data文件夹里创建me和other文件夹爱。
  * 在主目录下,创建face_data.py文件,存入下述代码

[python] view plain copy



  • #-*- coding: utf-8 -*-
  • import cv2
  • import sys
  • from PIL import Image
  • def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):
  •     cv2.namedWindow(window_name)
  •     #视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
  •     cap = cv2.VideoCapture(camera_idx)
  •     #告诉OpenCV使用人脸识别分类器
  •     classfier = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml")
  •     #识别出人脸后要画的边框的颜色,RGB格式
  •     color = (0, 255, 0)
  •     num = 0
  •     while cap.isOpened():
  •         ok, frame = cap.read() #读取一帧数据
  •         if not ok:
  •             break
  •         grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  #将当前桢图像转换成灰度图像
  •         #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
  •         faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
  •         if len(faceRects) > 0:          #大于0则检测到人脸
  •             for faceRect in faceRects:  #单独框出每一张人脸
  •                 x, y, w, h = faceRect
  •                 #将当前帧保存为图片
  •                 img_name = '%s/%d.jpg'%(path_name, num)
  •                 image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
  •                 cv2.imwrite(img_name, image)
  •                 num += 1
  •                 if num > (catch_pic_num):   #如果超过指定最大保存数量退出循环
  •                     break
  •                 #画出矩形框
  •                 cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
  •                 #显示当前捕捉到了多少人脸图片了,这样站在那里被拍摄时心里有个数,不用两眼一抹黑傻等着
  •                 font = cv2.FONT_HERSHEY_SIMPLEX
  •                 cv2.putText(frame,'num:%d' % (num),(x + 30, y + 30), font, 1, (255,0,255),4)
  •         #超过指定最大保存数量结束程序
  •         if num > (catch_pic_num):
  •             break
  •         #显示图像
  •         cv2.imshow(window_name, frame)
  •         c = cv2.waitKey(10)
  •         if c & 0xFF == ord('q'):
  •             break
  •     #释放摄像头并销毁所有窗口
  •     cap.release()
  •     cv2.destroyAllWindows()
  • if __name__ == '__main__':
  •     if len(sys.argv) != 4:
  •         print("Usage:%s camera_id face_num_max path_name\r\n" % (sys.argv[0]))
  •     else:
  •         CatchPICFromVideo("Collecting face data", int(sys.argv[1]), int(sys.argv[2]), sys.argv[3])
  •     '''''
  •     在shell先后运行:<python face_data.py 0 1000 data/me> 和
  •     <python face_data.py 0 1000 data/other>分别采集两个人各1000个人脸数据存入相应文件夹。
  •     '''


  • 手动剔除data文件夹中不是图片文件的数据。
OpenCV对人脸的识别也不是100%准确,因此,我们截取的人脸图像中会有些不合格的,比如误把灯笼当人脸存下来了或者人脸图像很模糊。在我截取的1000张人脸中大约有几十张这样的,要想确保模型可靠,必须要把这样的图片去掉。这个活只能手动了,没办法。幸运的是,数据量不大,不会耽误太多时间的。最后,请确保程序所在路径下已经有了data/me和data/other两个文件夹及各1000张人脸图片。



利用keras库训练人脸识别模型

利用keras库来建立模型和评估模型
  * 主目录下建立model文件夹:<mkdir model>

首先我们建立一个空白的python文件,文件名为:load_face_dataset.py,代码如下:


[python] view plain copy



  • # -*- coding: utf-8 -*-
  • # load_face_dataset.py
  • import os
  • import sys
  • import numpy as np
  • import cv2
  • IMAGE_SIZE = 64
  • #按照指定图像大小调整尺寸
  • def resize_image(image, height = IMAGE_SIZE, width = IMAGE_SIZE):
  •     top, bottom, left, right = (0, 0, 0, 0)
  •     #获取图像尺寸
  •     h, w, _ = image.shape
  •     #对于长宽不相等的图片,找到最长的一边
  •     longest_edge = max(h, w)
  •     #计算短边需要增加多上像素宽度使其与长边等长
  •     if h < longest_edge:
  •         dh = longest_edge - h
  •         top = dh // 2
  •         bottom = dh - top
  •     elif w < longest_edge:
  •         dw = longest_edge - w
  •         left = dw // 2
  •         right = dw - left
  •     else:
  •         pass
  •     #RGB颜色
  •     BLACK = [0, 0, 0]
  •     #给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定
  •     constant = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value = BLACK)
  •     #调整图像大小并返回
  •     return cv2.resize(constant, (height, width))
  • #读取训练数据
  • images = []
  • labels = []
  • def read_path(path_name):
  •     for dir_item in os.listdir(path_name):
  •         #从初始路径开始叠加,合并成可识别的操作路径
  •         full_path = os.path.abspath(os.path.join(path_name, dir_item))
  •         if os.path.isdir(full_path):    #如果是文件夹,继续递归调用
  •             read_path(full_path)
  •         else:   #文件
  •             if dir_item.endswith('.jpg'):
  •                 image = cv2.imread(full_path)
  •                 image = resize_image(image, IMAGE_SIZE, IMAGE_SIZE)
  •                 #放开这个代码,可以看到resize_image()函数的实际调用效果
  •                 #cv2.imwrite('1.jpg', image)
  •                 images.append(image)
  •                 labels.append(path_name)
  •     return images,labels
  • #从指定路径读取训练数据
  • def load_dataset(path_name):
  •     images,labels = read_path(path_name)
  •     #将输入的所有图片转成四维数组,尺寸为(图片数量*IMAGE_SIZE*IMAGE_SIZE*3)
  •     #我和闺女两个人共1200张图片,IMAGE_SIZE为64,故对我来说尺寸为1200 * 64 * 64 * 3
  •     #图片为64 * 64像素,一个像素3个颜色值(RGB)
  •     images = np.array(images)
  •     print(images.shape)
  •     #标注数据,'me'文件夹下都是我的脸部图像,全部指定为0,另外一个文件夹下是闺女的,全部指定为1
  •     labels = np.array([0 if label.endswith('me') else 1 for label in labels])
  •     return images, labels
  • if __name__ == '__main__':
  •     if len(sys.argv) != 2:
  •         print("Usage:%s path_name\r\n" % (sys.argv[0]))
  •     else:
  •         images, labels = load_dataset(sys.argv[1])


resize_image()函数。这个函数其实就做了一件事情,判断图片是不是四边等长,也就是图片是不是正方形。如果不是,则短的那两边增加两条黑色的边框,使图像变成正方形,这样再调用cv2.resize()函数就可以实现等比例缩放了。因为我们指定缩放的比例就是64 x 64,只有缩放之前图像为正方形才能确保图像不失真。resize_image()函数的执行结果如下所示:


上图为200 x 300的图片,宽度小于高度,因此,需要增加宽度,正常应该是两边各增加宽50像素的黑边:




如我们所愿,成了一个300 x 300的正方形图片,这时我们再缩放到64 x 64就可以了:

上图就是我们将要输入到CNN中的图片,之所以缩放到这么小,主要是为了减少计算量及内存占用,提升训练速度。执行程序之前,请把图片组织一下,结构参见下图:



  * 主目录下建立face_train_use_keras.py文件,存入下述代码:

[python] view plain copy



  • #-*- coding: utf-8 -*-
  • import random
  • import numpy as np
  • from sklearn.cross_validation import train_test_split
  • from keras.preprocessing.image import ImageDataGenerator
  • from keras.models import Sequential
  • from keras.layers import Dense, Dropout, Activation, Flatten
  • from keras.layers import Convolution2D, MaxPooling2D
  • from keras.optimizers import SGD
  • from keras.utils import np_utils
  • from keras.models import load_model
  • from keras import backend as K
  • from load_face_dataset import load_dataset, resize_image, IMAGE_SIZE
  • class Dataset:
  •     def __init__(self, path_name):
  •         #训练集
  •         self.train_images = None
  •         self.train_labels = None
  •         #验证集
  •         self.valid_images = None
  •         self.valid_labels = None
  •         #测试集
  •         self.test_images  = None
  •         self.test_labels  = None
  •         #数据集加载路径
  •         self.path_name    = path_name
  •         #当前库采用的维度顺序
  •         self.input_shape = None
  •     #加载数据集并按照交叉验证的原则划分数据集并进行相关预处理工作
  •     def load(self, img_rows = IMAGE_SIZE, img_cols = IMAGE_SIZE,
  •              img_channels = 3, nb_classes = 2):
  •         #加载数据集到内存
  •         images, labels = load_dataset(self.path_name)
  •         train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size = 0.3, random_state = random.randint(0, 100))
  •         _, test_images, _, test_labels = train_test_split(images, labels, test_size = 0.5, random_state = random.randint(0, 100))
  •         #当前的维度顺序如果为'th',则输入图片数据时的顺序为:channels,rows,cols,否则:rows,cols,channels
  •         #这部分代码就是根据keras库要求的维度顺序重组训练数据集
  •         if K.image_dim_ordering() == 'th':
  •             train_images = train_images.reshape(train_images.shape[0], img_channels, img_rows, img_cols)
  •             valid_images = valid_images.reshape(valid_images.shape[0], img_channels, img_rows, img_cols)
  •             test_images = test_images.reshape(test_images.shape[0], img_channels, img_rows, img_cols)
  •             self.input_shape = (img_channels, img_rows, img_cols)
  •         else:
  •             train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, img_channels)
  •             valid_images = valid_images.reshape(valid_images.shape[0], img_rows, img_cols, img_channels)
  •             test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, img_channels)
  •             self.input_shape = (img_rows, img_cols, img_channels)
  •             #输出训练集、验证集、测试集的数量
  •             print(train_images.shape[0], 'train samples')
  •             print(valid_images.shape[0], 'valid samples')
  •             print(test_images.shape[0], 'test samples')
  •             #我们的模型使用categorical_crossentropy作为损失函数,因此需要根据类别数量nb_classes将
  •             #类别标签进行one-hot编码使其向量化,在这里我们的类别只有两种,经过转化后标签数据变为二维
  •             train_labels = np_utils.to_categorical(train_labels, nb_classes)
  •             valid_labels = np_utils.to_categorical(valid_labels, nb_classes)
  •             test_labels = np_utils.to_categorical(test_labels, nb_classes)
  •             #像素数据浮点化以便归一化
  •             train_images = train_images.astype('float32')
  •             valid_images = valid_images.astype('float32')
  •             test_images = test_images.astype('float32')
  •             #将其归一化,图像的各像素值归一化到0~1区间
  •             train_images /= 255
  •             valid_images /= 255
  •             test_images /= 255
  •             self.train_images = train_images
  •             self.valid_images = valid_images
  •             self.test_images  = test_images
  •             self.train_labels = train_labels
  •             self.valid_labels = valid_labels
  •             self.test_labels  = test_labels
  •             #CNN网络模型类
  • class Model:
  •     def __init__(self):
  •         self.model = None
  •     #建立模型
  •     def build_model(self, dataset, nb_classes = 2):
  •         #构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型
  •         self.model = Sequential()
  •         #以下代码将顺序添加CNN网络需要的各层,一个add就是一个网络层
  •         self.model.add(Convolution2D(32, 3, 3, border_mode='same',
  •                                      input_shape = dataset.input_shape))    #1 2维卷积层
  •         self.model.add(Activation('relu'))                                  #2 激活函数层
  •         self.model.add(Convolution2D(32, 3, 3))                             #3 2维卷积层
  •         self.model.add(Activation('relu'))                                  #4 激活函数层
  •         self.model.add(MaxPooling2D(pool_size=(2, 2)))                      #5 池化层
  •         self.model.add(Dropout(0.25))                                       #6 Dropout层
  •         self.model.add(Convolution2D(64, 3, 3, border_mode='same'))         #7  2维卷积层
  •         self.model.add(Activation('relu'))                                  #8  激活函数层
  •         self.model.add(Convolution2D(64, 3, 3))                             #9  2维卷积层
  •         self.model.add(Activation('relu'))                                  #10 激活函数层
  •         self.model.add(MaxPooling2D(pool_size=(2, 2)))                      #11 池化层
  •         self.model.add(Dropout(0.25))                                       #12 Dropout层
  •         self.model.add(Flatten())                                           #13 Flatten层
  •         self.model.add(Dense(512))                                          #14 Dense层,又被称作全连接层
  •         self.model.add(Activation('relu'))                                  #15 激活函数层
  •         self.model.add(Dropout(0.5))                                        #16 Dropout层
  •         self.model.add(Dense(nb_classes))                                   #17 Dense层
  •         self.model.add(Activation('softmax'))                               #18 分类层,输出最终结果
  •         #输出模型概况
  •         self.model.summary()
  • #训练模型
  •     def train(self, dataset, batch_size = 20, nb_epoch = 10, data_augmentation = True):
  •         sgd = SGD(lr = 0.01, decay = 1e-6,
  •                   momentum = 0.9, nesterov = True) #采用SGD+momentum的优化器进行训练,首先生成一个优化器对象
  •         self.model.compile(loss='categorical_crossentropy',
  •                            optimizer=sgd,
  •                            metrics=['accuracy'])   #完成实际的模型配置工作
  •         #不使用数据提升,所谓的提升就是从我们提供的训练数据中利用旋转、翻转、加噪声等方法创造新的
  •         #训练数据,有意识的提升训练数据规模,增加模型训练量
  •         if not data_augmentation:
  •             self.model.fit(dataset.train_images,
  •                            dataset.train_labels,
  •                            batch_size = batch_size,
  •                            nb_epoch = nb_epoch,
  •                            validation_data = (dataset.valid_images, dataset.valid_labels),
  •                            shuffle = True)
  •         #使用实时数据提升
  •         else:
  •             #定义数据生成器用于数据提升,其返回一个生成器对象datagen,datagen每被调用一
  •             #次其生成一组数据(顺序生成),节省内存,其实就是python的数据生成器
  •             datagen = ImageDataGenerator(
  •                 featurewise_center = False,             #是否使输入数据去中心化(均值为0),
  •                 samplewise_center  = False,             #是否使输入数据的每个样本均值为0
  •                 featurewise_std_normalization = False,  #是否数据标准化(输入数据除以数据集的标准差)
  •                 samplewise_std_normalization  = False,  #是否将每个样本数据除以自身的标准差
  •                 zca_whitening = False,                  #是否对输入数据施以ZCA白化
  •                 rotation_range = 20,                    #数据提升时图片随机转动的角度(范围为0~180)
  •                 width_shift_range  = 0.2,               #数据提升时图片水平偏移的幅度(单位为图片宽度的占比,0~1之间的浮点数)
  •                 height_shift_range = 0.2,               #同上,只不过这里是垂直
  •                 horizontal_flip = True,                 #是否进行随机水平翻转
  •                 vertical_flip = False)                  #是否进行随机垂直翻转
  •             #计算整个训练样本集的数量以用于特征值归一化、ZCA白化等处理
  •             datagen.fit(dataset.train_images)
  •             #利用生成器开始训练模型
  •             self.model.fit_generator(datagen.flow(dataset.train_images, dataset.train_labels,
  •                                                    batch_size = batch_size),
  •                                      samples_per_epoch = dataset.train_images.shape[0],
  •                                      nb_epoch = nb_epoch,
  •                                      validation_data = (dataset.valid_images, dataset.valid_labels))
  •     MODEL_PATH = './me.face.model.h5'
  •     def save_model(self, file_path = MODEL_PATH):
  •         self.model.save(file_path)
  •     def load_model(self, file_path = MODEL_PATH):
  •         self.model = load_model(file_path)
  •     def evaluate(self, dataset):
  •         score = self.model.evaluate(dataset.test_images, dataset.test_labels, verbose = 1)
  •         print("%s: %.2f%%" % (self.model.metrics_names[1], score[1] * 100))
  • #识别人脸
  •     def face_predict(self, image):
  •         #依然是根据后端系统确定维度顺序
  •         if K.image_dim_ordering() == 'th' and image.shape != (1, 3, IMAGE_SIZE, IMAGE_SIZE):
  •             image = resize_image(image)                             #尺寸必须与训练集一致都应该是IMAGE_SIZE x IMAGE_SIZE
  •             image = image.reshape((1, 3, IMAGE_SIZE, IMAGE_SIZE))   #与模型训练不同,这次只是针对1张图片进行预测
  •         elif K.image_dim_ordering() == 'tf' and image.shape != (1, IMAGE_SIZE, IMAGE_SIZE, 3):
  •             image = resize_image(image)
  •             image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 3))
  •         #浮点并归一化
  •         image = image.astype('float32')
  •         image /= 255
  •         #给出输入属于各个类别的概率,我们是二值类别,则该函数会给出输入图像属于0和1的概率各为多少
  •         result = self.model.predict_proba(image)
  •         print('result:', result)
  •         #给出类别预测:0或者1
  •         result = self.model.predict_classes(image)
  •         #返回类别预测结果
  •         return result[0]
  • if __name__ == '__main__':
  •     dataset = Dataset('./data/')
  •     dataset.load()
  •     #训练模型
  •     model = Model()
  •     model.build_model(dataset)
  •     model.train(dataset)
  •     model.save_model(file_path = './model/me.face.model.h5')

[python] view plain copy







  • 在shell运行:<python face_train_use_keras.py>保存模型,可以看到训练误差(loss)、训练准确率(acc)、验证误差(val_loass)、验证准确率(val_acc)。

  • 修改face_train_use_keras.py,将训练模型注释掉,添加评估模型,如下:

    '''
    #训练模型,注释掉
    model = Model()
    model.build_model(dataset)
    model.train(dataset)
    model.save_model(file_path = './model/me.face.model.h5')
    '''
    #评估模型
    model = Model()
    model.load_model(file_path = './model/me.face.model.h5')
    model.evaluate(dataset)

  • 重新运行<python face_train_use_keras.py> 可以看到准确率。
从实时视频流识别出自己
  • 主目录下创建face_predict_use_keras.py文件,存入下述代码:

[python] view plain copy



  • #-*- coding: utf-8 -*-
  • import cv2
  • import sys
  • import gc
  • from face_train_use_keras import Model
  • if __name__ == '__main__':
  •     if len(sys.argv) != 2:
  •         print("Usage:%s camera_id\r\n" % (sys.argv[0]))
  •         sys.exit(0)
  •     #加载模型
  •     model = Model()
  •     model.load_model(file_path = './model/me.face.model.h5')
  •     #框住人脸的矩形边框颜色
  •     color = (0, 255, 0)
  •     #捕获指定摄像头的实时视频流
  •     cap = cv2.VideoCapture(int(sys.argv[1]))
  •     #人脸识别分类器本地存储路径
  •     cascade_path = "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml"
  •     #循环检测识别人脸
  •     while True:
  •         _, frame = cap.read()   #读取一帧视频
  •         #图像灰化,降低计算复杂度
  •         frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  •         #使用人脸识别分类器,读入分类器
  •         cascade = cv2.CascadeClassifier(cascade_path)
  •         #利用分类器识别出哪个区域为人脸
  •         faceRects = cascade.detectMultiScale(frame_gray, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
  •         if len(faceRects) > 0:
  •             for faceRect in faceRects:
  •                 x, y, w, h = faceRect
  •                 #截取脸部图像提交给模型识别这是谁
  •                 image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
  •                 faceID = model.face_predict(image)
  •                 #如果是“我”
  •                 if faceID == 0:
  •                     cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness = 2)
  •                     #文字提示是谁
  •                     cv2.putText(frame,'Me',
  •                                 (x + 30, y + 30),                      #坐标
  •                                 cv2.FONT_HERSHEY_SIMPLEX,              #字体
  •                                 1,                                     #字号
  •                                 (255,0,255),                           #颜色
  •                                 2)                                     #字的线宽
  •                 else:
  •                     pass
  •         cv2.imshow("Recognise myself", frame)
  •         #等待10毫秒看是否有按键输入
  •         k = cv2.waitKey(10)
  •         #如果输入q则退出循环
  •         if k & 0xFF == ord('q'):
  •             break
  •     #释放摄像头并销毁所有窗口
  •     cap.release()
  •     cv2.destroyAllWindows()

在shell输入:<python face_predict_use_keras.py 0>可以看到视频窗口里自己的头像会被文字“Me”标出。


结语

本文实现了一个简单的深度学习项目:用tensorflow实现人脸检测及识别。本文主要专注于实际的操作过程及步骤,本文对于代码中的原理没有过多解释。希望更进一步了解的朋友可以从开头提供的博客及寻找相关资料进行学习。




参考文献:

1.http://www.cnblogs.com/neo-T/p/6511273.html

2.https://zhuanlan.zhihu.com/p/36962109


本文转自:CSDN

最近看过此主题的会员

ccchen

17608439833

DJY

ncjindsif

hhdtest

微凉夏

usery

hee

张名

jxchencong

橙子

chenzyd

刘子瑜

zengxq

cghb

l3104468710

18987056421

3个回答

只看楼主

数据王子 数据达人Lv4

发表于2018-5-29 10:58

只看该作者

取消 关注该作者的回复

沙发

感谢大神分享哟!!

疯人院的歌颂者 初学数据Lv2

发表于2018-5-29 10:59

只看该作者

取消 关注该作者的回复

板凳

端好小板凳,积极学习中~

狗子还好吗 初学数据Lv2

发表于2018-5-29 11:00

只看该作者

取消 关注该作者的回复

地板

感谢分享,晚上下班后去尝试一下~

登录后可回答问题,请登录注册

快速回复 返回顶部 返回列表

小时

全天响应

分钟

快速处理问题

工程师强势助力

明星产品
解决方案
联系合作

400咨询:400-0011-866

技术支持QQ:400-0011-866(工作日9:00-18:00)

产品建议邮箱yixin@esensoft.com

关注我们

扫TA学习更多干货

一对一专家交流

版权所有© 2006-2024 北京亿信华辰软件有限责任公司 京ICP备07017321号 京公网安备11010802016281号