如何使用OpenCV和Python从图像中检测和提取面部

Haar级联是用于在图像中定位感兴趣对象的对象检测方法。在本教程中,您将使用OpenCV中预先训练的Haar Cascade来检测和提取图像中的面部。

作者选择了开放互联网/言论自由基金作为Write for DOnations计划的一部分进行捐赠。

介绍

图像构成了每天生成的大量数据,这使得处理这些图像的能力变得很重要。 处理图像的一种方法是通过面部检测 人脸检测是图像处理的一个分支,它使用机器学习来检测图像中的人脸。

Haar级联是用于在图像中定位感兴趣对象的对象检测方法。 该算法在大量正样本和负样本上进行训练,其中正样本是包含感兴趣对象的图像。 负样本是可能包含除所需对象之外的任何内容的图像。 一旦经过训练,分类器就可以在任何新图像中定位感兴趣的对象。

在本教程中,您将使用OpenCVPython中预先训练的Haar Cascade模型来检测和提取图像中的面部。 OpenCV是一个用于处理图像的开源编程库。

先决条件

第1步 - 配置本地环境

在开始编写代码之前,首先要创建一个工作空间来保存代码并安装一些依赖项。

使用mkdir命令为项目创建目录:

mkdir face_scrapper

切换到新创建的目录:

cd face_scrapper

接下来,您将为此项目创建一个虚拟环境。 虚拟环境隔离不同的项目,以便不同的依赖关系不会导致任何中断。 创建一个名为face_scrapper的虚拟环境以用于此项目:

python3 -m venv face_scrapper

激活隔离环境:

source face_scrapper/bin/activate

您现在将看到您的提示以虚拟环境的名称为前缀:


现在您已经激活了虚拟环境,您将使用nano或您喜欢的文本编辑器来创建requirements.txt文件。 此文件指示必要的Python依赖项:

nano requirements.txt

接下来,您需要安装三个依赖项来完成本教程:

  • numpynumpy是一个Python库,增加了对大型多维数组的支持。 它还包括大量数学函数,可在阵列上运行。
  • opencv-utils :这是OpenCV的扩展库,包括辅助函数。
  • opencv-python :这是Python使用的核心OpenCV模块。

将以下依赖项添加到文件中:

requirements.txt
numpy 
opencv-utils
opencv-python

保存并关闭文件。

通过将requirements.txt文件传递给Python包管理器pip来安装依赖项。 -r标志指定requirements.txt文件的位置。

pip install -r requirements.txt

在此步骤中,您将为项目设置虚拟环境并安装必要的依赖项。 现在,您已准备好开始编写代码,以便在下一步中检测输入图像中的面部。

第2步 - 编写并运行Face Detector脚本

在本节中,您将编写将图像作为输入并返回两件事的代码:

  • 在输入图像中找到的面数。
  • 围绕每个检测到的面部的矩形图的新图像。

首先创建一个新文件来保存代码:

nano app.py

在这个新文件中,首先通过导入必要的库来开始编写代码。 您将在此处导入两个模块: cv2sys cv2模块将OpenCV库导入到程序中, sys导入代码将使用的常见Python函数,例如argv

app.py
import cv2
import sys

接下来,您将指定输入图像将在运行时作为参数传递给脚本。 Pythonic读取第一个参数的方法是将sys.argv[1]函数返回的值赋给变量:

app.py
...
imagePath = sys.argv[1]

图像处理的常见做法是首先将输入图像转换为灰度。 这是因为与颜色相反,检测亮度通常会在物体检测中产生更好的结果。 添加以下代码以将输入图像作为参数并将其转换为灰度:

app.py
...
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

.imread()函数获取输入图像,该图像作为参数传递给脚本,并将其转换为OpenCV对象。 接下来,OpenCV的.cvtColor()函数将输入图像对象转换为灰度对象。

现在您已添加了加载图像的代码,您将添加检测指定图像中的面的代码:

app.py
...
faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.3,
        minNeighbors=3,
        minSize=(30, 30)
) 

print("Found {0} Faces!".format(len(faces)))

此代码将创建一个faceCascade对象,该对象将使用cv2.CascadeClassifier方法加载Haar Cascade文件。 这允许Python和您的代码使用Haar Cascade。

接下来,代码在faceCascade对象上应用OpenCV的.detectMultiScale()方法。 这将为图像中的所有检测到的面生成一个矩形列表 矩形列表是来自图像的像素位置的集合,以Rect(x,y,w,h)

以下是代码使用的其他参数的摘要:

  • gray :这指定使用先前加载的OpenCV灰度图像对象。
  • scaleFactor :此参数指定在每个图像比例下缩小图像大小的速率。 您的模型在训练期间具有固定的比例,因此可以按比例缩小输入图像以改进检测。 此过程在达到由maxSizeminSize定义的阈值限制后停止。
  • minNeighbors :此参数指定每个候选矩形应保留多少个邻居或检测。 较高的值可能导致较少的误报,但过高的值可以消除真正的正面。
  • minSize :这允许您定义以像素为单位测量的最小可能对象大小。 小于此参数的对象将被忽略。

在生成矩形列表之后,然后使用len函数对面进行计数。 然后,在运行脚本后,检测到的面数将作为输出返回。

接下来,您将使用OpenCV的.rectangle()方法在检测到的面周围绘制一个矩形:

app.py
...
for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)

此代码使用for循环遍历从faceCascade.detectMultiScale方法返回的每个检测到的对象的像素位置列表。 rectangle方法将采用四个参数:

  • image告诉代码在原始输入图像上绘制矩形。
  • (x,y), (x+w, y+h)是检测到的对象的四个像素位置。 rectangle将使用这些来定位和绘制输入图像中检测到的对象周围的矩形。
  • (0, 255, 0)是形状的颜色。 该参数作为BGR的元组传递。 例如,您将使用(255, 0, 0)蓝色。 在这种情况下我们使用绿色。
  • 2是以像素为单位测量的线的粗细。

既然您已经添加了绘制矩形的代码,请使用OpenCV的.imwrite()方法将新图像作为faces_detected.jpg写入本地文件系统。 如果写入成功,此方法将返回true如果无法写入新图像,则返回false

app.py
...
status = cv2.imwrite('faces_detected.jpg', image)

最后,添加此代码以打印返回.imwrite()函数的truefalse状态到控制台。 这将告诉您在运行脚本后写入是否成功。

app.py
...
print ("Image faces_detected.jpg written to filesystem: ",status)

完成的文件如下所示:

app.py
import cv2
import sys

imagePath = sys.argv[1]

image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
faces = faceCascade.detectMultiScale(
    gray,
    scaleFactor=1.3,
    minNeighbors=3,
    minSize=(30, 30)
)

print("[INFO] Found {0} Faces!".format(len(faces)))

for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

status = cv2.imwrite('faces_detected.jpg', image)
print("[INFO] Image faces_detected.jpg written to filesystem: ", status)

确认所有内容都输入正确后,保存并关闭文件。

注意:此代码源自公开的OpenCV文档

您的代码已完成,您已准备好运行该脚本。

第3步 - 运行脚本

在此步骤中,您将使用图像来测试脚本。 当您找到要用于测试的图像时,请将其保存在与app.py脚本相同的目录中。 本教程将使用以下图像:

输入图像的四个人看着手机

如果要使用相同的映像进行测试,请使用以下命令进行下载:

curl -O https://www.howtoing.com/wp-content/uploads/articles/CART-63965/people_with_phones.png

获得映像以测试脚本后,运行脚本并提供映像路径作为参数:

python app.py path/to/input_image

脚本运行完毕后,您将收到如下输出:

[INFO] Found 4 Faces!
[INFO] Image faces_detected.jpg written to filesystem:  True

true输出告诉您已更新的映像已成功写入文件系统。 在本地计算机上打开映像以查看新文件的更改:

检测到面部的输出图像

您应该看到您的脚本在输入图像中检测到四个面并绘制矩形以标记它们。 在下一步中,您将使用像素位置从图像中提取面。

第4步 - 提取面部并在本地保存(可选)

在上一步中,您编写的代码使用OpenCV和Haar Cascade来检测和绘制图像中各个面的矩形。 在本节中,您将修改代码以将检测到的面从图像中提取到自己的文件中。

首先使用文本编辑器重新打开app.py文件:

nano app.py

接下来,在cv2.rectangle行下添加突出显示的行:

app.py
...
for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
    roi_color = image[y:y + h, x:x + w] 
    print("[INFO] Object found. Saving locally.") 
    cv2.imwrite(str(w) + str(h) + '_faces.jpg', roi_color) 
...

roi_color对象绘制原始输入图像上的faces列表中的像素位置。 xyhw变量是从faceCascade.detectMultiScale方法检测到的每个对象的像素位置。 然后代码打印输出,说明找到了一个对象并将在本地保存。

完成后,代码使用cv2.imwrite方法将绘图保存为新图像。 它将绘图的宽度和高度附加到要写入的图像的名称。 如果检测到多个面,这将使名称保持唯一。

更新后的app.py脚本如下所示:

app.py
import cv2
import sys

imagePath = sys.argv[1]

image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
faces = faceCascade.detectMultiScale(
    gray,
    scaleFactor=1.3,
    minNeighbors=3,
    minSize=(30, 30)
)

print("[INFO] Found {0} Faces.".format(len(faces)))

for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
    roi_color = image[y:y + h, x:x + w]
    print("[INFO] Object found. Saving locally.")
    cv2.imwrite(str(w) + str(h) + '_faces.jpg', roi_color)

status = cv2.imwrite('faces_detected.jpg', image)
print("[INFO] Image faces_detected.jpg written to filesystem: ", status)

总而言之,更新的代码使用像素位置将图像中的面提取到新文件中。 完成代码更新后,保存并关闭文件。

现在您已经更新了代码,您可以再次运行该脚本:

python app.py path/to/image

完成脚本处理图像后,您将看到类似的输出:

[INFO] Found 4 Faces.
[INFO] Object found. Saving locally.
[INFO] Object found. Saving locally.
[INFO] Object found. Saving locally.
[INFO] Object found. Saving locally.
[INFO] Image faces_detected.jpg written to file-system: True

根据样本图像中的面数,您可能会看到更多或更少的输出。

在执行脚本后查看工作目录的内容,您将看到输入图像中找到的所有面的头像的文件。

目录列表

现在,您将看到从工作目录中收集的输入图像中提取的头像:

提取的面孔

在此步骤中,您修改了脚本以从输入图像中提取检测到的对象并将其保存在本地。

结论

在本教程中,您编写了一个脚本,该脚本使用OpenCV和Python来检测,计算和提取输入图像中的面。 您可以使用OpenCV库中不同的预先训练的Haar Cascade来更新此脚本以检测不同的对象,或者您可以学习如何训练自己的 Haar Cascade。