人脸检测--传统方法

news2024/10/7 19:22:25

人脸检测与识别综述

人脸检测与识别是计算机视觉和生物识别领域中最受关注的研究内容。

如何从包含人脸内容的图像、视频等多媒体数据中,找到人脸,并对其身份进行判定,是人脸检测与识别中的主要问题。

应用场景

- 安全监控

- 访问控制

- 数据挖掘

如何将人脸识别与检测技术应用于新的场景,是具有应用创新的工作。

基本流程

总体上,人脸检测与识别包含如下几个关键技术步骤:

1. 人脸检测

利用目标检测算法,从视频或者图像中检测出所有人脸区域。

2. 人脸对齐

将检测到的人脸区域进行对齐,使他们具有相同的尺度、姿态和位置

3. 特征提取

从检测得到的特征中提取出特征向量,作为人脸的唯一特征

4. 特征检索

将特征与数据库中的特征进行比较,找到最佳匹配

主要挑战

人脸检测与识别在实际应用中,不同的场景具有不同的挑战。

例如,监控场景下的大规模人群中检测出人脸小目标,是目前学术界主要关心的核心问题之一


在人脸识别中,一些长期存在的、经典的挑战包括 

- 头部姿态

- 年龄、妆容的变换

- 光影的变化

- 表情的变换

- 遮挡


人脸检测与识别的主要数据集

人脸检测

[链接1](http://host.robots.ox.ac.uk/pascal/VOC/databases.html) |
[链接2](http://shuoyang1213.me/WIDERFACE/) |
[链接3](http://vis-www.cs.umass.edu/fddb/) |

人脸识别

  [链接1](https://mmlab.ie.cuhk.edu.hk/projects/CelebA.html) |
 [链接2](http://vis-www.cs.umass.edu/lfw/) |

目标

1. 了解人脸检测与识别的基本目标,常规流程,以及数学定义

2. 掌握人脸检测的基本算法和处理方法,理解每个步骤的意义所在

3. 了解较新的人脸检测与识别算法的基本思路

人脸检测--传统方法1

非深度学习方法

基本思路

从一张图像中找到人脸目标,需要找寻人脸的特点。

方法1:模板匹配

一种浅显且直白的方式,就是利用模板匹配算法来实现,如下图所示


显然,这种方法不够好。无法通用的检索出“人脸”这个概念。
由于人脸缺少一些模式化的特征,不具备特殊的纹理、形状等规律,因此很难利用初等图像处理的方法来得到普世的结果。
**肤色检测** 而通过肤色划分出人脸区域,通常是较为常见的一种做法。

除此之外,将图像分成小块,并对每个小块进行特征提取,通过机器学习的方式,也可以实现人脸检测。

需要注意的是,传统方法由于缺少特征的鲁棒性无法获取高维特征表述,因此鲁棒性不够

可以考虑不同方法的叠加进行实现。

方法二:肤色检测

import numpy as np
import cv2
import matplotlib.pyplot as plt
img1 = cv2.imread("../../dataset/d1.png")
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
plt.imshow(img2)

img_HSV = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
HSV_mask = cv2.inRange(img_HSV, (0, 60, 60), (20,255,255)) 
HSV_mask = cv2.morphologyEx(HSV_mask, cv2.MORPH_OPEN, np.ones((3,3), np.uint8))
plt.imshow(HSV_mask)

 

img_YCrCb = cv2.cvtColor(img1, cv2.COLOR_BGR2YCrCb)
YCrCb_mask = cv2.inRange(img_YCrCb, (80, 135, 85), (255,255,255))
YCrCb_mask[np.abs(img_YCrCb[:, :, 1] - img_YCrCb[:, :, 2])<15] = 0 
YCrCb_mask = cv2.morphologyEx(YCrCb_mask, cv2.MORPH_OPEN, np.ones((3,3), np.uint8))
plt.imshow(YCrCb_mask)

 

final_mask=cv2.bitwise_and(YCrCb_mask,HSV_mask)
final_mask=cv2.medianBlur(final_mask,3)
final_mask = cv2.morphologyEx(final_mask, cv2.MORPH_OPEN, np.ones((4,4), np.uint8))
plt.imshow(final_mask)

img2[final_mask == 0] = 0
plt.imshow(img2)

同hsv一样,ycrcb格式也是一种颜色空间

其中,Y表示亮度,Cr和Cb表示色度信息

常用于肤色检测。(因为肤色载这一空间更加集中)

通常,肤色检测中,采用的阈值为

Y>80, Cr>135, Cb>85, 且|Cr-Cb|>15


需要注意的是,颜色分割只能作为识别的辅助进行。因为其非常容易收到光照和肤色的影响。

方法3:手工特征+分类器

img_a = cv2.imread("../../dataset/d1.png")
img_b = cv2.imread("../../dataset/d2.jpg")

sift = cv2.SIFT_create()

plt.subplot(1,2,1)
plt.imshow(cv2.cvtColor(img_a, cv2.COLOR_BGR2RGB))
plt.subplot(1,2,2)
plt.imshow(cv2.cvtColor(img_b, cv2.COLOR_BGR2RGB))

 

人脸的另一个规律在于,五官之间存在一定的模式

这种模式很难拿自然语言完整的描述出来,大致上,人脸图像应当符合这样的内容

如 :

    - 眼睛区域暗于脸颊区域
    
    - 嘴部区域暗于周围区域等
那么本质上,我们只需要**总结出脸部区域的明暗变化**,那么在待检测区域中发现了类似的模式,则可以认为是一个人脸。
那么如何总结出人脸模式呢?

机器学习
这里涉及两个部分:

1. 特征提取

2. 分类器训练
特征提取:HOG特征
hog, higtogram of oriented gradient, 是抓取图像轮廓的算法。

具体上,将一张图像切成很多个区域,并从每个区域中提取出方向梯度,作为特征。

其基本步骤包括:

1. 图像预处理: 灰度化、归一化、平滑等
2. 计算梯度: 用sobel计算梯度的赋值和方向
3. 分割图像:将图像切割成大小相同的cell。并构建直方图
4. 块归一化:将每个patch中的图直方图进行归一化,消除光影影响
5. 将所有直方图组合成一个特征向量

# 块大小
patch_size = 8
# 直方图数量
nbins = 9

img = cv2.imread('../../dataset/d2.jpg', 0)
plt.imshow(img, cmap='gray')


 

gx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1)
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=1)
mag, ang = cv2.cartToPolar(gx, gy)
plt.imshow(mag, cmap='gray')

ph = patch_size
pw = patch_size
h, w = img.shape 

n_h = h // ph
n_w = w // pw

bin_width = 360 // nbins
bins = np.zeros((mag.shape[0], mag.shape[1], nbins))
for i in range(nbins):
        angle_bin = ang < (i + 1) * bin_width
        angle_bin = angle_bin.astype(np.float32)
        bins[:, :, i] = mag * angle_bin
feature_vectors = np.zeros([img.shape[0] // patch_size, img.shape[1] // patch_size, nbins])
for iidx, i in enumerate(range(0, img.shape[0] - patch_size + 1, patch_size)):
        for jidx, j in enumerate(range(0, img.shape[1] - patch_size + 1, patch_size)):
            cell_bins = bins[i:i+patch_size, j:j+patch_size, :]
            cell_hist = np.sum(cell_bins, axis=(0, 1))
            feature_vectors[iidx, jidx, :] = cell_hist


归一化,就是将临近四个patch放到一起进行归一化

归一化的目的在于去除阴影和光照的影响,尽可能地令光照统一。

归一化的方法包括采用l2,l1等,如以下公式所示

block_size = 2

block_size_cells = (block_size, block_size)
feature_vectors = np.array(feature_vectors)
eps = 1e-7
for i in range(0, feature_vectors.shape[0] - block_size + 1, block_size):
        for j in range(0, feature_vectors.shape[1] - block_size + 1, block_size):
            block = feature_vectors[i:i+block_size, j:j+block_size, :]
            block_sum = np.sum(block)
            block_norm = block / (block_sum + eps)
            feature_vectors[i:i+block_size, j:j+block_size, :] = block_norm
features = feature_vectors.reshape(-1)
print(features.shape)

以上,就是hog特征的底层提取方法。

opencv也为提取hog特征提供了接口,具体实现如下所示

hog = cv2.HOGDescriptor()
features = hog.compute(img)
print(features.shape)

需要注意的是,实际上我们采用滑动窗口的方式来检测人脸。

固定窗口尺寸为50\*50,然后为每个patch提取特征

然后,对提取的特征进行分类器分类。
分类器

支持向量机SVM,在特征空间上找到一个超平面将不同类别分开。

sklearn库中提供了相关的接口实现
 

hog = cv2.HOGDescriptor()
features = hog.compute(img)
print(features.shape)
# 训练过程

```
grad = GridSearchCV(LinearSVC(dual=False), {'C': [1.0, 2.0, 4.0, 8.0]}, cv=3)

grad.fit(X_train, y_train)

print(grad.best_score_)

print(grad.best_params_)

model = grad.best_estimator_

model.fit(X_train, y_train)

```

# 测试过程

```
labels = model.predict(patches_hog)
```

人脸检测的传统方法2

传统方法对比深度学习的一个主要弱势在于特征不够泛化,不能描述抽象的概念

对于这样的特征,就需要更好的分类器,或者更加复杂的分类器。

在前深度学习时代,提高分类器性能的一个主要手段在于**集成**。(深度学习时代也是,kaggle中有大量的高分项目都采用了集成策略。)

 集成学习

集成算法,是指将一组不同的分类器整合起来,相互配合地对目标进行预测。

其中所涉及的一个主要内容就是

结合策略

1. 平均法

把若干个学习器的输出进行加权平均,得到最终结果的输出。

其中,权重设置为可学习,就能得到更加鲁棒的学习器。

2. 投票法

少数服从多数的策略。

包括:多数投票法,或者加权投票法等。

同理,也可以设置可学习的分类器权重。

3. stacking

将一部分的分类器的输出,作为另一部分学习器的输入,构建次级模型来提高模型性能。

例如,将一组20个svm的分类器的十分类预测结果组合到一起,输入一个可以接受200维输入的分类器中,进一步预测10分类结果。    

 Boosting算法

强学习:分类精度比较高的分类器

弱学习:分类精度比随机分类高一点的分类器

通过集成了多个弱学习分类器,通过非随机的组合,得到一个强分类器。

 adaboost模型

adaboost模型是一种boosting算法

 更新权重



最终,采用加权平均法,得到最终的分类器如下


总结:adaboost采用了较低的成本,得到了一个性能与SVM差不多的分类器。 

其特点在于,对于难分样本,具有很好的效果。且不容易过拟合。

但是对于噪声和扰动较为敏感

Haar特征

Haar是一种用模板来描述图像变化的工具,所得到的结果可以作为图像特征。

根据目标不同,设计了多种不同的模板。

这些特征,实际上就是卷积神经网络的灵感源泉之一。

在实际运算中,haar特征的精度并不高。但结合积分图,可以快速计算图像特征

具体计算方法是,白色区域的和 减去 黑色区域的和.

本质上,白色区域的值为1,黑色区域的值为0。

如何利用haar特征进行较好的目标检测?

adaboost的级联算法

首先,用提取的haar特征,训练一个adaboost分类器。

分类器会给出一个决策值,如果这个值大于设定的阈值,则认为是人脸,如果小于,则认为不是人脸。

然而,阈值的设定非常难,且不鲁棒
那么之所以可能会分错,可能是因为特征不够泛化。

所以,我们重新设置haar的参数,来获得不同的特征表达。

并且用这种特征,训练一个新的分类器。
两种分类器之间,采用级联方式链接

如果A分类器能够确定他是一个目标,则认为是一个目标

如果不能确定是一个目标,则会把他交给B分类器继续判断。

依次类推,直到最后的分类器也不能确定他是一个目标,则不是目标。

同理,上述过程可以反过来,如果有一级分类器判断他不是,则他就不是目标;直到最后一级分类器也认定是一个人脸,则才是一个人脸。

 以上过程就是经典的机器学习理论之一:决策树

决策树集成

一个决策树可能并不鲁棒。

我们将数据集划分成若干个重叠的部分,分别训练若干个决策树,并用加权集成的方式,来得到最终的结果。


训练与推理

级联分类器的训练过程,可采用opencv提供的工具来实现。

具体可见该链接

[级联分类器的训练-英](https://docs.opencv.org/4.2.0/dc/d88/tutorial_traincascade.html)

[级联分类器的训练-中](https://zhuanlan.zhihu.com/p/407571417)

具体上,要使用到`opencv_traincascade.exe`训练准备好的数据。

不过,也可以使用opencv提供的事先训练好的模型直接进行推理。具体链接为[训练好的各种级联模型](https://github.com/opencv/opencv/tree/master/data/haarcascades)以训练好的模型为例,一个haar级联分类器的人脸检测代码实现如下

import cv2
import matplotlib.pyplot as plt

face_detector = cv2.CascadeClassifier('../../dataset/haarcascade_frontalface_default.xml')
img = cv2.imread('../../dataset/lena.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_detector.detectMultiScale(gray,1.1, 4 )
for (x,y, w, h) in faces:
    cv2.rectangle(img, pt1 = (x,y),pt2 = (x+w, y+h), color = (255,0,0),thickness =  3)
    roi_gray = gray[y:y+h,x:x+w]
    roi_color = img[y:y+h, x:x+w]

plt.imshow(img)

 

其他的方法与思路

前深度学习时代,思路和方法百花齐放,人们采用不同的方式来实现目标检测。

主要来说,研究的重点涉及两个方面:特征和分类器。

特征层面

特征,本质上的研究问题在于,如何泛化地描述图像的像素变化

无论是sift、surf、orb这种基于关键点的特征

还是hog、Haar这种滑动窗口式的特征,都在一定程度上描述了图像的变化

**除此之外,LBP也是作为检测人脸特征的重要工具。**

LBP特征简而言之,就是一个描述像素周围灰度变化的特征。

对任意像素,其近邻有8个像素。

这八个像素和中心像素进行比较,如果大于目标像素则记为1,否则记为0

那么每个像素都可以得到一个8位的二进制数字。

将二进制数字转化为十进制,来表示当前像素特征。

**随着研究进步,LBP特征也不局限于周围八像素的场景**

比如圆形区域取n个像素比较,这就和orb很类似了。

**同时,LBP也可以做金字塔,从而获得更好的尺度不变性**

 模型层面

除了决策树、adaboost之外,

**随机森林也是一个重要的分类器**

随机森林本质上就是多个决策树的集成

但与adaboost不同的是,随机森林每个决策树的训练数据,采用的维度是不一样的

比如第一棵树用的是第一第二维特征,第二课树用的是第2第3维特征等等。

其本质思想,与dropout非常传神,可以说是理论的不谋而合

小结

关于特征、分类器,在前深度学习时代有非常多的研究,每种研究路线各不相同。

学习这些内容,对于理解深度学习时代的各类研究,揣摩其本质思想,有着非常大的帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/475985.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java校招面试】基础知识(一)——Java常用类库

目录 前言一、编程时常用的Java类库1. 异常捕获模块(try-catch-finally, Error, Exception)2. boolean / short / int / long / float / double / char / byte及其对应的引用类型 二、面试时常考的Java类库1. 一切类型的父类Object及其equals / hashCode / toString方法2. 常用…

anaconda安装pytorch的流程

1.查看本机支持的CUDA版本 cmd:nvidia-smi 2.安装本机支持的CUDA版本 CUDA安装教程 GPU, CUDA,cuDNN三者的关系总结 3.使用anaconda创建虚拟环境 3.在anaconda对应的虚拟环境中安装CUDA对应的pytorch版本 anaconda安装pytorch&#xff08;anaconda3&#xff0c;Windows10&am…

idea使用 ( 二 ) 创建java项目

3.创建java项目 3.1.创建普通java项目 3.1.1.打开创建向导 接 2.3.1.创建新的项目 也可以 从菜单选择建立项目 会打开下面的选择界面 3.1.2.不使用模板 3.1.3.设置项目名 Project name : 项目名 Project location : 项目存放的位置 确认创建 3.1.4.关闭tips 将 Dont s…

二叉搜索树【Java】

文章目录 二叉搜索树的性质二叉搜索树的操作遍历查找插入删除 二叉搜索树又称为二叉排序树&#xff0c;是一种具有一定性质的特殊的二叉树&#xff1b; 二叉搜索树的性质 若它的左子树不为空&#xff0c;则左子树上结点的值均小于根节点的值&#xff1b; 若它的右子树不为空&a…

08 Kubernetes应用配置管理

课件 在 Kubernetes 中&#xff0c;secret 是一种用于存储敏感信息的对象。Kubernetes 支持以下三种类型的 secret&#xff1a; Opaque&#xff1a;这是默认的 secret 类型&#xff0c;可以用于存储任何类型的数据&#xff0c;包括字符串、二进制数据等。 Service Account&…

【P2】Jmeter 线程组的并行与串行

一、串行与并行规则 &#xff08;1&#xff09;、测试计划中的执行顺序遵循&#xff1a;setUp 线程组 -> 线程组 -> tearDown 线程组 &#xff08;2&#xff09;、如果将测试计划中的独立运行每个线程组勾选上&#xff0c;则多个线程组串行执行&#xff0c;否则并发执行…

吴恩达 Chatgpt prompt 工程--5.Transforming

探索如何将大型语言模型用于文本转换任务&#xff0c;如语言翻译、拼写和语法检查、音调调整和格式转换。 Setup import openai import osfrom dotenv import load_dotenv, find_dotenv _ load_dotenv(find_dotenv()) # read local .env fileopenai.api_key os.getenv(OPE…

2.6 浮点运算方法和浮点运算器

学习目标&#xff1a; 以下是一些具体的学习目标&#xff1a; 理解浮点数的基本概念和表示方法&#xff0c;包括符号位、指数和尾数。学习浮点数的运算规则和舍入规则&#xff0c;包括加、减、乘、除、开方等。了解浮点数的常见问题和误差&#xff0c;例如舍入误差、溢出、下…

Unity一般打包流程

Unity一般打包流程 通常打包流程主要是通过 Building setting来选择需要打包的场景后出包到指定文件夹位置&#xff0c;也可以采用 [MenuItem("MyMenu/Do Something")]中使用static函数来选择打包路径和打包方式——需要将该脚本放置在 Editor文件夹下 [MenuItem(&…

Vue3源码 第六篇-JavaScript AST transform函数

系列文章目录 Vue3源码 第一篇-总览 Vue3源码 第二篇-Reactive API Vue3源码 第三篇-Vue3是如何实现响应性 Vue3源码 第四篇-Vue3 setup Vue3源码 第五篇-Vue3 模版compile AST生成篇 文章目录 系列文章目录前言一、transform 转换二、traverseNode 遍历节点&#xff0c;trave…

B/S结构系统的会话机制(session)

B/S结构系统的会话机制(session) 文章目录 B/S结构系统的会话机制(session)每博一文案1. session 会话机制的概述2. 什么是 session 的会话3. session 的作用4. session 的实现原理解释5. 补充&#xff1a; Cookie禁用了&#xff0c;session还能找到吗 &#xff1f;6. 总结一下…

PCA学习

前置知识 统计 假设数据集 X ∈ R n m \mathbf{X}\in\mathbb{R}^{n\times m} X∈Rnm,其中 n n n表示样本数量&#xff0c; m m m表示特征个数 均值 X ˉ 1 n e T X 1 n ∑ i 1 n X i \bar{\mathbf{X}} \frac{1}{n}\mathbf{e}^T\mathbf{X} \frac{1}{n} \sum_{i1}^{n}\mat…

[架构之路-178]-《软考-系统分析师》- 分区操作系统(Partition Operating System)概述

目录&#xff1a; 本文概述&#xff1a; 1.1 什么是分区操作系统 1.2 分区操作系统出现背景 1. 前后台系统(Foreground/Background System) 2. 实时操作系统(RTOS) 本文概述&#xff1a; 随着嵌入式系统日趋复杂化以及对安全性要求的不断提高&#xff0c;采用空间隔离、时…

java学习之枚举二

目录 一、enum关键字实现枚举 二、注意事项 一、对Season2进行反编译&#xff08;javap&#xff09; ​编辑 三、练习题 第一题 第二题 一、enum关键字实现枚举 package enum_;public class Enumeration03 {public static void main(String[] args) {System.out.println…

el-upload组件的文件回显功能和添加文件到elupload组件

省流&#xff1a; 先获取这个文件对象&#xff0c;使用handleStart方法添加到组件。 this.$refs.uploadRefName.handleStart(rawfile); 在开发的时候遇到表单里需要上传图片的功能。看了下el-upload组件的使用方法&#xff0c;在修改表单的时候&#xff0c;el-upload组件的回显…

flutter学习之旅(二)

如果不知道怎么安装编写可以查看这篇 创建项目 另一个创建方法 flutter create 项目名热部署 vscode 热部署 vscode很简单&#xff1a;可以通过Debug进行调试 使用flutter查看设备 flutter devices如图所见我现在用的是windows所以&#xff0c;我们检测不到ios因为 我们看…

【Fluent】边界类型总结,什么时候用壁面对(wall-shadow pair)、什么时候用interface?

一、fluent自动生成边界类型的规律 Enclosure是包裹在外面的气体&#xff08;流体&#xff09;&#xff0c;mold是模具&#xff08;固体&#xff09;&#xff0c;sheet是模具上的薄板件&#xff08;固体&#xff09;。 1.1 正向思维 不管是流体域还是固体域&#xff0c;每一…

高度可定制可用于商用目的全流程供应链系统(全部源码)

一、开源项目简介 高度可定制零售供应链中台基础系统&#xff0c;集成零售管理, 电子商务, 供应链管理, 财务管理, 车队管理, 仓库管理, 人员管理, 产品管理, 订单管理, 会员管理, 连锁店管理, 加盟管理, 前端React/Ant Design, 后端Java Spring自有开源框架&#xff0c;全面支…

软件测试之黑盒测试的具体方法详解

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一.基于需求的设计方法二.等价类三.边界值四.判定表4.1 **关系**4.2 如何设计测试用例4.3 实际案例第一步第二步第三步第四步 五.正交排列5.1 什么是正交表5.2 …

shell脚本的判断式

文章目录 shell脚本的判断式利用test命令的测试功能关于某个文件名的【文件类型】判断关于文件的权限检测两个文件之间的比较关于两个整数之间的比较判定字符串的数据多重条件判定例题 利用判断符号[ ]例题 shell脚本的默认变量($0、$1...)例题shift&#xff1a;造成参数变量号…