FaceNet人脸特征提取

FaceNet是一种用于提取人脸图像特征的深度神经网络。它由谷歌研究人员 Schroff 等人提出。

论文地址:https://arxiv.org/abs/1503.03832

FaceNet 的工作原理是,输入一张人脸图像,将其压缩并输出为一个由128 位数组成的向量,表示人脸的基本特征。这个向量被称为嵌入,(来自面部图像的所有相关信息都嵌入到向量中)。

那么,如何通过FaceNet实现人脸识别呢?

通常的做法是,获取图像嵌入并计算与已知人脸的图片的距离。通常使用余弦定理或欧几里得距离公式计算。如果计算出的人脸距离与已知人脸的嵌入足够接近,我们就假设这张脸是同一个人的。

那么问题就是,FaceNet 如何知道需要从人脸图像中提取什么东西?

为了训练一个人脸识别器,我们需要很多人脸图像。像每个机器学习问题一样,训练通常需要数千张不同的图像。当我们开始训练过程时,模型会为每张图像生成随机向量,这意味着图像是随机分布的。

学习步骤:

  1. 随机选择一个锚点图像;
  2. 随机选择同一个人的正样本图像作为基础图像;
  3. 随机选择与主图像不同的人的负样本图像;
  4. 调整 FaceNet 神经网络参数,使正样本比负样本更靠近锚点。

我们重复这四个步骤,直到不再需要更改或这些更改非常小以至于没有影响。训练完成后,同一个人的所有面孔在距离上都彼此靠近,而与不同的面孔相距很远。

faceNet.py完整的对象代码:

# faceNet.py
import cv2import cv2
import stow
import typing
import numpy as np
import onnxruntime as ortclass FaceNet:"""FaceNet class object, which can be used for simplified face recognition"""def __init__(self, detector: object,onnx_model_path: str = "models/faceNet.onnx", anchors: typing.Union[str, dict] = 'faces',force_cpu: bool = False,threshold: float = 0.5,color: tuple = (255, 255, 255),thickness: int = 2,) -> None:"""Object for face recognitionParams:detector: (object) - detector object to detect faces in imageonnx_model_path: (str) - path to onnx modelforce_cpu: (bool) - if True, onnx model will be run on CPUanchors: (str or dict) - path to directory with faces or dictionary with anchor names as keys and anchor encodings as valuesthreshold: (float) - threshold for face recognitioncolor: (tuple) - color of bounding box and textthickness: (int) - thickness of bounding box and text"""if not stow.exists(onnx_model_path):raise Exception(f"Model doesn't exists in {onnx_model_path}")self.detector = detectorself.threshold = thresholdself.color = colorself.thickness = thicknessproviders = ['CUDAExecutionProvider', 'CPUExecutionProvider']providers = providers if ort.get_device() == "GPU" and not force_cpu else providers[::-1]self.ort_sess = ort.InferenceSession(onnx_model_path, providers=providers)self.input_shape = self.ort_sess._inputs_meta[0].shape[1:3]self.anchors = self.load_anchors(anchors) if isinstance(anchors, str) else anchorsdef normalize(self, img: np.ndarray) -> np.ndarray:"""Normalize imageArgs:img: (np.ndarray) - image to be normalizedReturns:img: (np.ndarray) - normalized image"""mean, std = img.mean(), img.std()return (img - mean) / stddef l2_normalize(self, x: np.ndarray, axis: int = -1, epsilon: float = 1e-10) -> np.ndarray:"""l2 normalization functionArgs:x: (np.ndarray) - input arrayaxis: (int) - axis to normalizeepsilon: (float) - epsilon to avoid division by zeroReturns:x: (np.ndarray) - normalized array"""output = x / np.sqrt(np.maximum(np.sum(np.square(x), axis=axis, keepdims=True), epsilon))return outputdef detect_save_faces(self, image: np.ndarray, output_dir: str = "faces"):"""Detect faces in given image and save them to output_dirArgs:image: (np.ndarray) - image to be processedoutput_dir: (str) - directory where faces will be savedReturns:bool: (bool) - True if faces were detected and saved"""face_crops = [image[t:b, l:r] for t, l, b, r in self.detector(image, return_tlbr=True)]if face_crops == []: return Falsestow.mkdir(output_dir)for index, crop in enumerate(face_crops):output_path = stow.join(output_dir, f"face_{str(index)}.png")cv2.imwrite(output_path, crop)print("Crop saved to:", output_path)self.anchors = self.load_anchors(output_dir)return Truedef load_anchors(self, faces_path: str):"""Generate anchors for given faces pathArgs:faces_path: (str) - path to directory with facesReturns:anchors: (dict) - dictionary with anchor names as keys and anchor encodings as values"""anchors = {}if not stow.exists(faces_path):return {}for face_path in stow.ls(faces_path):anchors[stow.basename(face_path)] = self.encode(cv2.imread(face_path.path))return anchorsdef encode(self, face_image: np.ndarray) -> np.ndarray:"""Encode face image with FaceNet modelArgs face_image: (np.ndarray) - face image to be encodedReturns:face_encoding: (np.ndarray) - face encoding"""face = self.normalize(face_image)face = cv2.resize(face, self.input_shape).astype(np.float32)encode = self.ort_sess.run(None, {self.ort_sess._inputs_meta[0].name: np.expand_dims(face, axis=0)})[0][0]normalized_encode = self.l2_normalize(encode)return normalized_encodedef cosine_distance(self, a: np.ndarray, b: typing.Union[np.ndarray, list]) -> np.ndarray:"""Cosine distance between wectors a and bArgs:a: (np.ndarray) - first vectorb: (np.ndarray) - second list of vectorsReturns:distance: (float) - cosine distance"""if isinstance(a, list):a = np.array(a)if isinstance(b, list):b = np.array(b)return np.dot(a, b.T) / (np.linalg.norm(a) * np.linalg.norm(b))def draw(self, image: np.ndarray, face_crops: dict):"""Draw face crops on imageArgs:image: (np.ndarray) - image to be drawn onface_crops: (dict) - dictionary with face crops as values and face names as keysReturns:image: (np.ndarray) - image with drawn face crops"""for value in face_crops.values():t, l, b, r = value["tlbr"]cv2.rectangle(image, (l, t), (r, b), self.color, self.thickness)cv2.putText(image, stow.name(value['name']), (l, t - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, self.color, self.thickness)return imagedef __call__(self, frame: np.ndarray) -> np.ndarray:"""Face recognition pipelineArgs:frame: (np.ndarray) - image to be processedReturns:frame: (np.ndarray) - image with drawn face recognition results"""face_crops = {index: {"name": "Unknown", "tlbr": tlbr} for index, tlbr in enumerate(self.detector(frame, return_tlbr=True))}for key, value in face_crops.items():t, l, b, r = value["tlbr"]face_encoding = self.encode(frame[t:b, l:r])distances = self.cosine_distance(face_encoding, list(self.anchors.values()))if np.max(distances) > self.threshold:face_crops[key]["name"] = list(self.anchors.keys())[np.argmax(distances)]frame = self.draw(frame, face_crops)return frame

我们可以使用faceNet/convert_to_onnx.py脚本进行转换:

# faceNet/convert_to_onnx.py
import os
import tensorflow as tf
import tf2onnx
from architecture import InceptionResNetV2if __name__ == '__main__':""" weights can be downloaded from https://drive.google.com/drive/folders/1scGoVCQp-cNwKTKOUqevCP1N2LlyXU3l?usp=sharingPut facenet_keras_weights.h5 file in model folder"""facenet_weights_path = "models/facenet_keras_weights.h5"onnx_model_output_path = "models/faceNet.onnx"if not os.path.exists(facenet_weights_path):raise Exception(f"Model doesn't exists in {facenet_weights_path}, download weights from \https://drive.google.com/drive/folders/1scGoVCQp-cNwKTKOUqevCP1N2LlyXU3l?usp=sharing")faceNet = InceptionResNetV2()faceNet.load_weights(facenet_weights_path) spec = (tf.TensorSpec(faceNet.inputs[0].shape, tf.float32, name="image_input"),)tf2onnx.convert.from_keras(faceNet, output_path=onnx_model_output_path, input_signature=spec)
view raw

首先,从代码中的给定链接下载权重并将它们放在模型文件夹中。然后使用python 运行 faceNet/convert_to_onnx.py代码,它能够将模型转换为.onnx格式。

有了模型后,我们可以打开main.py脚本并使用以下代码运行网络摄像头实时人脸识别:

# main.py
from utils import FPSmetric
from engine import Engine
from faceDetection import MPFaceDetection
from faceNet.faceNet import FaceNetif __name__ == '__main__':facenet = FaceNet(detector = MPFaceDetection(),onnx_model_path = "models/faceNet.onnx", anchors = "faces",force_cpu = True,)engine = Engine(webcam_id=0, show=True, custom_objects=[facenet, FPSmetric()])# save first face crop as anchor, otherwise don't usewhile not facenet.detect_save_faces(engine.process_webcam(return_frame=True), output_dir="faces"):continueengine.run()

给定模型的存储路径时。我们给它保存anchors的路径;它必须是带有面部裁剪的图像。保证模型将加载此锚点并在找到匹配项时显示对应的名字。

接下来,我们需要创建一个引擎对象,负责处理图像、视频或网络摄像头流;可以选择处理网络摄像头。使用“show”参数。

另外,我们可以添加一个 FPSmetric 来了解人脸识别的工作速度。

最后,我们需要将“facenet”对象传递给“custom_objects”参数。在这里,我们可以添加更多,“pencil sketch”、“background removal”或其他我们想要的实体。

我们还可以创建一个函数来抓取第一个网络摄像头帧,如果它在其中找到一张人脸,它就会裁剪并保存它:

while not facenet.detect_save_faces(engine.process_webcam(return_frame=True), output_dir="faces"):continue

这样我们就创建了一个可以在我们的 CPU 上进行实时人脸识别的系统,它的运行速度在 30 fps 左右,这对我们来说已经足够了!

来源:

GitHub地址:https://github.com/pythonlessons/background_removal

基于FaceNet的实时人脸识别训练相关推荐

  1. python人脸识别库_基于facenet的实时人脸识别系统

    facenet_facerecognition opencv+mtcnn+facenet+python+tensorflow 实现实时人脸识别 Abstract:本文记录了在学习深度学习过程中,使用o ...

  2. 基于facenet的实时人脸检测

    参考自https://github.com/shanren7/real_time_face_recognition 本人的项目代码https://github.com/zouzhen/real_tim ...

  3. 视觉识别入门之人脸识别——基于FACENET的高精度人脸识别

    视觉识别入门之人脸识别---- 基于FACENET的高精度人脸识别 一:项目展示: - 这是实时视频读取的展示,是可以读单张图片,或者本地视频流,抑或是实时人脸检测与分类的,至于我为什么不展示我的自拍 ...

  4. 基于视频的实时人脸识别(含代码)

    文章目录 介绍 思路介绍 运行环境介绍 模型介绍 人脸关键点预测器 人脸识别模型 效果展示 识别过程 代码 建立本地人脸库 人脸识别 介绍 思路介绍 无论是基于视频或者调用摄像头来完成人脸识别,其实是 ...

  5. 基于MTCNN和FaceNet的实时人脸检测识别系统

    文章目录 模型介绍 MTCNN FaceNet 基于MTCNN和FaceNet的实时人脸检测识别系统 在LFW数据集上测试 参考文献 GitHub项目地址:https://github.com/Har ...

  6. gpu训练cnn人脸识别准确率_opencv+mtcnn+facenet+python+tensorflow 实现实时人脸识别

    opencv+mtcnn+facenet+python+tensorflow 实现实时人脸识别 Abstract:本文记录了在学习深度学习过程中,使用opencv+mtcnn+facenet+pyth ...

  7. mtcnn人脸检测python_基于mtcnn和facenet的实时人脸检测与识别系统开发

    简介:本文主要介绍了实时人脸检测与识别系统的详细方法.该系统基于python/opencv2/tensorflow环境,实现了从摄像头读取视频,检测人脸,识别人脸的功能.本系统代码地址:real ti ...

  8. Keras之CNN:基于Keras利用cv2建立训练存储卷积神经网络模型(2+1)并调用摄像头进行实时人脸识别

    Keras之CNN:基于Keras利用cv2建立训练存储卷积神经网络模型(2+1)并调用摄像头进行实时人脸识别 目录 输出结果 设计思路 核心代码 输出结果 设计思路 核心代码 # -*- codin ...

  9. mtcnn人脸检测python_opencv+mtcnn+facenet+python+tensorflow 实现实时人脸识别

    Abstract:本文记录了在学习深度学习过程中,使用opencv+mtcnn+facenet+python+tensorflow,开发环境为ubuntu18.04,实现局域网连接手机摄像头,对目标人 ...

最新文章

  1. [问题解决] Python中 == 与 is 的区别
  2. 播放视频android学习笔记---44_在线视频播放器,网络视频解析器,SurfaceView 控件使用方法...
  3. 嵌入式linux系统,给WIFI模块增加一个开关
  4. 三大主流软件负载均衡器(LVS、Nginx、HAproxy) 与商业SLB比较
  5. 产品经理的四个重要阶段
  6. BZOJ3743 : [Coci2014]Kamp
  7. 选择排序:简单选择排序
  8. poj 2153 Rank List
  9. STL中的所有算法(70个)
  10. 增强型绿植植被指数_植被指数--数据产品-国家青藏高原科学数据中心
  11. 百兆1光4电工业级光纤收发器4口百兆光纤收发器工业导轨式发送机导轨式以太网光电转换器
  12. 微信小程序上传并设置为体验版的办法(解决了没有上传按钮,体验版拉取不到数据的问题)
  13. VOT竞赛paper阅读笔记
  14. Matlab中regress函数各参数解释
  15. 牛逼!java只能输入数字的正则
  16. 深入浅出pytorch笔记——第一章
  17. 全栈工程师必备技能栈,聊聊月薪2W以内都该会点啥?
  18. 双硫脲改性Zr-MOF吸附材料|聚多巴胺(PDA)改性MOF-5|羧酸改性的UiO-66(Zr)膜|有机骨架材料的定制技术
  19. 细数Mac上那些好用且免费的软件(四)
  20. 黑马程序员训练营十道满分题解

热门文章

  1. Zookeeper 原理与优化
  2. 2020.11.9--AE--文字的文本属性、文字动画效果、内置动画预设
  3. 2021年12月中国A股石油加工贸易行业上市企业市值排行榜:中国石油位居榜首,宇新股份股价最高(附月榜TOP24详单)
  4. centos下Intel核显应用ffmpeg的qsv插件编解码
  5. 手机党心声:“离开手机生活”这是不可能的!
  6. Spark程序使用Scala进行单元测试
  7. Java多线程实现的两种方式
  8. i5 12490f和i5 12600kf差距 i512490f和i512600kf对比
  9. webstorm 常用插件集合
  10. shell脚本中实现远程和其他用户的子shell执行