经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
人脸识别实战之Opencv+SVM实现人脸识别
来源:jb51  时间:2021/12/17 11:25:13  对本文有异议

前言

在本文中,您将学习如何使用 OpenCV 进行人脸识别。文章分三部分介绍:

第一,将首先执行人脸检测,使用深度学习从每个人脸中提取人脸量化为128位的向量。

第二, 在嵌入基础上使用支持向量机(SVM)训练人脸识别模型。

第三,最后使用 OpenCV 识别图像和视频流中的人脸。

项目结构

编码

新建face_embeddings.py脚本,写入如下代码:

  1. # import the necessary packages
  2. import numpy as np
  3. import pickle
  4. import cv2
  5. import os
  6. import os

导入需要的包。然后定义几个函数:

  1. def list_images(basePath, contains=None):
  2. # return the set of files that are valid
  3. return list_files(basePath, validExts=image_types, contains=contains)
  4.  
  5.  
  6. def list_files(basePath, validExts=None, contains=None):
  7. # loop over the directory structure
  8. for (rootDir, dirNames, filenames) in os.walk(basePath):
  9. # loop over the filenames in the current directory
  10. for filename in filenames:
  11. # if the contains string is not none and the filename does not contain
  12. # the supplied string, then ignore the file
  13. if contains is not None and filename.find(contains) == -1:
  14. continue
  15. # determine the file extension of the current file
  16. ext = filename[filename.rfind("."):].lower()
  17. # check to see if the file is an image and should be processed
  18. if validExts is None or ext.endswith(validExts):
  19. # construct the path to the image and yield it
  20. imagePath = os.path.join(rootDir, filename)
  21. yield imagePath
  22.  
  23. def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
  24. dim = None
  25. (h, w) = image.shape[:2]
  26. # 如果高和宽为None则直接返回
  27. if width is None and height is None:
  28. return image
  29. # 检查宽是否是None
  30. if width is None:
  31. # 计算高度的比例并并按照比例计算宽度
  32. r = height / float(h)
  33. dim = (int(w * r), height)
  34. # 高为None
  35. else:
  36. # 计算宽度比例,并计算高度
  37. r = width / float(w)
  38. dim = (width, int(h * r))
  39. resized = cv2.resize(image, dim, interpolation=inter)
  40. # return the resized image
  41. return resized

list_images函数,读取数据集文件夹下面的图片。

resize函数,等比例resize图片。接下来定义一些变量:

  1. dataset_path='dataset'
  2. embeddings_path='output/embeddings.pickle'
  3. detector_path='face_dete_model'
  4. embedding_model='nn4.small2.v1.t7'
  5. confidence_low=0.5

dataset_path:数据集路径

embeddings_path:输出编码文件的路径

detector_path:人脸检测模型的路径

embedding_model:编码模型

confidence_low:最低的置信度。

接下来就是代码的最重要的部分:

  1. print("loading face detector...")
  2. protoPath = os.path.sep.join([detector_path, "deploy.proto.txt"])
  3. modelPath = os.path.sep.join([detector_path,"res10_300x300_ssd_iter_140000_fp16.caffemodel"])
  4. detector = cv2.dnn.readNetFromCaffe(protoPath, modelPath)
  5. # 加载序列化的人脸编码模型
  6. print("loading face recognizer...")
  7. embedder = cv2.dnn.readNetFromTorch(embedding_model)
  8. # 获取数据集中输入图像的路径
  9. print("quantifying faces...")
  10. imagePaths = list(list_images(dataset_path))
  11. # 初始化我们提取的面部编码列表和相应的人名
  12. knownEmbeddings = []
  13. knownNames = []
  14. # 初始化处理的人脸总数
  15. total = 0
  16. # loop over the image paths
  17. for (i, imagePath) in enumerate(imagePaths):
  18. # extract the person name from the image path
  19. print("processing image {}/{}".format(i + 1,len(imagePaths)))
  20. name = imagePath.split(os.path.sep)[-2]
  21. # 加载图像,将其调整为宽度为 600 像素(同时保持纵横比),然后抓取图像尺寸
  22. image = cv2.imread(imagePath)
  23. image = resize(image, width=600)
  24. (h, w) = image.shape[:2]
  25. # 从图像构建一个 blob
  26. imageBlob = cv2.dnn.blobFromImage(
  27. cv2.resize(image, (300, 300)), 1.0, (300, 300),
  28. (104.0, 177.0, 123.0), swapRB=False, crop=False)
  29. # 使用 OpenCV 的基于深度学习的人脸检测器来定位输入图像中的人脸
  30. detector.setInput(imageBlob)
  31. detections = detector.forward()
  32. # ensure at least one face was found
  33. if len(detections) > 0:
  34. # 假设每个图像只有一张脸,所以找到概率最大的边界框
  35. i = np.argmax(detections[0, 0, :, 2])
  36. confidence = detections[0, 0, i, 2]
  37. # 确保最大概率的检测也意味着我们的最小概率测试(从而帮助过滤掉弱检测)
  38. if confidence > confidence_low:
  39. # 计算人脸边界框的 (x, y) 坐标
  40. box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
  41. (startX, startY, endX, endY) = box.astype("int")
  42. # 提取人脸ROI并抓取ROI维度
  43. face = image[startY:endY, startX:endX]
  44. (fH, fW) = face.shape[:2]
  45. # 确保人脸宽度和高度足够大
  46. if fW < 20 or fH < 20:
  47. continue
  48. # 为人脸 ROI 构造一个 blob,然后将 blob 通过我们的人脸嵌入模型来获得人脸的 128-d 量化
  49. faceBlob = cv2.dnn.blobFromImage(face, 1.0 / 255,
  50. (96, 96), (0, 0, 0), swapRB=True, crop=False)
  51. embedder.setInput(faceBlob)
  52. vec = embedder.forward()
  53. # 将人名+对应的人脸嵌入添加到各自的列表中
  54. knownNames.append(name)
  55. knownEmbeddings.append(vec.flatten())
  56. total += 1
  57. # 保存编码文件
  58. print("serializing {} encodings...".format(total))
  59. data = {"embeddings": knownEmbeddings, "names": knownNames}
  60. f = open(embeddings_path, "wb")
  61. f.write(pickle.dumps(data))
  62. f.close()

加载人脸检测器和编码器:

检测器:使用基于Caffe的DL人脸检测器来定位图像中的人脸。

编码器:模型基于Torch,负责通过深度学习特征提取来提取人脸编码。

接下来,让我们抓取图像路径并执行初始化。

遍历 imagePaths。从路径中提取人名。

构造了一个 blob。

然后,通过将 imageBlob 通过检测器网络来检测图像中的人脸。

检测列表包含定位图像中人脸的概率和坐标。

假设我们至少有一个检测,将进入 if 语句的主体。

假设图像中只有一张脸,因此提取具有最高置信度的检测并检查以确保置信度满足用于过滤弱检测的最小概率阈值。

假设已经达到了这个阈值,提取面部 ROI 并抓取/检查尺寸以确保面部 ROI 足够大。

然后,我们将利用编码器 并提取人脸编码。

继续构建另一个 blob。

随后,将 faceBlob 通过编码器 。 这会生成一个 128 维向量 (vec) 来描述面部。

然后我们简单地将名称和嵌入 vec 分别添加到 knownNames 和 knownEmbeddings 中。

继续循环图像、检测人脸并为数据集中的每个图像提取人脸编码的过程。

循环结束后剩下的就是将数据转储到磁盘。

运行结果:

loading face detector...

loading face recognizer...

quantifying faces...

processing image 1/19

processing image 2/19

processing image 3/19

processing image 4/19

processing image 5/19

processing image 6/19

processing image 7/19

processing image 8/19

processing image 9/19

processing image 10/19

processing image 11/19

processing image 12/19

processing image 13/19

processing image 14/19

processing image 15/19

processing image 16/19

processing image 17/19

processing image 18/19

processing image 19/19

serializing 19 encodings...

Process finished with exit code 0

训练人脸识别模型

已经为每张脸提取了 128 维编码——但是我们如何根据这些嵌入来识别一个人呢?

答案是我们需要在嵌入之上训练一个“标准”机器学习模型(例如 SVM、k-NN 分类器、随机森林等)。

今天我们使用SVM实现

打开 train_face.py 文件并插入以下代码:

  1. from sklearn.preprocessing import LabelEncoder
  2. from sklearn.svm import SVC
  3. import pickle
  4.  
  5. embeddings_path='output/embeddings.pickle'
  6. recognizer_path='output/recognizer.pickle'
  7. lable_path='output/le.pickle'
  8. # 加载编码模型
  9. print("[INFO] loading face embeddings...")
  10. data = pickle.loads(open(embeddings_path, "rb").read())
  11.  
  12. # 给label编码
  13. print("[INFO] encoding labels...")
  14. le = LabelEncoder()
  15. labels = le.fit_transform(data["names"])
  16. # 训练用于接受人脸 128-d 嵌入的模型,然后产生实际的人脸识别
  17. recognizer = SVC(C=1.0, kernel="linear", probability=True)
  18. recognizer.fit(data["embeddings"], labels)
  19. # 保存模型
  20. f = open(recognizer_path, "wb")
  21. f.write(pickle.dumps(recognizer))
  22. f.close()
  23. # 保存lable
  24. f = open(lable_path, "wb")
  25. f.write(pickle.dumps(le))
  26. f.close()

导入包和模块。 我们将使用 scikit-learn 的支持向量机 (SVM) 实现,这是一种常见的机器学习模型。

定义变量。

  • embeddings_path:序列化编码。
  • recognizer_path:这将是我们识别人脸的输出模型。 它基于 SVM。
  • lable_path:标签编码器输出文件路径

加载编码。

然后初始化 scikit-learn LabelEncoder 并编码名称标签。

训练模型。本文使用的是线性支持向量机 (SVM),但如果您愿意,您可以尝试使用其他机器学习模型进行试验。

训练模型后,我们将模型和标签编码器保存到电脑上。

运行train_face.py 脚本。

识别图像中的人脸

新建脚本文件recognize_face.py,插入一下代码:

  1. import numpy as np
  2. import pickle
  3. import cv2
  4. import os

导入包,然后我们需要新增一个resize方法。

  1. def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
  2. dim = None
  3. (h, w) = image.shape[:2]
  4. # 如果高和宽为None则直接返回
  5. if width is None and height is None:
  6. return image
  7. # 检查宽是否是None
  8. if width is None:
  9. # 计算高度的比例并并按照比例计算宽度
  10. r = height / float(h)
  11. dim = (int(w * r), height)
  12. # 高为None
  13. else:
  14. # 计算宽度比例,并计算高度
  15. r = width / float(w)
  16. dim = (width, int(h * r))
  17. resized = cv2.resize(image, dim, interpolation=inter)
  18. # return the resized image
  19. return resized

等比例resize图像,定义变量:

  1. image_path = '11.jpg'
  2. detector_path = 'face_dete_model'
  3. embedding_path = 'nn4.small2.v1.t7'
  4. recognizer_path = 'output/recognizer.pickle'
  5. label_path = 'output/le.pickle'
  6. confidence_low = 0.5

这六个变量的含义如下:

  1. image_path :输入图像的路径。
  2. detector_path:OpenCV 深度学习人脸检测器的路径。 使用这个模型来检测人脸 ROI 在图像中的位置。
  3. embedding_path : OpenCV 深度学习人脸编码模型的路径。 我们将使用这个模型从人脸 ROI 中提取 128 维人脸嵌入——然后将把数据输入到识别器中。
  4. recognizer_path :识别器模型的路径。
  5. label_path : 标签编码器的路径。
  6. confidence_low:过滤弱人脸检测的可选阈值。

接下来是代码的主体部分:

  1. # 加载序列化人脸检测器
  2. print("[INFO] loading face detector...")
  3. protoPath = os.path.sep.join([detector_path, "deploy.proto.txt"])
  4. modelPath = os.path.sep.join([detector_path,"res10_300x300_ssd_iter_140000_fp16.caffemodel"])
  5. detector = cv2.dnn.readNetFromCaffe(protoPath, modelPath)
  6. # 加载我们序列化的人脸编码模型
  7. print("[INFO] loading face recognizer...")
  8. embedder = cv2.dnn.readNetFromTorch(embedding_path)
  9. # 加载实际的人脸识别模型和标签编码器
  10. recognizer = pickle.loads(open(recognizer_path, "rb").read())
  11. le = pickle.loads(open(label_path, "rb").read())
  12. # 加载图像,将其调整为宽度为 600 像素(同时保持纵横比),然后抓取图像尺寸
  13. image = cv2.imread(image_path)
  14. image = resize(image, width=600)
  15. (h, w) = image.shape[:2]
  16. # 从图像构建一个 blob
  17. imageBlob = cv2.dnn.blobFromImage(
  18. cv2.resize(image, (300, 300)), 1.0, (300, 300),
  19. (104.0, 177.0, 123.0), swapRB=False, crop=False)
  20.  
  21. # 应用 OpenCV 的基于深度学习的人脸检测器来定位输入图像中的人脸
  22. detector.setInput(imageBlob)
  23. detections = detector.forward()
  24.  
  25. # loop over the detections
  26. for i in range(0, detections.shape[2]):
  27. # 提取与预测相关的置信度(即概率)
  28. confidence = detections[0, 0, i, 2]
  29. # filter out weak detections
  30. if confidence > confidence_low:
  31. # 计算人脸边界框的 (x, y) 坐标
  32. box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
  33. (startX, startY, endX, endY) = box.astype("int")
  34. # 提取人脸ROI
  35. face = image[startY:endY, startX:endX]
  36. (fH, fW) = face.shape[:2]
  37. # 确保人脸宽度和高度足够大
  38. if fW < 20 or fH < 20:
  39. continue
  40.  
  41. # 为人脸 ROI 构造一个 blob,然后将 blob 通过我们的人脸嵌入模型来获得人脸的 128-d 量化
  42. faceBlob = cv2.dnn.blobFromImage(face, 1.0 / 255, (96, 96),
  43. (0, 0, 0), swapRB=True, crop=False)
  44. embedder.setInput(faceBlob)
  45. vec = embedder.forward()
  46. # 执行分类以识别人脸
  47. preds = recognizer.predict_proba(vec)[0]
  48. j = np.argmax(preds)
  49. proba = preds[j]
  50. name = le.classes_[j]
  51. # 绘制人脸的边界框以及相关的概率
  52. text = "{}: {:.2f}%".format(name, proba * 100)
  53. y = startY - 10 if startY - 10 > 10 else startY + 10
  54. cv2.rectangle(image, (startX, startY), (endX, endY),
  55. (0, 0, 255), 2)
  56. cv2.putText(image, text, (startX, y),
  57. cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
  58.  
  59. # 展示结果
  60. cv2.imshow("Image", image)
  61. cv2.waitKey(0)

我们在这个块中加载三个模型。 冒着冗余的风险,我想明确提醒您模型之间的差异:

  • 检测器:一个预训练的 Caffe DL 模型,用于检测人脸在图像中的位置。
  • embedder:一个预训练的 Torch DL 模型,用于计算我们的 128-D 人脸嵌入。
  • 识别器:线性 SVM 人脸识别模型。

1 和 2 都是预先训练好的,这意味着它们是由 OpenCV 按原样提供给您的

加载标签编码器,其中包含我们的模型可以识别的人的姓名。

将图像加载到内存中并构建一个 blob。

通过我们的检测器定位图像中的人脸。

您将从步骤 1 中识别出此块。 我在这里再解释一遍:

遍历检测并提取每个检测的置信度。

然后将置信度与命令行 最小概率检测阈值进行比较,确保计算出的概率大于最小概率。

我们提取人脸 ROI并确保它的空间维度足够大。

下面是识别人脸 ROI代码:

首先,构建一个 faceBlob)并将其通过编码器以生成描述面部的 128 维向量

然后,我们将 vec 通过我们的 SVM 识别器模型,其结果是我们对面部 ROI 中的人的预测。

我们取最高概率指数并查询我们的标签编码器以找到名称。

循环中识别每一张脸(包括“未知”)人:

在构造了一个包含名称和概率的文本字符串。

然后在脸部周围绘制一个矩形并将文本放在框上方。

最后我们在屏幕上可视化结果,直到按下某个键。

可以看出使用机器学习的方式准确率还是比较低的,但是优点是速度快。

摄像头识别人脸

这里我用视频代替,代码和图像中识别人脸的步骤一致,直接上代码。

新建recognize_video.py脚本,插入一下代码:

  1. import numpy as np
  2. import pickle
  3. import time
  4. import cv2
  5. import os
  6.  
  7.  
  8. def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
  9. dim = None
  10. (h, w) = image.shape[:2]
  11. # 如果高和宽为None则直接返回
  12. if width is None and height is None:
  13. return image
  14. # 检查宽是否是None
  15. if width is None:
  16. # 计算高度的比例并并按照比例计算宽度
  17. r = height / float(h)
  18. dim = (int(w * r), height)
  19. # 高为None
  20. else:
  21. # 计算宽度比例,并计算高度
  22. r = width / float(w)
  23. dim = (width, int(h * r))
  24. resized = cv2.resize(image, dim, interpolation=inter)
  25. # return the resized image
  26. return resized
  27.  
  28. out_put='output.avi'
  29. video_path = '1.mp4'
  30. detector_path = 'face_dete_model'
  31. embedding_path = 'nn4.small2.v1.t7'
  32. recognizer_path = 'output/recognizer.pickle'
  33. label_path = 'output/le.pickle'
  34. confidence_low = 0.5
  35. # load our serialized face detector from disk
  36. print("[INFO] loading face detector...")
  37. protoPath = os.path.sep.join([detector_path, "deploy.proto.txt"])
  38. modelPath = os.path.sep.join([detector_path,"res10_300x300_ssd_iter_140000_fp16.caffemodel"])
  39. detector = cv2.dnn.readNetFromCaffe(protoPath, modelPath)
  40.  
  41. # load our serialized face embedding model from disk
  42. print("[INFO] loading face recognizer...")
  43. embedder = cv2.dnn.readNetFromTorch(embedding_path)
  44.  
  45. # load the actual face recognition model along with the label encoder
  46. recognizer = pickle.loads(open(recognizer_path, "rb").read())
  47. le = pickle.loads(open(label_path, "rb").read())
  48.  
  49. # initialize the video stream, then allow the camera sensor to warm up
  50. print("[INFO] starting video stream...")
  51. #vs = cv2.VideoCapture(0) #摄像头
  52. vs=cv2.VideoCapture(video_path)# 视频
  53. time.sleep(2.0)
  54.  
  55. # start the FPS throughput estimator
  56.  
  57. writer=None
  58. # loop over frames from the video file stream
  59. while True:
  60. # grab the frame from the threaded video stream
  61. ret_val, frame = vs.read()
  62.  
  63. if ret_val is False:
  64. break
  65. # resize the frame to have a width of 600 pixels (while
  66. # maintaining the aspect ratio), and then grab the image
  67. # dimensions
  68. frame = resize(frame, width=600)
  69. (h, w) = frame.shape[:2]
  70.  
  71. # construct a blob from the image
  72. imageBlob = cv2.dnn.blobFromImage(
  73. cv2.resize(frame, (300, 300)), 1.0, (300, 300),
  74. (104.0, 177.0, 123.0), swapRB=False, crop=False)
  75.  
  76. # apply OpenCV's deep learning-based face detector to localize
  77. # faces in the input image
  78. detector.setInput(imageBlob)
  79. detections = detector.forward()
  80.  
  81. # loop over the detections
  82. for i in range(0, detections.shape[2]):
  83. # extract the confidence (i.e., probability) associated with
  84. # the prediction
  85. confidence = detections[0, 0, i, 2]
  86.  
  87. # filter out weak detections
  88. if confidence >confidence_low:
  89. # compute the (x, y)-coordinates of the bounding box for
  90. # the face
  91. box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
  92. (startX, startY, endX, endY) = box.astype("int")
  93. # extract the face ROI
  94. face = frame[startY:endY, startX:endX]
  95. (fH, fW) = face.shape[:2]
  96.  
  97. # ensure the face width and height are sufficiently large
  98. if fW < 20 or fH < 20:
  99. continue
  100.  
  101. # construct a blob for the face ROI, then pass the blob
  102. # through our face embedding model to obtain the 128-d
  103. # quantification of the face
  104. faceBlob = cv2.dnn.blobFromImage(face, 1.0 / 255,
  105. (96, 96), (0, 0, 0), swapRB=True, crop=False)
  106. embedder.setInput(faceBlob)
  107. vec = embedder.forward()
  108.  
  109. # perform classification to recognize the face
  110. preds = recognizer.predict_proba(vec)[0]
  111. j = np.argmax(preds)
  112. proba = preds[j]
  113. name = le.classes_[j]
  114.  
  115. # draw the bounding box of the face along with the
  116. # associated probability
  117. text = "{}: {:.2f}%".format(name, proba * 100)
  118. y = startY - 10 if startY - 10 > 10 else startY + 10
  119. cv2.rectangle(frame, (startX, startY), (endX, endY),
  120. (0, 0, 255), 2)
  121. cv2.putText(frame, text, (startX, y),
  122. cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
  123.  
  124. if writer is None and out_put is not None:
  125. fourcc = cv2.VideoWriter_fourcc(*"MJPG")
  126. writer = cv2.VideoWriter(out_put, fourcc, 20,
  127. (frame.shape[1], frame.shape[0]), True)
  128. # 如果 writer 不是 None,则将识别出人脸的帧写入磁盘
  129. if writer is not None:
  130. writer.write(frame)
  131. # show the output frame
  132. cv2.imshow("Frame", frame)
  133. key = cv2.waitKey(1) & 0xFF
  134. # if the `q` key was pressed, break from the loop
  135. if key == ord("q"):
  136. break
  137.  
  138. # do a bit of cleanup
  139. cv2.destroyAllWindows()
  140. vs.release()
  141. if writer is not None:
  142. writer.release()

运行结果:?

以上就是人脸识别实战之Opencv+SVM实现人脸识别的详细内容,更多关于Opencv SVM人脸识别的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号