「Python」Python基于OpenCV实现图片中人脸识别

背景

最近在和一家科技设计公司合作,需要从大量图片中挑选出含有人物的图片。因此考虑使用 OpenCV 进行人脸识别,有检测到人脸的图片再进额外的处理

介绍

OpenCV是一个跨平台的计算机视觉库,支持Windows、Linux、macOS等操作系统。它提供了丰富的接口和函数,包括图像处理、计算机视觉、模式识别、机器学习等领域,可用于多种计算机视觉应用,如人脸识别、行人检测、运动跟踪、医学图像处理、目标识别等。

Github仓库:https://github.com/opencv/opencv

安装

1
pip install opencv-python

安装完成后,我们就可以开始使用OpenCV在Python中实现各种计算机视觉任务了。

图像处理

使用OpenCV读取和显示图像

1
2
3
4
5
6
7
8
9
10
11
import cv2

# 读取图像
img = cv2.imread('image.jpg')

# 显示图像。接受两个参数:窗口名称(在这里是"Image")和要显示的图像。
cv2.imshow('Image', img)
# 等待按键输入的函数。它会暂停程序执行,直到用户按下任意键。括号中的参数0表示无限等待用户的键盘输入。
cv2.waitKey(0)
# 销毁所有创建的窗口的函数,会关闭显示图像的窗口。
cv2.destroyAllWindows()

除了读取和显示图像外,OpenCV还提供了许多其他图像处理功能,如调整大小、旋转、裁剪、灰度化、边缘检测、过滤器、形态学运算等。

例如,下面的代码实现了将图像缩小一半,并进行灰度化和Canny边缘检测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import cv2

# 读取图像
img = cv2.imread('image.jpg')

# 缩小图像。将图像缩小到原来尺寸的50%。这里通过设置fx=0.5和fy=0.5来实现等比例缩小。
resized_img = cv2.resize(img, (0, 0), fx=0.5, fy=0.5)

# 灰度化图像,将缩小后的彩色图像转换为灰度图像。这里使用cv2.COLOR_BGR2GRAY参数表示从BGR颜色空间转换为灰度颜色空间。
gray = cv2.cvtColor(resized_img, cv2.COLOR_BGR2GRAY)

# 使用Canny边缘检测算法,对灰度图像进行边缘检测。参数100和200分别表示边缘梯度的阈值
edges = cv2.Canny(gray, 100, 200)

# 显示边缘检测结果
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

人脸检测

OpenCV提供了多种方法来实现人脸检测,其中一种常用的方法是使用Haar 特征 (Haar-like Features) Cascade分类器。下面的代码演示了如何使用OpenCV在Python中实现人脸检测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import cv2

# 加载Haar Cascade分类器模型
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

# 读取图像
img = cv2.imread('image.jpg')

# 将图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 调用Haar Cascade分类器检测人脸
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)

# 在原始图像中绘制人脸框
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

# 显示图像
cv2.imshow('Faces', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

这个分类器模型可以在

OpenCV 的 Github 仓库中的 data/haarcascades 目录中找到并且下载 opencv/data/haarcascades

可以看到有

  • haarcascade_frontalface _default.xml 人脸检测级联分类器的默认模型。它对于一般情况下的人脸检测具有较好的效果,但在某些复杂场景下可能会有一些限制。

  • haarcascade_frontalface_alt.xml 用于检测正面人脸。它在一般情况下具有较好的检测性能,并且速度相对较快。

  • haarcascade_ frontalface_alt2.xml alt 的改进版本,对于一些复杂场景或具有不同角度的人脸也能进行较好的检测。

  • haarcascade_ frontalface_alt_tree.xml 在 alt 的基础上使用了更复杂的级联结构,提高了人脸检测的准确性和鲁棒性。它通常对于复杂背景和表情变化较大的人脸具有更好的效果,但相应的检测速度可能会稍慢一些。

通常我们使用 alt2 就可以取得很好的效果

使用

我们可以看到 detectMultiScale 函数中有需要传入 scaleFactorminNeighbors 参数。

scaleFactor 用于指定在图像金字塔中采用多大的步长搜索人脸。它的值越小,搜索所花费的时间和检测精度都会增加。一般情况下,值在 1.01 到 1.5 之间逐步增加可以获得比较理想的效果。

minNeighbors 是指人脸矩形相邻两个矩形之间的最小有效距离,也就是当检测到一个人脸时,如果该矩形周围有比它更小的矩形,则认为该矩形并非人脸矩形,会被过滤掉。这个参数的值越大,过滤掉的矩形就越多,同时漏检率也会增加,而值越小,检出的矩形较多,但误检率也会增加。一般情况下,建议在 3-6 之间取值。

我们也可以通过跟踪当前检测到的人脸数量和大小与预期值之间的差距,然后根据差距来自动调整检测器的参数。例如,当检测到的人脸数量过少时,可以尝试减小 minNeighbors 参数或增加 scaleFactor 参数,以扩大检测范围;而当检测到的人脸数量过多时,则可以尝试增加 minSize 参数,以过滤掉小尺寸的误检测。

动态调整参数可以使用反馈控制的方法。具体来说,可以根据当前检测到人脸的数量和大小与预期值之间的差距,自动调整检测器的参数,以达到更好的检测效果。

下面是完整代码,请留意 while True 部分代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import cv2


def dynamic_face_detection(img_path, target_count=1, min_width=50, min_height=50, scale_factor=1.1, min_neighbors=3):
"""
动态人脸检测函数
:param img_path: 输入图像的文件路径
:param target_count: 目标检测到的人脸数量,默认为4
:param min_width: 最小人脸宽度,默认为50
:param min_height: 最小人脸高度,默认为50
:param scale_factor: 窗口缩放比例因子,默认为1.1
:param min_neighbors: 每个候选矩形应该保留的邻居数,默认为3
:return: faces_vertices 检测到的人脸顶点坐标数组,格式为[((x1, y1), (x2, y2), (x3, y3), (x4, y4)), ...]
"""
# 读取输入图像
img = cv2.imread(img_path)

# 转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 初始化人脸检测器
face_cascade = cv2.CascadeClassifier('./models/cv/haarcascades/haarcascade_frontalface_alt2.xml')

# 初始化参数
while True:
# 调用人脸检测器
faces = face_cascade.detectMultiScale(gray, scaleFactor=scale_factor, minNeighbors=min_neighbors, minSize=(min_width, min_height))

# 检测到目标数量,终止算法
if len(faces) == target_count:
break

# 计算当前平均人脸大小
total_width = sum([f[2] for f in faces])
total_height = sum([f[3] for f in faces])
avg_width = total_width / len(faces)
avg_height = total_height / len(faces)

# 针对检测结果进行参数调整
if len(faces) < target_count:
# 当检测到人脸数量过少时,减小 min_neighbors 或者增加 scaleFactor
if min_neighbors > 1:
min_neighbors -= 1
else:
scale_factor += 0.1
elif len(faces) > target_count:
# 当检测到人脸数量过多时,增加 min_width 或者 min_height
min_width = int(avg_width * 1.1)
min_height = int(avg_height * 1.1)

# 避免参数过大或过小
scale_factor = max(1, min(scale_factor, 2))
min_neighbors = max(1, min(min_neighbors, 10))
min_width = max(20, min(min_width, 200))
min_height = max(20, min(min_height, 200))

faces_vertices = []
# 在图像中绘制检测到的人脸矩形
for (x, y, w, h) in faces:
# 绘制人脸矩形
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 绘制顶点像素位置
# 第一个参数是输入图像,第二个参数是要添加的文本,第三个参数是文本要显示的位置,第四个参数是字体,第五个参数是字体大小,第六个参数是颜色,第七个参数是文本厚度
cv2.putText(img, f"({x}, {y})", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 2)
cv2.putText(img, f"({x + w}, {y})", (x + w, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 2)
cv2.putText(img, f"({x}, {y + h})", (x, y + h + 40), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 2)
cv2.putText(img, f"({x + w}, {y + h})", (x + w, y + h + 40), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 2)

# 添加顶点到数组中
top_left = (x, y)
top_right = (x + w, y)
bottom_right = (x + w, y + h)
bottom_left = (x, y + h)
vertices = (top_left, top_right, bottom_right, bottom_left)
faces_vertices.append(vertices)

# 预览检测结果
cv2.imshow('Dynamic Face Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 返回检测到的人脸顶点坐标数组
return faces_vertices


if __name__ == '__main__':
url = './img/img2.png'
dynamic_face_detection(url)

总结

通过本文,我们了解了OpenCV的功能及应用领域、如何在Python中使用OpenCV实现图像处理和人脸检测等计算机视觉任务。OpenCV还支持多种其他计算机视觉操作,例如目标跟踪、形态学运算、特征提取和机器学习等。

需要注意的是,在实际使用过程中,应根据具体要求和数据集选用不同的算法和模型,并对其进行调优和优化,以充分发挥OpenCV的性能和应用优势。