# 使用 Haar Cascades 进行人脸检测

## 目标

在本次会议中:

* 我们将看到使用基于 Haar 特征的级联分类器进行人脸检测的基础知识
* 我们将对眼睛检测等进行扩展。

## 基本

使用基于 Haar 特征的级联分类器的对象检测是 Paul Viola 和 Michael Jones 在他们的论文“使用简单特征的 Boosted 级联的快速对象检测”中提出的有效的对象检测方法。它是一种基于机器学习的方法，其中 a 级联功能是从许多正面和负面图像中训练出来的。然后它用于检测其他图像中的对象。

在这里，我们将使用面部检测。最初，该算法需要许多正图像（面部图像）和负图像（没有面部的图像）来训练分类器。然后我们需要从中提取特征。为此，使用下图所示的 Haar 特征。它们就像我们的卷积内核一样。每个特征是通过从黑色矩形下的像素之和减去白色矩形下的像素之和而获得的单个值。

![图片](/docs/4.0.0/img/haar_features.jpg)

现在，每个内核的所有可能大小和位置都用于计算大量功能。（想象一下它需要多少计算？即使 24x24 窗口也会产生超过 160000 个特征）。对于每个特征计算，我们需要找到白色和黑色矩形下的像素之和。为了解决这个问题，他们引入了积分图像。无论图像多大，它都会将给定像素的计算减少到仅涉及四个像素的操作。不错，不是吗？它使事情超级快。

但在我们计算的所有这些功能中，大多数都是无关紧要的。例如，请考虑下面的图像。第一行显示了两个很好的功能。选择的第一个特征似乎集中在眼睛区域通常比鼻子和脸颊区域更暗的属性。选择的第二个特征依赖于眼睛比鼻梁更暗的特性。但适用于脸颊或任何其他地方的窗户是无关紧要的。那么我们如何从 160000 多个功能中选择最佳功能呢？这是由 Adaboost 实现的。

![图片](/docs/4.0.0/img/haar.png)

为此，我们在所有训练图像上应用每个特征。对于每个特征，它会找到最佳阈值，将面部分类为正面和负面。显然，会出现错误或错误分类。我们选择具有最小错误率的特征，这意味着它们是最准确地对面部和非面部图像进行分类的特征。（过程并不像这样简单。每个图像在开始时给予相同的权重。在每次分类之后，错误分类图像的权重增加。然后进行相同的处理。计算新的错误率。还有新的权重。继续处理，直到达到所需的精度或错误率或找到所需的特征数量）。

最终的分类器是这些弱分类器的加权和。它被称为弱，因为它不能单独对图像进行分类，但与其他图像一起形成一个强大的分类器。该报称，即使是 200 种功能也能提供 95％的检测精度。他们的最终设置有大约 6000 个功能。（想象一下，从 160000 多个功能减少到 6000 个功能。这是一个很大的收获）。

所以现在你拍了一张照片。每个 24x24 窗口。应用 6000 功能。检查它是否是面部。哇..是不是有点低效和耗时？是的。作者有一个很好的解决方案。

在图像中，大部分图像是非面部区域。因此，有一个简单的方法来检查窗口是否不是面部区域是一个更好的主意。如果不是，请一次丢弃，不要再处理。相反，要关注可能有面孔的区域。这样，我们花费更多时间检查可能的面部区域。

为此，他们引入了 **Cascade of Classifiers** 的概念。这些功能不是在窗口上应用所有 6000 个功能，而是分组到不同的分类器阶段并逐个应用。（通常前几个阶段将包含很少的功能）。如果窗口在第一阶段失败，则将其丢弃。我们不考虑其余的功能。如果通过，则应用第二阶段功能并继续该过程。通过所有阶段的窗口是面部区域。那个计划怎么样！

作者的探测器具有 6000 多个特征，其中 38 个阶段在前五个阶段具有 1 个，10 个，25 个，25 个和 50 个特征。（上图中的两个特征实际上是 Adaboost 中最好的两个特征）。据作者说，平均每个子窗口评估了 6000 多个特征中的 10 个特征。

因此，这是 Viola-Jones 面部检测工作原理的简单直观解释。阅读本文以获取更多详细信息，或查看“其他资源”部分中的参考资料。

## OpenCV 中的 Haar-cascade 检测

OpenCV 配有训练器和探测器。如果您想为汽车，飞机等任何对象训练自己的分类器，您可以使用 OpenCV 创建一个。它的全部细节在这里给出：[级联分类器训练](docs/4.0.0/tutorial_traincascade.md)。

在这里我们将处理检测。OpenCV 已经包含许多面部，眼睛，微笑等预先训练的分类器。这些 XML 文件存储在 opencv/data/haarcascades/文件夹 中。让我们用 OpenCV 创建一个面部和眼睛探测器。

首先，我们需要加载所需的 XML 分类器。然后以灰度模式加载输入图像（或视频）。

```python
import numpy as np
import cv2 as cv
face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv.CascadeClassifier('haarcascade_eye.xml')
img = cv.imread('sachin.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
```

现在我们在图像中找到面孔。如果找到了面，它会将检测到的面的位置返回为 Rect（x，y，w，h）。一旦我们获得这些位置，我们就可以为脸部创建投资回报率，并在此投资回报率上应用眼睛检测（因为眼睛总是在脸上!!!）。

```python
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
    cv.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_gray)
    for (ex,ey,ew,eh) in eyes:
        cv.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()
```

结果如下所示：

![图片](/docs/4.0.0/img/face.jpg)

## 其他资源

* 关于 [人脸检测和跟踪](https://www.youtube.com/watch?v=WfdYYNamHZ8) 的视频讲座
* [Adam Harvey](https://web.archive.org/web/20171204220159/http://www.makematics.com/research/viola-jones/) 关于人脸探测的有趣采访

## 练习
