基于keras的人脸识别

一、整体思路

1. 人脸检测

为了后续人脸识别时能通过摄像头实时检测出人脸,进而对框出的人脸进行识别。

  1. 引用Python中的opencv库,调用其中的人脸分类器。

  2. 调用摄像头,使用循环读取单帧数据。

  3. 进行人脸检测,框出人脸。

2. 读取训练数据

将训练数据集从图片转换成数组形式,并得到对应标签。

  1. 引用Python中的os库对指定文件夹进行逐层搜索。

  2. 使用数组记录下搜索到的图片的路径。

  3. 将图片转化为四维数组。

  4. 根据路径得到每张图片的标签。

  5. 根据路径得到数组序号对应的学号。

3. 训练模型

构建CNN网络将数据集训练为可直接调用的、性能较好的模型。

  1. 加载数据集,使用 train_test_split 函数将数据集分别按一定比例分成训练集与验证集和训练集与测试集。

  2. 使用categorical_crossentropy作为损失函数。

  3. 将得到数据集进行浮点化和归一化。

  4. 根据类别数量nb_classes将类别标签进行one-hot编码使其向量化。

  5. 构建一个空的线性堆叠网络模型。

  6. 构建CNN网络模型。

  7. 训练模型,使用实时数据提升进行训练。

  8. 存储模型,并对模型进行评估。

4. 人脸识别

调用模型对摄像头读取的视频或选中的图片进行人脸识别。

  1. 调用摄像头或读取图片。

  2. 对单帧图片进行灰度化处理。

  3. 检测人脸并框出对应区域。

  4. 对检测出的人脸进行识别。

  5. 显示检测结果的序号与学号。

5. 可视化

  1. 使用pyqt5构建可视化见面。

  2. 打包成可执行文件。

二、核心代码

1. loaddata.py

读取训练数据,将训练数据集从图片转换成数组形式,并得到对应标签,同时得到数组序号对应的学号。

函数功能表

函数名 函数功能
read_path(path_name) 搜索并读取对应路径下的图片,将其和其标签添加到数组 images[ ] 和 lables[ ] 中。
load_dataset(path_name) 将 read_path 中读取的图片转化为四维数组,尺寸为(图片数量x图片长x图片宽x3)。
match_number(path_name) 通过文件夹名字得到数组序号所对应的学号,比如 0 对应 1511277 。

核心代码

def read_path(path_name):  #搜索并读取对应路径下的图片,将其和其标签添加到数组 images和 lables中
    k = -1
    for dir_item in os.listdir(path_name):
        k = k + 1
        numbers.append(str(dir_item))
        # 从初始路径开始叠加,合并成可识别的操作路径
        full_path = os.path.abspath(os.path.join(path_name, dir_item))
        for image_item in os.listdir(full_path):
            if image_item.endswith('.png'):
                image_path = os.path.abspath(os.path.join(full_path, image_item))
                image = cv2.imread(image_path)
                images.append(image)
                labels.append(k)
    return images, labels, numbers
def load_dataset(path_name):  # 从指定路径读取训练数据
    images, labels, numbers = read_path(path_name)
    # 将输入的所有图片转成四维数组,尺寸为(图片数量*IMAGE_SIZE*IMAGE_SIZE*3)
    images = np.array(images)
    return images, labels
def match_number(path_name):  # 得到序号对应的学号
    images, labels, numbers = read_path(path_name)
    return numbers

2. facetrain.py

函数功能表

  1. class Dataset

加载数据集并对其进行相应的处理。

函数名 函数功能
dataload(self, img_rows, img_cols,img_channels, nb_classes) 划分数据集,根据keras库要求的维度顺序重组训练数据集,将类别标签进行one-hot编码使其向量化,将数据浮点化、归一化。
  1. class Model

构建CNN网络并训练数据集得到模型。

函数名 函数功能
build_model(self, dataset, nb_classes) 构建CNN网络。
train(self, dataset, batch_size, nb_epoch) 训练模型,并使用实时数据提升进行训练。
save_model(self, file_path=MODEL_PATH) 将模型(.h5文件)存储到指定路径。
load_model(self, file_path=MODEL_PATH) 用于加载指定路径的模型。
evaluate(self, dataset) 使用之前划分的测试集对加载的模型进行评估。
face_recognize(self, image) 对给出的图片进行resize并进行识别。

核心代码

  1. class Dataset


    def dataload(self, img_rows=IMAGE_SIZE, img_cols=IMAGE_SIZE, img_channels=3, nb_classes=60):
        # 加载数据集到内存
        images, labels = load_dataset(self.path_name)
        # 将数据集分别划分为训练集、验证集和训练集和测试集,划分比例由test_size确定
        train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels,                                                      test_size=0.2,random_state=0)
        _, test_images, _, test_labels = train_test_split(images, labels, test_size=0.4,
                                                          random_state=0)
        # tensorflow的维度顺序为行数(rows)、列数(cols)、通道数(颜色通道,channels)
        # 根据keras库要求的维度顺序重组训练数据集
        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)
        # 使用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
  1. class Model

def build_model(self, dataset, nb_classes=60):
        # 构建一个空的线性堆叠模型(序贯模型),各神经网络层会被顺序添加
        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):  # 训练模型
        # 采用SGD+momentum的优化器进行训练,首先生成一个优化器对象
        sgd = SGD(lr=0.01, decay=1e-6,momentum=0.9, nesterov=True)  
        # 完成实际的模型配置工作
        self.model.compile(loss='categorical_crossentropy',optimizer=sgd,metrics=['accuracy']) 
        # 使用实时数据提升,从训练数据中利用旋转、翻转、加噪声等方法创造新的训练数据
        datagen = ImageDataGenerator(
            rotation_range=20,  # 数据提升时图片随机转动的角度(范围为0~180)
            width_shift_range=0.2,  # 数据提升时图片水平偏移的幅度
            height_shift_range=0.2,  # 数据提升时图片垂直偏移的幅度
            horizontal_flip=True)  # 进行随机水平翻转
        # 计算整个训练样本集的数量以用于特征值归一化、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 = './model/facerec_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_recognize(self, image):  # 识别人脸
        if backend.image_dim_ordering() == 'tf' and image.shape != (1, IMAGE_SIZE, IMAGE_SIZE, 3):
            image = resize_image(image)  # 尺寸必须与训练集一致都应该是IMAGE_SIZE x IMAGE_SIZE
            image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 3))
        image = image.astype('float32')  # 浮点化
        image /= 255  # 归一化
        result = self.model.predict_proba(image)  # 给出输入属于各个类别的概率
        result = self.model.predict_classes(image)  # 给出类别预测
        return result[0]  # 返回类别预测结果3. facerecognition.py

3. facerecognition.py

函数功能表

函数名 函数功能
rec(frame) 对得到的图片进行识别,并标上标签、学号。
camerarec() 调用摄像头的实时识别。
imgrec(img_path) 对指定路径的单张图片的识别。

核心代码


def rec(frame):
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # 图像灰度化
    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_recognize(image)  # 截取脸部图像提交给模型识别这是谁
            cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness=2)
            # 文字提示 标签+学号
            cv2.putText(frame, 'NO.' + str(faceID) + '  ' + numbers[faceID],
                        (int(x), y + 65), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), thickness=2)
    return frame
def camerarec():  # 摄像头
    cap = cv2.VideoCapture(0)  # 捕获指定摄像头的实时视频流
    while True:  # 循环检测识别人脸
        _, frame = cap.read()  # 读取一帧视频
        rec(frame)
        cv2.imshow("Recognition", frame)
        c = cv2.waitKey(10)
        if c & 0xFF == 27:   # 等待ESC按键输入 关闭窗口
            break
    cap.release()  # 释放摄像头并销毁所有窗口
    cv2.destroyAllWindows()
def imgrec(img_path):  # 指定路径的单张图片
    frame = cv2.imread(img_path)
    rec(frame)
    cv2.imshow("Recognition", frame)
    cv2.waitKey()  # 等待按键输入 关闭窗口

三、模型评估

对训练得到的模型进行评估和调整,得到最佳的模型。

​ 在模型训练中,主要参数有:批尺寸batch_size,时期数nb_epoch,学习率lr。

适当的增加batch_size 的优点:

  1. 通过并行化提高内存利用率。

  2. 单次epoch的迭代次数减少,提高运行速度。

  3. 适当的增加batch_size,梯度下降方向准确度增加,训练震动的幅度减小。

经验总结:

  1. 相对于正常数据集,如果batch_size过小,训练数据就会非常难收敛,从而导致underfitting。

  2. 增大batch_Size,相对处理速度加快,但所需内存容量增加(epoch的次数需要增加以达到最好结果)。

  3. iteration:1个iteration等于使用batchsize个样本训练一次。

  4. 我们发现当epoch增加以后同样也会导致耗时增加从而速度下降,因此我们需要寻找最好的batch_size。

batch_size iteration nb_epoch lr loss accuracy
15 1600 10 0.01 1.0126 70.72%
20 1200 10 0.01 0.8014 75.82%
30 800 15 0.01 0.5602 85.77%

没有更多推荐了,返回首页