#-*-coding:utf8-*-
import os
import cv2
import numpy as np
#输入一个文件路径,对其下的每个文件夹下的图片读取,并对每个文件夹给一个不同的Label
#返回一个img的list,返回一个对应label的list,返回一下有几个文件夹(有几种label)
def read_file(path):
img_list = []
label_list = []
dir_counter = 0
IMG_SIZE = 128
#对路径下的所有子文件夹中的所有jpg文件进行读取并存入到一个list中
for child_dir in os.listdir(path):
child_path = os.path.join(path, child_dir)
for dir_image in os.listdir(child_path):
if dir_image.endswith('jpg'):
img = cv2.imread(os.path.join(child_path, dir_image))
resized_img = cv2.resize(img, (IMG_SIZE, IMG_SIZE))
recolored_img = cv2.cvtColor(resized_img,cv2.COLOR_BGR2GRAY)
img_list.append(recolored_img)
label_list.append(dir_counter)
dir_counter += 1
# 返回的img_list转成了 np.array的格式
img_list = np.array(img_list)
return img_list,label_list,dir_counter
#读取训练数据集的文件夹,把他们的名字返回给一个list
def read_name_list(path):
name_list = []
for child_dir in os.listdir(path):
name_list.append(child_dir)
return name_list
if __name__ == '__main__':
img_list,label_lsit,counter = read_file('L:\人脸识别进阶版--人脸检测\input_img')
print(counter)
导入必需的模块
在开始实际编码之前,我们需要导入所需的编码模块。 所以让我们先导入它们。
cv2:是Python的OpenCV模块,我们将用它来进行人脸检测和人脸识别。
os:我们将使用这个Python模块来读取我们的培训目录和文件名。
numpy:我们将使用此模块将Python列表转换为numpy数组,因为OpenCV人脸识别器接受numpy数组。
#导入OpenCV模块
import cv2
#导入os模块用于读取训练数据目录和路径
import os
# 导入numpy将python列表转换为numpy数组,OpenCV面部识别器需要它
import numpy as np
训练数据
训练中使用的图像越多越好。 通常很多图像用于训练面部识别器,以便它可以学习同一个人的不同外观,例如戴眼镜,不戴眼镜,笑,伤心,快乐,哭泣,留着胡子,没有胡子等。 简单的教程我们将为每个人使用500张图片。
所以我们的训练数据由共10人组成,每个人有500张图像。 所有培训数据都在培训数据文件夹内。 训练数据文件夹包含每个人的一个文件夹,并且每个文件夹以姓名(例如zhangsan,lisi)命名,其中标签实际上是分配给该人的整数标签。 例如,名为s1的文件夹意味着该文件夹包含人员1的图像。培训数据的目录结构树如下所示:
training-data
|--------------zhangsan
| |-- 1.jpg
| |-- …
| |-- 12.jpg
|-------------- lisi
| |-- 1.jpg
| |-- …
| |-- 12.jpg
测试数据文件夹包含我们将用于在成功培训完成后测试人脸识别器的图像
由于OpenCV人脸识别器接受标签为整数,因此我们需要定义整数标签和人物实际名称之间的映射,所以我定义了人员整数标签及其各自名称的映射。
准备训练数据
您可能想知道为什么要进行数据准备,对吗? 那么,OpenCV人脸识别器接受特定格式的数据。 它接受两个矢量,一个矢量是所有人的脸部,第二个矢量是每个脸部的整数标签,因此在处理脸部时,脸部识别器会知道该脸部属于哪个人。
例如,如果我们有两个人和两个图像为每个人。
zhangsan lisi
img1 img1
img2 img2
然后,准备数据步骤将生成以下面和标签向量。
FACES LABELS
zhangsan_img1_face 1
zhangsan_img2_face 1
lisi_img1_face 2
lisi_img2_face 2
准备数据步骤可以进一步分为以下子步骤。
1、阅读培训数据文件夹中提供的所有主题/人员的文件夹名称。 例如,在本教程中,我们有文件夹名称:zhangsan,lisi。
2、对于每个主题,提取标签号码。 你还记得我们的文件夹有一个特殊的命名约定吗? 文件夹名称遵循格式xingmin,是一个拼音,代表我们已分配给该主题的标签。 因此,例如,文件夹名称zhangsan表示主题具有zhangsan,lisi表示主题标签为lisi等。 将在此步骤中提取的标签分配给在下一步中检测到的每个面部。
3、阅读主题的所有图像,从每张图像中检测脸部。
4、将添加到标签矢量中的具有相应主题标签(在上述步骤中提取)的每个脸部添加到脸部矢量。
#使用OpenCV用来检测脸部的函数
def detect_face(img):
#将测试图像转换为灰度图像,因为opencv人脸检测器需要灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#加载OpenCV人脸检测器,我正在使用的是快速的LBP
#还有一个更准确但缓慢的Haar分类器
face_cascade = cv2.CascadeClassifier('opencv-files/lbpcascade_frontalface.xml')
#让我们检测多尺度(一些图像可能比其他图像更接近相机)图像
#结果是一张脸的列表
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5);
#如果未检测到面部,则返回原始图像
if (len(faces) == 0):
return None, None
#假设只有一张脸,
#提取面部区域
(x, y, w, h) = faces[0]
#只返回图像的正面部分
return gray[y:y+w, x:x+h], faces[0]
我正在使用OpenCV的LBP人脸检测器。 在第4行,我将图像转换为灰度,因为OpenCV中的大多数操作都是以灰度进行的,然后在第8行使用cv2.CascadeClassifier类加载LBP人脸检测器。 在第12行之后,我使用cv2.CascadeClassifier类’detectMultiScale方法来检测图像中的所有面部。 在第20行中,从检测到的脸部我只挑选第一张脸部,因为在一张图像中只有一张脸部(假设只有一张醒目的脸部)。 由于detectMultiScale方法返回的面实际上是矩形(x,y,宽度,高度),而不是实际的面部图像,所以我们必须从主图像中提取面部图像区域。 所以在第23行我从灰色图像中提取人脸区域并返回人脸图像区域和人脸矩形。
#该功能将读取所有人的训练图像,从每个图像检测人脸
#并将返回两个完全相同大小的列表,一个列表
每张脸的脸部和另一列标签
def prepare_training_data(data_folder_path):
#------STEP-1--------
#获取数据文件夹中的目录(每个主题的一个目录)
dirs = os.listdir(data_folder_path)
#列表来保存所有主题的面孔
faces = []
#列表以保存所有主题的标签
labels = []
#让我们浏览每个目录并阅读其中的图像
for dir_name in dirs:
#我们的主题目录以字母's'开头
#如果有的话,忽略任何不相关的目录
if not dir_name.startswith("s"):
continue;
#------STEP-2--------
#从dir_name中提取主题的标签号
#目录名称格式= slabel
#,所以从dir_name中删除字母''会给我们标签
label = int(dir_name.replace("s", ""))
#建立包含当前主题主题图像的目录路径
#sample subject_dir_path = "training-data/s1"
subject_dir_path = data_folder_path + "/" + dir_name
#获取给定主题目录内的图像名称
subject_images_names = os.listdir(subject_dir_path)
#------STEP-3--------
#浏览每个图片的名称,阅读图片,
#检测脸部并将脸部添加到脸部列表
for image_name in subject_images_names:
#忽略.DS_Store之类的系统文件
if image_name.startswith("."):
continue;
#建立图像路径
#sample image path = training-data/s1/1.pgm
image_path = subject_dir_path + "/" + image_name
#阅读图像
image = cv2.imread(image_path)
#显示图像窗口以显示图像
cv2.imshow("Training on image...", image)
cv2.waitKey(100)
#侦测脸部
face, rect = detect_face(image)
#------STEP-4--------
#为了本教程的目的
#我们将忽略未检测到的脸部
if face is not None:
#将脸添加到脸部列表
faces.append(face)
#为这张脸添加标签
labels.append(label)
cv2.destroyAllWindows()
cv2.waitKey(1)
cv2.destroyAllWindows()
return faces, labels
我已经定义了一个函数,它将存储培训主题文件夹的路径作为参数。 该功能遵循上述的4个准备数据子步骤。
(step–1)在第8行,我使用os.listdir方法读取存储在传递给函数的路径上的所有文件夹的名称作为参数。 在第10-13行,我定义了标签并面向矢量。
(step–2)之后,我遍历所有主题的文件夹名称以及第27行中每个主题的文件夹名称,我将提取标签信息。 由于文件夹名称遵循sLabel命名约定,所以从文件夹名称中删除字母将给我们分配给该主题的标签。
(step–3)在第34行,我读取了当前被摄体的所有图像名称,并且在第39-66行中我逐一浏览了这些图像。 在53-54行,我使用OpenCV的imshow(window_title,image)和OpenCV的waitKey(interval)方法来显示当前正在传播的图像。 waitKey(interval)方法将代码流暂停给定的时间间隔(毫秒),我以100ms的间隔使用它,以便我们可以查看100ms的图像窗口。 在第57行,我从当前正在遍历的图像中检测出脸部。
(step–4)在第62-66行,我将检测到的面和标签添加到它们各自的向量中。
但是一个函数只能在需要准备的某些数据上调用它时才能做任何事情,对吗? 别担心,我有两张脸的数据。 我相信你至少会认出其中的一个!
让我们在这些图像上调用这个函数来准备数据来训练我们的人脸识别器。 下面是一个简单的代码来做到这一点。
#让我们先准备好我们的训练数据
#数据将在两个相同大小的列表中
#一个列表将包含所有的面孔
#数据将在两个相同大小的列表中
print("Preparing data...")
faces, labels = prepare_training_data("training-data")
print("Data prepared")
#打印总面和标签
print("Total faces: ", len(faces))
print("Total labels: ", len(labels))
复制代码
Preparing data...
Data prepared
Total faces: 23
Total labels: 23