Python-OpenCV训练一个人脸识别器

Published by 煎鱼 on

想要能人脸识别,我们需要训练一个识别器处理。训练的话就需要之前已经标注好的训练集,在前一篇文章中,我们创建了一个已经标注好的训练集。现在,是时候用这个训练集来训练一个人脸识别器了。当然,是用OpenCV Python。

准备

首先,我们在(前一篇文章的)同目录下创建一个叫Python文件,名为trainner.py,用于编写数据集生成脚本。同目录下,创建一个文件夹,名为trainner,用于存放我们训练后的识别器。

现在,项目目录大概如此:

其他乱七八糟的文件、目录,都是之前的文章中创建的。

在开始之前,我们先安装一个Python库,Pillow:

pip install pillow

开始Coding

导入库

编写训练程序需要先做是:

  1. 导入opencv库
  2. 导入os库,用于方法文件
  3. 导入numpy库,用于计算
  4. 导入pillow库,用于处理图像

其实就是这样:

import cv2
import os
import numpy as np
from PIL import Image

现在我们初始化识别器和人脸检测器:

recognizer = cv2.face.LBPHFaceRecognizer_create()
# 有可能是 recognizer = cv2.createLBPHFaceRecognizer()
detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

如果face.LBPHFaceRecognizer_create或createLBPHFaceRecognizer显示不存在,则需要下载opencv-contrib-python:

pip install opencv-contrib-python

当然用IDE的也行:

载入训练数据

恩,现在我们来创建一个函数,用于从数据集文件夹中获取训练图片,然后从图片的文件名中获取到这个素材相应的id。需要remind的是,根据前文,图片的格式是User.id.samplenumber

给函数起个名字,就叫get_images_and_labels吧(Python不建议用驼峰),然后参数需要有素材的文件夹:

def get_images_and_labels(path):

在函数中,我们需要的做的有:

  1. 从数据集文件夹中载入训练图片
  2. 获取到人脸和id
  3. 整理成list并返回

获取图片:

image_paths=[os.path.join(path, f) for f in os.listdir(path)]

新建两个list用于存放:

face_samples=[]
ids=[]

遍历图片路径,导入图片和id,添加到list:

    for image_path in image_paths:
        image = Image.open(image_path).convert('L')
        image_np = np.array(image, 'uint8')
        image_id = int(os.path.split(image_path)[-1].split(".")[1])
        faces = detector.detectMultiScale(image_np)
        for (x, y, w, h) in faces:
            face_samples.append(image_np[y:y + h, x:x + w])
            ids.append(image_id)

以上代码,使用了Image.open(image_path).convert(‘L’)通过图片路径并将其转换为灰度图片。

接下来我们通过image_np = np.array(image, 'uint8')将图片转换成了Numpy数组,Numpy数组的逻辑结构和普通的数组无异,但是是经过优化的。

为了获取到id,我们将图片的路径分裂一下并获取相关信息,即image_id = int(os.path.split(image_path)[-1].split(".")[1])

接下来的一个循环for (x, y, w, h) in faces则是将图片和id都添加在list中。

再return一下即可。

训练

差不多完成了,现在我们调用一下这个函数,然后将我们的数据喂给识别器去训练吧。

faces, Ids = get_images_and_labels('dataSet')
recognizer.train(faces, np.array(Ids))
recognizer.save('trainner/trainner.yml')

现在只要我们运行这些代码,程序就会在trainner文件夹中创建一个trainner.yml文件。

这个yml文件,存着我们的训练好的数据,以后识别会用到的。

完整代码

import cv2
import os
import numpy as np
from PIL import Image

# recognizer = cv2.createLBPHFaceRecognizer()
detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
recognizer = cv2.face.LBPHFaceRecognizer_create()


def get_images_and_labels(path):
    image_paths = [os.path.join(path, f) for f in os.listdir(path)]
    face_samples = []
    ids = []

    for image_path in image_paths:
        image = Image.open(image_path).convert('L')
        image_np = np.array(image, 'uint8')
        if os.path.split(image_path)[-1].split(".")[-1] != 'jpg':
            continue
        image_id = int(os.path.split(image_path)[-1].split(".")[1])
        faces = detector.detectMultiScale(image_np)
        for (x, y, w, h) in faces:
            face_samples.append(image_np[y:y + h, x:x + w])
            ids.append(image_id)

    return face_samples, ids


faces, Ids = get_images_and_labels('dataSet')
recognizer.train(faces, np.array(Ids))
recognizer.save('trainner/trainner.yml')

先这样吧

原文,若有错误之处请指出,更多地关注煎鱼


14 Comments

weixin_王玮_1 · 06/24/2018 at 00:49

写的很仔细,我有个疑问,图片训练集不用裁剪为统一大小么,看别的博客,为什么都要裁剪为统一大小,求指点求指点

    aimer · 06/24/2018 at 00:50

    不同的训练机制有不同的特点吧。比如说有的把输入图像的高宽固定住了就必须要统一大小了,而有的则通过改进就可以输入不同大小的图片,如RCNN中的插入一个pooling层,它可以将不同大小的输入映射成固定大小的特征向量。 当然,继续研究OpenCV这个东西用了什么训练机制,这个就不太好意思了,还没有时间去研究😂

clarence · 10/30/2018 at 20:18

python3 没有 createLBPHFaceRecognizer 这个模块了,
需要安装
> pip install opencv-contrib-python
然后
recognizer = cv2.face_LBPHFaceRecognizer.create()

我是这样做的..

    煎鱼 · 11/02/2018 at 13:44

    感谢补充

坏坏 · 12/02/2018 at 18:39

老哥 那这个岂不是只要有检测出人脸就一定会返回一个在yml里的id?
那怎么识别一个没经过训练的陌生人呢?

    煎鱼 · 12/04/2018 at 16:50

    不是的,我大致说一下我理解的。
    简单来说,识别过程一般是,识别器和输入图像进行计算,计算得出结果(就是你说的id)和置信度,当这个置信度大于设定的阈值时(比如说50%),才会认为这个结果是正确的。
    如果对陌生人进行识别,一般置信度是不会达标的,识别器会认为识别不出已知的人脸。
    详情可以看看这里的代码:https://www.mkshell.com/python-opencv-face-recognition/

      坏坏 · 12/06/2018 at 18:40

      🤔这样啊,谢谢

肉肉 · 03/28/2019 at 22:50

1. 想问一下app.py和generate.py里有什么?因为我没有把cascade之类的xml和recognizer放在trainer文件夹里。也就是说trainer文件夹里只有trainer.py和trainer.yml。
2. 在dataSet里有100张我的照片,然而识别的时候别人的脸和我的照片之间的conf总是出奇的高(95之类的),想问一下为什么?

    煎鱼 · 03/28/2019 at 23:02

    1. app和generate是之前生成训练数据用到的文件,详情可以翻之前的文章,https://www.mkshell.com/face-detection-using-opencv-in-python/,https://www.mkshell.com/face-recognition-dataset-generator/。 有trainer和yml,就证明你已经训练过数据了,有模型就可以直接去拿去识别了:https://www.mkshell.com/python-opencv-face-recognition/。
    2. 识别时,conf越高,代表识别的置信率越高,就越可信(当然,也有可能是误判)。
    3. 最后,希望你不只是学习这么浅的模型,可以感受下CNN那些更深的模型,识别率会高很多。

肉肉 · 03/28/2019 at 23:27

谢谢!

long · 07/30/2019 at 16:26

这个image 的图片路径应该是什么样的?

Benja · 03/15/2020 at 15:42

mac用户一定要记得删除dataset下的隐藏文件.Ds_Store 不然疯狂报错

    煎鱼 · 03/15/2020 at 15:47

    感谢补充~

badage · 06/20/2020 at 11:08

py版本为3.7但是recognizer = cv2.face.LBPHFaceRecognizer_create()我的cv2找不到face,求解·

发表评论

电子邮件地址不会被公开。 必填项已用*标注