---
title: 'Python-OpenCV训练一个人脸识别器'
pubDate: 2018-05-17
tags:
  - 'cv'
description: 'Python-OpenCV训练一个人脸识别器'
---

想要能人脸识别，我们需要训练一个识别器处理。训练的话就需要之前已经标注好的训练集，在[前一篇文章](https://www.jianyujianyu.com/face-recognition-dataset-generator/)中，我们创建了一个已经标注好的训练集。现在，是时候用这个训练集来训练一个人脸识别器了。当然，是用OpenCV Python。

## 准备

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

现在，项目目录大概如此：

![](https://source.mkshell.com/15265263830787.jpg)

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

在开始之前，我们先安装一个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的也行：

![](https://source.mkshell.com/15265427455379.jpg)

### 载入训练数据

恩，现在我们来创建一个函数，用于从数据集文件夹中获取训练图片，然后从图片的文件名中获取到这个素材相应的id。需要remind的是，根据[前文](https://www.jianyujianyu.com/face-recognition-dataset-generator/)，图片的格式是`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文件。

![](https://source.mkshell.com/15265428780936.jpg)

这个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')
```

_先这样吧_

[原文](https://thecodacus.com/face-recognition-opencv-train-recognizer/)，若有错误之处请指出，更多地关注[煎鱼](http://www.jianyujianyu.com)。
