谷动谷力
标题:
基于 NXP i.MX 的人脸识别初探
[打印本页]
作者:
鸣涧
时间:
2022-5-5 22:26
标题:
基于 NXP i.MX 的人脸识别初探
基于 NXP i.MX 的人脸识别初探
一、 人脸识别的facenet 模型
1.1、facenet
谷歌人脸检测算法,发表于 CVPR 2015,利用相同人脸在不同角度等姿态的照片下有高内聚性,不同人脸有低耦合性,提出使用 cnn + triplet mining 方法,在 LFW 数据集上准确度达到 99.63%。
通过 CNN 将人脸映射到欧式空间的特征向量上,实质上:不同图片人脸特征的距离较大;通过相同个体的人脸的距离,总是小于不同个体的人脸这一先验知识训练网络。测试时只需要计算人脸特征EMBEDDING,然后计算距离使用阈值即可判定两张人脸照片是否属于相同的个体。
1.png
(31.53 KB, 下载次数: 92)
下载附件
2022-5-5 22:26 上传
图1:人脸识别过程
简单来讲,在使用阶段,facenet即是:
1、输入一张人脸图片
2、通过深度学习网络提取特征
3、L2标准化
4、得到128维特征向量。
其中 Inception-ResNetV1是facenet使用的主干网络,如下图所示:
2.png
(46.98 KB, 下载次数: 92)
下载附件
2022-5-5 22:26 上传
图2:Inception-ResNetV1 网络结构
可以看到里面的结构分为几个重要的部分
1、stem
2、Inception-resnet-A
3、Inception-resnet-B
4、Inception-resnet-C
为了能够在手机或者是其他端平台实时运行人脸识别功能,需要对其中的 Backbone 进行替换成更更小同事精度也高的模型,于是又提出了 MobileFaceNet 模型。
1.2、MobileFaceNetMobileFaceNets,该模型使用少于一百万个参数,专门针对移动和嵌入式设备上的高精度实时人脸验证而量身定制。文章首先分析普通的mobilenet应用在人脸验证上有哪些缺陷,本文设计的MobileFaceNet可以很好克服这个缺陷,在同样的实验条件下,MobileFaceNet达到明显优越的精度,而且实际速度是MobileNetV2的两倍。单个4M尺寸大小的MobileFaceNet在MS-Celeb-1M数据集上用ArcFace训练后,可以在LFW达到99.55%精度,甚至可以和一些大型几百M的CNN网络相比较。最快的MobileFaceNet在手机上推断时间仅有18毫秒,对于人脸验证,相比之前的MobileCNN,它的效率大大提高。
二、i.MX 中人脸识别
2.1、数据库的初始化数据库中每一张图片对应一个人的人脸,图片名字就是这个人的名字。数据库初始化指的是人脸数据库的初始化。想要实现人脸识别,首先要知道自己需要识别哪些人脸。所以我们数据库的一个功能,将想要检测到的人脸放入数据库中,并进行编码。
数据库的初始化具体执行的过程就是:
1、遍历数据库中所有的图片。
2、检测每个图片中的人脸位置。
该步需要使用到face_detect_helpers.cpp 中的 init_face_detect() 函数功能:
void init_face_detect(Settings *setting)
{
String face_cascade_name = "haarcascade_frontalface_alt.xml";
if( !face_cascade.load(face_cascade_name) )
printf("--(!)Error loading haarcascade_frontalface_alt.xml\n");
s = setting;
}
复制代码
3、利用CascadeClassifier将人脸截取下载。
在face_recognition.cpp 中可以实现:
Mat face = get_data_face(img);
复制代码
其中 get_data_face() 中用来检测获取人脸 face ,所使用的的模型是 haarcascade_frontalface_alt.xml
<div>Mat get_data_face(Mat &img)
</div>{
float scale = (float)rescaled_width / img.cols;
if (scale > 1)
scale = 1;
Mat small_img = preprocess(img, scale);
Rect faceRef = detect_face(small_img, scale);
Mat face;
if (!faceRef.empty())
{
cvtColor(img(faceRef), face, COLOR_BGR2RGB);
}
return face;
}
复制代码
5、利用facenet将人脸进行编码。
6、将所有人脸编码的结果放在一个array中。
利用 facenet 模型将 face 信息转为 feature_vec ,并存在 array 中
std::array<float, num_class> feature_vec = get_output_tensor(face, s);
复制代码
第6步得到的列表就是已知的所有人脸的特征列表,在之后获得的实时图片中的人脸都需要与已知人脸进行比对,这样我们才能知道谁是谁。
其完整的实现代码如下:
face_recognition.cpp
void init_database(Settings *s)
{
if (s->verbose)
LOG(INFO) << "Enrolling...." << "\r\n";
std::vector image_names;
const int dir_err = mkdir(s->data_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (dir_err != -1 && errno != EEXIST)
{
return;
}
glob(s->data_dir, image_names);
auto start_time = GET_TIME_IN_US();
for (size_t i = 0; i < image_names.size(); i++)
{
if (s->verbose)
LOG(INFO) << "Add face " << i + 1 << "\r\n";
Mat img = imread(image_names, IMREAD_COLOR);
Mat face = get_data_face(img);
if (face.empty())
{
continue;
}
std::array<float, num_class> feature_vec = get_output_tensor(face, s);
cvtColor(face, face, COLOR_RGB2BGR);
std::string label = image_names;
label.erase(0, s->data_dir.length());
label.erase(label.find("."));
face_database[label] = {face, feature_vec};
}
auto stop_time = GET_TIME_IN_US();
if (s->profiling)
{
LOG(INFO) << "init face database\n\r";
LOG(INFO) << "total time: "
<< (GET_TIME_DIFF(start_time, stop_time)) / 1000
<< " ms \n\r";
LOG(INFO) << "average time: "
<< (GET_TIME_DIFF(start_time, stop_time)) / 1000 / image_names.size()
<< " ms \n\r";
}
if (s->verbose)
LOG(INFO) << "The all " << image_names.size() << "faces database is done!\r\n";
}
复制代码
2.2、实时图像的处理
在上面讲完数据库的初始化部分后,既可以对摄像头实时处理图像数据部分开始进行研究,了解 i.MX 中图像处理流程。
2.2.1、人脸的检测 首先是从摄像头抓取 camera 图像,图像尺寸大小为 640 x 480
void init_camera()
{
cap.open(s->camera);
if (!cap.isOpened())
{
LOG(FATAL) << "Capture from camera #" << s->camera << " didn't work"
<< "\n\r";
exit(-1);
}
cap.set(CAP_PROP_FRAME_WIDTH, 640);
cap.set(CAP_PROP_FRAME_HEIGHT, 480);
}
复制代码
利用 opencv 中的 CascadeClassifier face_cascade ,人脸检测器来检测人脸,过程如下:
Rect detect_face(Mat &img, float scale)
{
std::vector faces;
face_cascade.detectMultiScale(img, faces, 1.3, 3,
CASCADE_FIND_BIGGEST_OBJECT, Size(65, 65));
Rect faceRef;
if (faces.empty())
{
return faceRef;
}
for (int i = 0; i < faces.size(); i++)
{
if (faces.area() > faceRef.area())
{
faceRef = faces;
}
}
faceRef = Rect(Point(cvRound(faceRef.x / scale),
cvRound(faceRef.y / scale)),
Point(cvRound((faceRef.x + faceRef.width - 1) / scale),
cvRound((faceRef.y + faceRef.height - 1) / scale)));
return faceRef;
}
复制代码
将人脸 face 从输入的图像中截取下来,节省计算资源:
Mat get_data_face(Mat &img)
{
float scale = (float)rescaled_width / img.cols;
if (scale > 1)
scale = 1;
Mat small_img = preprocess(img, scale);
Rect faceRef = detect_face(small_img, scale);
Mat face;
if (!faceRef.empty())
{
cvtColor(img(faceRef), face, COLOR_BGR2RGB);
}
return face;
}
复制代码
2.2.2、Tflite 中对人脸进行编码
i.MX 中对人脸进行编码,源码中并没有将 inference 过程给出,仅仅是针对不同的模型进行推理,代码如下所示:
std::array<float, num_class> get_output_tensor(Mat img, Settings *s)
{
int output = interpreter->outputs()[0];
TfLiteTensor *output_tensor = interpreter->tensor(output);
TfLiteIntArray *output_dims = output_tensor->dims;
// assume output dims to be something like (1, 1, ... ,size)
auto output_size = output_dims->data[output_dims->size - 1];
std::array<float, num_class> array;
switch (interpreter->tensor(output)->type)
{
case kTfLiteFloat32:
{
float *output_float = interpreter->typed_output_tensor(0);
for (int i = 0; i < output_size; i++)
array = output_float;
break;
}
case kTfLiteUInt8:
{
uint8_t *output_uint8 = interpreter->typed_output_tensor(0);
for (int i = 0; i < output_size; i++)
array = output_uint8;
break;
}
default:
{
LOG(FATAL) << "cannot handle output type "
<< interpreter->tensor(input)->type << " yet";
exit(-1);
}
}
return array;
}
复制代码
2.2.3、将实时图像中的人脸特征与数据库中的进行对比
对得到的人脸特征编码进行计算,大于 threshold 则判断是同一人脸,具体计算过程如下:
std::pair<std::string, float> predict_label(std::array<float, num_class> &pred, float threshold)
{
float ret = 0, mod1 = 0, mod2 = 0;
float max = 0;
std::string label;
for (int i = 0; i < num_class; i++)
mod1 += pred * pred;
for (auto &face : face_database)
{
ret = 0, mod2 = 0;
std::array<float, num_class> &vec = face.second.second;
for (int i = 0; i < num_class; i++)
{
ret += pred * vec;
mod2 += vec * vec;
}
float cosine_similarity = (ret / sqrt(mod1) / sqrt(mod2) + 1) / 2.0;
if (max <= cosine_similarity)
{
max = cosine_similarity;
label = face.first;
}
}
if (max < threshold)
{
label.clear();
}
return {label, max};
}
复制代码
三、总结归纳
通过本文的讲述,初步阐述了如何在NXP 的 i.MX 中使用 tflite 框架对人脸进行识别的过程,重点强调算法实现的过程原理。当然实际上的操作还有增加人脸数据库的内容,需要再官网的demo 中演示查看效果。
四、参考资料
1.《睿智的目标检测40——Keras搭建Retinaface人脸检测与关键点定位平台》:
https://blog.csdn.net/weixin_44791964/article/details/106871010
欢迎光临 谷动谷力 (http://bbs.sunsili.com/)
Powered by Discuz! X3.2