经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 大数据/云/AI » 人工智能基础 » 查看文章
基于开源模型搭建实时人脸识别系统(二):人脸检测概览与模型选型
来源:cnblogs  作者:CoderInCV  时间:2023/9/14 9:53:06  对本文有异议

基于开源模型的实时人脸识别系统
进行人脸识别首要的任务就是要定位出画面中的人脸,这个任务就是人脸检测。人脸检测总体上算是目标检测的一个特殊情况,但也有自身的特点,比如角度多变,表情多变,可能存在各类遮挡。早期传统的方法有Haar Cascade、HOG等,基本做法就是特征描述子+滑窗+分类器,随着2012年Alexnet的出现,慢慢深度学习在这一领域开始崛起。算法和硬件性能的发展,也让基于深度学习的人脸识别不仅性能取得了很大的提升,速度也能达到实时,使得人脸技术真正进入了实用。
image.png
人脸检测大体上跟随目标检测技术的发展,不过也有些自己的方法,主要可以分为一下几类方法.
image.png

人脸检测算法概览

由于这个系列重点并不在于算法细节本身,因而对于一些算法只是提及,有兴趣可以自己精读。

Cascade-CNN Based Models

这类方法通过级联几个网络来逐步提高准确率,比较有代表性的是MTCNN方法。
image.png
MTCNN通过级联PNet, RNet, ONet,层层过滤来提高整个检测的精度。这个方法更适合CPU,那个时期的嵌入式设备使用比较多。 由于有3个网络,训练起来比较麻烦。

R-CNN

这一块主要来源于目标检测中的RCNN, Fast RCNN, Faster RCNN
image.png

这类方法精度高,但速度相对较慢。

Single Shot Detection Models

SSD是目标检测领域比较有代表性的一个算法,与RCNN系列相比,它是one stage方法,速度比较快。基于它的用于人脸检测的代表性方法是SSH.

Feature Pyramid Network Based Models

image.png

YOLO系列

YOLO系列在目标检测领域比较成功,自然的也会用在人脸检测领域,比如tiny yolo face,yolov5face, yolov8face等,基本上每一代都会应用于人脸。

开源模型的选型

为了能够达到实时,同时也要有较好的效果,我们将目光锁定在yolo系列上,yolo在精度和速度的平衡上做的比较好,也比较易用。目前最新的是yolov8, 经过搜索,也已经有人将其用在人脸检测上了:derronqi/yolov8-face: yolov8 face detection with landmark (github.com)
image.png

推理框架的选择

简单起见,我们选择onnxruntime,该框架既支持CPU也支持GPU, 基本满足了我们的开发要求。

yolov8-face的使用

为了减少重复工作,我们可以定义一个模型的基类, 对模型载入、推理的操作进行封装,这样就不需要每个模型都实现一遍了:

  1. from easydict import EasyDict as edict
  2. import onnxruntime
  3. import threading
  4. class BaseModel:
  5. def __init__(self, model_path, device='cpu', **kwargs) -> None:
  6. self.model = self.load_model(model_path, device)
  7. self.input_layer = self.model.get_inputs()[0].name
  8. self.output_layers = [output.name for output in self.model.get_outputs()]
  9. self.lock = threading.Lock()
  10. def load_model(self, model_path:str, device:str='cpu'):
  11. available_providers = onnxruntime.get_available_providers()
  12. if device == "gpu" and "CUDAExecutionProvider" not in available_providers:
  13. print("CUDAExecutionProvider is not available, use CPUExecutionProvider instead")
  14. device = "cpu"
  15. if device == 'cpu':
  16. self.model = onnxruntime.InferenceSession(model_path, providers=['CPUExecutionProvider'])
  17. else:
  18. self.model = onnxruntime.InferenceSession(model_path,providers=['CUDAExecutionProvider'])
  19. return self.model
  20. def inference(self, input):
  21. with self.lock:
  22. outputs = self.model.run(self.output_layers, {self.input_layer: input})
  23. return outputs
  24. def preprocess(self, **kwargs):
  25. pass
  26. def postprocess(self, **kwargs):
  27. pass
  28. def run(self, **kwargs):
  29. pass

继承BaseModel, 实现模型的前处理和后处理:

  1. class Yolov8Face(BaseModel):
  2. def __init__(self, model_path, device='cpu',**kwargs) -> None:
  3. super().__init__(model_path, device, **kwargs)
  4. self.conf_threshold = kwargs.get('conf_threshold', 0.5)
  5. self.iou_threshold = kwargs.get('iou_threshold', 0.4)
  6. self.input_size = kwargs.get('input_size', 640)
  7. self.input_width, self.input_height = self.input_size, self.input_size
  8. self.reg_max=16
  9. self.project = np.arange(self.reg_max)
  10. self.strides=[8, 16, 32]
  11. self.feats_hw = [(math.ceil(self.input_height / self.strides[i]), math.ceil(self.input_width / self.strides[i])) for i in range(len(self.strides))]
  12. self.anchors = self.make_anchors(self.feats_hw)
  13. def make_anchors(self, feats_hw, grid_cell_offset=0.5):
  14. """Generate anchors from features."""
  15. anchor_points = {}
  16. for i, stride in enumerate(self.strides):
  17. h,w = feats_hw[i]
  18. x = np.arange(0, w) + grid_cell_offset # shift x
  19. y = np.arange(0, h) + grid_cell_offset # shift y
  20. sx, sy = np.meshgrid(x, y)
  21. # sy, sx = np.meshgrid(y, x)
  22. anchor_points[stride] = np.stack((sx, sy), axis=-1).reshape(-1, 2)
  23. return anchor_points
  24. def preprocess(self, image, **kwargs):
  25. return resize_image(image, keep_ratio=True, dst_width=self.input_width, dst_height=self.input_height)
  26. def distance2bbox(self, points, distance, max_shape=None):
  27. x1 = points[:, 0] - distance[:, 0]
  28. y1 = points[:, 1] - distance[:, 1]
  29. x2 = points[:, 0] + distance[:, 2]
  30. y2 = points[:, 1] + distance[:, 3]
  31. if max_shape is not None:
  32. x1 = np.clip(x1, 0, max_shape[1])
  33. y1 = np.clip(y1, 0, max_shape[0])
  34. x2 = np.clip(x2, 0, max_shape[1])
  35. y2 = np.clip(y2, 0, max_shape[0])
  36. return np.stack([x1, y1, x2, y2], axis=-1)
  37. def postprocess(self, preds, scale_h, scale_w, top, left, **kwargs):
  38. bboxes, scores, landmarks = [], [], []
  39. for i, pred in enumerate(preds):
  40. stride = int(self.input_height/pred.shape[2])
  41. pred = pred.transpose((0, 2, 3, 1))
  42. box = pred[..., :self.reg_max * 4]
  43. cls = 1 / (1 + np.exp(-pred[..., self.reg_max * 4:-15])).reshape((-1,1))
  44. kpts = pred[..., -15:].reshape((-1,15)) ### x1,y1,score1, ..., x5,y5,score5
  45. # tmp = box.reshape(self.feats_hw[i][0], self.feats_hw[i][1], 4, self.reg_max)
  46. tmp = box.reshape(-1, 4, self.reg_max)
  47. bbox_pred = softmax(tmp, axis=-1)
  48. bbox_pred = np.dot(bbox_pred, self.project).reshape((-1,4))
  49. bbox = self.distance2bbox(self.anchors[stride], bbox_pred, max_shape=(self.input_height, self.input_width)) * stride
  50. kpts[:, 0::3] = (kpts[:, 0::3] * 2.0 + (self.anchors[stride][:, 0].reshape((-1,1)) - 0.5)) * stride
  51. kpts[:, 1::3] = (kpts[:, 1::3] * 2.0 + (self.anchors[stride][:, 1].reshape((-1,1)) - 0.5)) * stride
  52. kpts[:, 2::3] = 1 / (1+np.exp(-kpts[:, 2::3]))
  53. bbox -= np.array([[left, top, left, top]]) ###合理使用广播法则
  54. bbox *= np.array([[scale_w, scale_h, scale_w, scale_h]])
  55. kpts -= np.tile(np.array([left, top, 0]), 5).reshape((1,15))
  56. kpts *= np.tile(np.array([scale_w, scale_h, 1]), 5).reshape((1,15))
  57. bboxes.append(bbox)
  58. scores.append(cls)
  59. landmarks.append(kpts)
  60. bboxes = np.concatenate(bboxes, axis=0)
  61. scores = np.concatenate(scores, axis=0)
  62. landmarks = np.concatenate(landmarks, axis=0)
  63. bboxes_wh = bboxes.copy()
  64. bboxes_wh[:, 2:4] = bboxes[:, 2:4] - bboxes[:, 0:2] ####xywh
  65. classIds = np.argmax(scores, axis=1)
  66. confidences = np.max(scores, axis=1) ####max_class_confidence
  67. mask = confidences>self.conf_threshold
  68. bboxes_wh = bboxes_wh[mask] ###合理使用广播法则
  69. confidences = confidences[mask]
  70. classIds = classIds[mask]
  71. landmarks = landmarks[mask]
  72. if len(bboxes_wh) == 0:
  73. return np.empty((0, 5)), np.empty((0, 5))
  74. indices = cv2.dnn.NMSBoxes(bboxes_wh.tolist(), confidences.tolist(), self.conf_threshold,
  75. self.iou_threshold).flatten()
  76. if len(indices) > 0:
  77. mlvl_bboxes = bboxes_wh[indices]
  78. confidences = confidences[indices]
  79. classIds = classIds[indices]
  80. ## convert box to x1,y1,x2,y2
  81. mlvl_bboxes[:, 2:4] = mlvl_bboxes[:, 2:4] + mlvl_bboxes[:, 0:2]
  82. # concat box, confidence, classId
  83. mlvl_bboxes = np.concatenate((mlvl_bboxes, confidences.reshape(-1, 1), classIds.reshape(-1, 1)), axis=1)
  84. landmarks = landmarks[indices]
  85. return mlvl_bboxes, landmarks.reshape(-1, 5, 3)[..., :2]
  86. else:
  87. return np.empty((0, 5)), np.empty((0, 5))
  88. def run(self, image, **kwargs):
  89. img, newh, neww, top, left = self.preprocess(image)
  90. scale_h, scale_w = image.shape[0]/newh, image.shape[1]/neww
  91. # convert to RGB
  92. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  93. img = img.astype(np.float32)
  94. img = img / 255.0
  95. img = np.transpose(img, (2, 0, 1))
  96. img = np.expand_dims(img, axis=0)
  97. output = self.inference(img)
  98. bboxes, landmarks = self.postprocess(output, scale_h, scale_w, top, left)
  99. # limit box in image
  100. bboxes[:, 0] = np.clip(bboxes[:, 0], 0, image.shape[1])
  101. bboxes[:, 1] = np.clip(bboxes[:, 1], 0, image.shape[0])
  102. return bboxes, landmarks

测试

在Intel(R) Core(TM) i5-10210U上,yolov8-lite-t耗时50ms, 基本可以达到实时的需求。
image.png

参考文献:
ZOU, Zhengxia, et al. Object detection in 20 years: A survey. Proceedings of the IEEE, 2023.
MINAEE, Shervin, et al. Going deeper into face detection: A survey. arXiv preprint arXiv:2103.14983, 2021.

人脸识别系统源码

https://mbd.pub/o/bread/ZJyTmZty

原文链接:https://www.cnblogs.com/haoliuhust/p/17700867.html

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

本站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号