一、背景

人脸识别是近年来最热门的计算机视觉领域的应用之一,而且现在已经出现了非常多的人脸识别算法,如:DeepID、FaceNet、DeepFace等等。人脸识别被广泛应用于景区、客运、酒店、办公室、工地、小区等场所,极大的方便了人们的生活。在安防领域,人脸识别也展现出巨大的活力,通过人脸识别对摄像头采集的图像进行处理,可以更快的发现可疑人员。

1:1人脸核验通常不会过度考虑速度问题,而1:N的人脸识别场景有的时候速度是非常重要的。比如用户想通过人脸识别快速确定图片中的明星是谁,而后台的星数据库中有几百万甚至几千万的数据,一一对比将很难在短时间内返回结果,在高并发的时候更是非常占用资源。所以使用向量近似搜索将在大规模的人脸识别场景中显得非常重要。

二、虹软SDK及Milvus简介

虹软人脸识别SDK是一款集人脸检测、人脸跟踪、人脸比对、人脸查找、人脸属性、IR/RGB活体检测多项能力于一身的离线人脸识别SDK。支持Windows,Linux,Android等多个平台。支持离线服务,可在无网络环境下使用,本地化部署。有增值版免费版两个版本。

Milvus 是一款开源的向量相似度搜索引擎,提供了Python、Java、Go、C++、RESTful 等API接口,支持针对 TB 级向量的增删改操作和近实时查询,具有高度灵活、稳定可靠以及高速查询等特点。Milvus 集成了 Faiss、NMSLIB、Annoy 等广泛应用的向量索引库,提供了一整套简单直观的 API,让你可以针对不同场景选择不同的索引类型。此外,Milvus 还可以对标量数据进行过滤,进一步提高了召回率,增强了搜索的灵活性。

三、开发环境

本文中虹软SDK使用C++调用,Milvus使用Python API。如需使用C++版本的Milvus API 请自行编译。

本文代码所需环境:

  1. 虹软人脸识别SDK4.0增值版
  2. Milvus 1.0.0
  3. OpenCV 2.4.9
  4. VS 2013
  5. Python 3.6 +(低于3.6可能无法安装pymilvus)

四、虹软人脸识别SDK使用简介

虹软人脸识别SDK使用非常简单。对于一般的人脸识别流程:

  1. 调用ASFOnlineActivation在线激活,激活后会生成激活文件,下次再运行就不用再次激活了。
  2. 调用ASFInitEngine初始化引擎,在这里可以选择人脸检测模式或人脸追踪模式(人脸追踪更快)以及传入其他参数。
  3. 调用ASFDetectFaces检测人脸,得到一帧图像里所有的人脸框。
  4. 调用ASFFaceFeatureExtract提取人脸特征
  5. 调用ASFFaceFeatureCompare对两个人脸特征进行对比,返回相似度。

  • 使用虹软SDK的时候需要注意,每次调用ASFDetectFacesASFFaceFeatureExtract等接口时保存结果的位置是固定的,并且这个位置是在初始化引擎时就确定好了的,返回的结构仅仅是指向这个位置的一个指针,也就是说下一次调用ASFDetectFaces就会覆盖上一次ASFDetectFaces的结果,如果需要保存上一次的结果,请将这部分内存copy出来。 虹软这样做的好处是函数不会因为申请不到内存而失败,并且不会造成内存泄漏。

这只是最简单的人脸识别流程,除此之外,虹软人脸识别SDK还支持RGB活体识别,IR活体识别,口罩检测,闭眼检测,遮挡检测,图像质量检测等等功能。更多文档参考虹软文档中心

五、Milvus环境搭建

Milvus最简单的安装方式是通过docker安装。Milvus有CPU版和GPU版,这里以CPU版为例。可以参考Milvus官方参考文档。 https://milvus.io/cn/docs/v1.0.0/milvus_docker-cpu.md

  1. 安装CentOS或Ubuntu,我使用的这个 https://vault.centos.org/7.4.1708/isos/x86_64/CentOS-7-x86_64-DVD-1708.iso

  2. 安装Docker,使用官方安装脚本自动安装

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
  1. 拉取 Milvus 镜像
sudo docker pull milvusdb/milvus:1.0.0-cpu-d030521-1ea92e
  1. 下载Milvus配置文件
mkdir -p /home/$USER/milvus/conf
cd /home/$USER/milvus/conf
wget https://raw.githubusercontent.com/milvus-io/milvus/v1.0.0/core/conf/demo/server_config.yaml

如果无法通过 wget 命令下载配置文件,也可以在 /home/$USER/milvus/conf目录下创建 server_config.yaml 文件,然后将 server_config.yaml文件 的内容复制到你创建的配置文件中。

  1. 启动 Milvus Docker 容器
sudo docker run -d --name milvus_cpu_1.0.0 \
-p 19530:19530 \
-p 19121:19121 \
-v /home/$USER/milvus/db:/var/lib/milvus/db \
-v /home/$USER/milvus/conf:/var/lib/milvus/conf \
-v /home/$USER/milvus/logs:/var/lib/milvus/logs \
-v /home/$USER/milvus/wal:/var/lib/milvus/wal \
milvusdb/milvus:1.0.0-cpu-d030521-1ea92e

使用sudo docker ps确认 Milvus 运行状态。

如果Milvus没有正常运行,可以通过sudo docker logs milvus_cpu_1.0.0查看错误日志。

如果CPU不支持SSE42、AVX、AVX2、AVX512中的一个,可能无法启动Milvus。

如需安装GPU版,参考GPU版安装

六、快速检索实现

人脸识别流程简介

前面已经介绍了使用虹软SDK人脸识别的基本流程,现在的人脸识别基本上流程都是一样的。这里再简单说明一下一般人脸识别的三个步骤:

1. 人脸检测

给出图像,获取图像中人脸的位置。有的也会获取人脸的一些关键点、角度等信息,用来对人脸进行对齐。

2. 提取特征

将检测出来的人脸图像截取出来,通过神经网络进行特征提取。提取出来的通常是一个128维或者256维的特征向量(通常还会加入归一化等操作)。

3. 特征对比

将上一步提取出来的特征向量进行对比,计算两个向量的距离,再对距离简单的处理就可以得到两个人脸相似度。常见的相似度计算方法有欧氏距离、余弦相似度等。

快速检索

通常1:N人脸搜索最常见的办法是直接暴力搜索,对人脸库中全部人脸都进行对比,找出相似度最高的k个。虹软SDK提供了ASFFaceFeatureCompare来对两个人脸特征向量进行对比。如果人脸库过大,搜索的速度无疑会变慢,在一些对实时性要求高的场景下将很难有好的表现。通过一些向量相似度搜索的算法,可以在短的时间内对大量数据进行相似度计算,找出相似度最高的。本文使用虹软人脸识别SDK进行人脸检测和人脸特征提取。提取出来的人脸特征向量使用Milvus进行检索。

虹软SDK如何获取特征向量

废话不多说,直接上结果。

C++版本虹软SDK中,人脸特征使用结构体ASF_FaceFeature结构体存储。

typedef struct {
MByte* feature; // 人脸特征信息
MInt32 featureSize; // 人脸特征信息长度
}ASF_FaceFeature, *LPASF_FaceFeature;

查看多个feature指向的内存,稍微对机器学习有过了解的人就可以很容易的发现规律,这些数据除了前两个整数,后面的都是的浮点数,明显是经过归一化后的特征向量。

结论:feature指向的是一个float类型的数组。前8个字节固定是浮点数类型的2004,78(可能用于区别SDK的不同版本) 。后面的2048个字节是512个浮点数。如果ASFFaceFeatureExtract设置的registerOrNot参数为false,那么这512个数据的前256个是0。

另外,如果registerOrNot设置为false的话,前256个特征向量全部为0,可以忽略。我们只需要把后256个特征向量复制出来就可以了。

//registerOrNot设置为false时,只复制后256个向量即可
float data[256];
memcpy(data, f.feature + 8 + 1024, 1024);

到这里我们已经获取到了虹软SDK提取出来的人脸特征向量。

批量提取特征向量并插入Milvus

将CelebA数据集里面的人脸照片提取出特征向量并保存到文件中。如果使用多线程提取的话,最后copy /b *.txt res.txt即可合并成一个。

#include "FaceEngine.h"
#include <string>
#include <atomic>
#include <fstream>
#include "TP.cpp"
#include <windows.h> atomic_int n = 0; void task(int start, int end , int index)
{
ofstream save("D:/Face/feature/" + to_string(index) + ".txt");
//调用前请先确保已经激活
FaceEngine x(ASF_DETECT_MODE_IMAGE, ASF_OP_0_ONLY, 1);
char file[50] = { 0 };
for (int i = start; i <= end; i++)
{
sprintf(file, "D:/Face/img_celeba/%06d.jpg", i);
Mat img = imread(file);
auto faces = x.DetectFace(img);
if (faces.faceNum == 1)
{
auto face = x.GetSingleFace(faces,0);
auto f = x.GetFaceFeature(img,face);
float data[256];
memcpy(data, f.feature + 8 + 1024, 1024);
save << i << "|";
for (int u = 0; u < 256; u++)
save << data[u] << "|";
save << endl;
}
n++;
} } int main()
{
ThreadPool pool(2);
pool.AddTask(bind(task, 1, 100000, 1));
pool.AddTask(bind(task, 100001, 202599, 2)); while (n < 202599)
{
cout << "\r" << n << "\t" << n * 100 / 202599 << "\t";
Sleep(1000);
}
}

为了简化虹软SDK的使用,我对SDK简单封装了一下,还有一个简单的线程池实现,可以在文末链接下载。

上面已经将特征向量保存到txt中了,接下来将20万特征向量插入Milvus(20万数据量有点少,不过由于我的电脑配置较低,提取20万个人脸的特征向量花了接近2个小时,这里就不添加过多数据了。有条件的话可以添加更多数据。)。也可以通过编译Milvus的C++ SDK,提取插入一步到位。 首先安装一下pymilvus pip3 install pymilvus==1.0.1

from milvus import Milvus, IndexType, MetricType, Status
import numpy as np m = Milvus(host='IP', port='19530') # 创建collection
param = {
'collection_name':'face',
'dimension':256,
'index_file_size':256,
'metric_type':MetricType.IP #相似度计算方式使用內积
}
print(m.create_collection(param)) num = 200000
step = 5000
now = 0 def GetBatch(data):
global now ids = np.zeros(step,dtype=np.int32)
vects = np.zeros((step,256),dtype=np.float32) for i in range(step):
tmp = data[i+now].split("|")
ids[i] = int(tmp[0])
for u in range(256):
vects[i][u] = float(tmp[u+1])
now += step
return ids.tolist() , vects.tolist() # 将所以人脸向量插入Milvus
data = open("G:\\feature\\res.txt").readlines()
for i in range(int(num / step)):
ids , vs = GetBatch(data)
res = m.insert(collection_name='face', records=vs, ids=ids)
print(i)

这里要注意的是,Milvus的Python SDK插入时使用的是list,numpy创建的数据需要使用tolist()来转成list

默认插入后是使用的FLAT索引(暴力搜索),暴力搜索的速度最慢,但召回率为100%,如果数据量很大,可以通过建立其他的索引来加快检索速度。在CPU上查询常见的索引有:



了解更多索引,参考Milvus官方文档

创建索引:

# `ivf_param` 是创建索引的参数,`IVF_FLAT`是索引类型。
ivf_param = {'nlist': 16384}
print(m.create_index('face', IndexType.IVF_FLAT, ivf_param))

查询

data = open("G:\\feature\\res.txt").readlines()
ids , vs = GetBatch(data) idx = int(input("index:")) # 输入一个下标,从Batch中取出第idx个进行查询
print("id:" , ids[idx]) # 输出下标为idx的特征向量的id,这里的id就是文件名。14就是CelebA数据集中的000014.jpg
search_param = {'nprobe': 16}
res = m.search(collection_name='face', query_records=[vs[idx]], top_k=3, params=search_param)
print(res)

查询batch里面随便一项得到结果:

index:12
id: 14
(Status(code=0, message='Search vectors successfully!'), [
[
(id:14, distance:1.0000004768371582),
(id:39306, distance:0.8084499835968018),
(id:109420, distance:0.776871919631958)
]
])

Milvus搜索到的tok3个相似度最高的id是14、39306、109420(id就是文件名编号,14就是000014.jpg)。14就是这个文件本身,所以计算出来內积为1,39306和109420的內积分别是0.8084、0.7769。这三个id对应的图片分别是:

可见,Top3的人脸确实为同一个值。可以根据计算出来的distance设置一个阈值来判断是否为同一个人。阈值可以设置为0.55-0.6左右,有需要的话可以自行测试确定一个更合适的阈值。

七、性能说明

使用虹软SDK的ASFFaceFeatureCompare接口单线程检索20万人脸需要156ms。Milvus(运行在虚拟机中)使用默认FLAT索引,检索20万人脸需要168ms,建立IVF_FLAT 索引并且搜索nprobe设置为16时耗时70ms。

在高并发场景下,使用GPU版的Milvus可以很大程度的减少搜索时间,并且可以通过设置参数获得一个理想的召回率。但是在低并发且数据量少的时候,推荐之间使用ASFFaceFeatureCompare接口。

八、补充

关于相似度计算方式,Milvus中常用的有两种:

  • 欧氏距离(L2)

  • 内积 (IP)

当向量归一化后,这两种计算方式是等价的。虹软SDK提取的人脸特征是经过归一化的,所以选择这两种计算方式都是可以的。

全部代码已经上传github https://github.com/Memory2414/milvus-arcface

如果你连OpenCV环境也懒得配置,也可以在这里下载已经配置好的虹软SDK和OpenCV的环境(VS2013),提取码:atkw。

了解更多人脸识别产品相关内容请到虹软视觉开放平台

虹软人脸识别SDK接入Milvus实现海量人脸快速检索的更多相关文章

  1. AI人脸识别SDK接入 — 参数优化篇(虹软)

    引言 使用了虹软公司免费的人脸识别算法,感觉还是很不错的,当然,如果是初次接触的话会对一些接口的参数有些疑问的.这里分享一下我对一些参数的验证结果(这里以windows版本为例,linux.andro ...

  2. 虹软AI 人脸识别SDK接入 — 参数优化篇

    引言 使用了免费的人脸识别算法,感觉还是很不错的,但是初次接触的话会对一些接口的参数有些疑问的.这里分享一下我对一些参数的验证结果(这里以windows版本为例,linux.android基本一样), ...

  3. 虹软人脸识别SDK在网络摄像头中的实际应用

    目前在人脸识别领域中,网络摄像头的使用很普遍,但接入网络摄像头和人脸识别SDK有一定门槛,在此篇中介绍过虹软人脸识别SDK的接入流程,本文着重介绍网络摄像头获取视频流并处理的流程(红色框内),以下内容 ...

  4. 人脸识别SDK小结

    Face++人脸识别 进入官网 Face++ 致力于研发世界最好的人脸技术,提供免费的API和SDK供企业和开发者调用,更有灵活的定制化服务满足不同需求.已有多家公司使用Face++技术服务,完成包括 ...

  5. 基于虹软的Android的人脸识别SDK使用测试

    现在有很多人脸识别的技术我们可以拿来使用:但是个人认为还是离线端的SDK比较实用:所以个人一直在搜集人脸识别的SDK:原来使用开源的OpenCV:最近有个好友推荐虹软的ArcFace, 闲来无事就下来 ...

  6. Java版 人脸识别SDK demo

    虹软人脸识别SDK之Java版,支持SDK 1.1+,以及当前最新版本2.0,滴滴,抓紧上车! 前言 由于业务需求,最近跟人脸识别杠上了,本以为虹软提供的SDK是那种面向开发语言的,结果是一堆dll· ...

  7. Java版 人脸识别SDK dem

    虹软人脸识别SDK之Java版,支持SDK 1.1+,以及2.0版本,滴滴,抓紧上车! 前言由于业务需求,最近跟人脸识别杠上了,本以为虹软提供的SDK是那种面向开发语言的,结果是一堆dll······ ...

  8. Java离线人脸识别SDK 支持arcface 2.0 最新版

    虹软人脸识别SDK之Java版,支持SDK 1.1+,以及当前最新版本2.0,滴滴,抓紧上车! JDK SDK Win release license status 前言 由于业务需求,最近跟人脸识别 ...

  9. 百度离线人脸识别sdk的使用

    1.1下载sdk运行 百度离线人脸识别sdk的使用 1.2配置环境 添加至项目,可以拖动复制或者以类库形式添加face-resource此文件夹 放到根目录上一层 激活文件与所有dll引用放到根目录嫌 ...

随机推荐

  1. POJ1324贪吃蛇(状态压缩广搜)

    题意:       给你一个地图,有的地方能走,有的地方不能走,然后给你一条蛇,问你这条蛇的头部走到1,1的位置的最少步数,注意,和贪吃蛇不太一样,就是蛇咬到自己身体的那个地方,具体怎么不一样自己模拟 ...

  2. [CTF]凯撒密码

    [CTF]凯撒密码 ---------------------  作者:___Blue_H  来源:CSDN  原文:https://blog.csdn.net/qq_37653144/article ...

  3. 【python】Leetcode每日一题-丑数2

    [python]Leetcode每日一题-丑数2 [题目描述] 给你一个整数 n ,请你找出并返回第 n 个 丑数 . 丑数 就是只包含质因数 2.3 和/或 5 的正整数. 示例1: 输入:n = ...

  4. 23.Quick QML-简单且好看的图片浏览器-支持多个图片浏览、缩放、旋转、滑轮切换图片

    之前我们已经学习了Image.Layout布局.MouseArea.Button.GroupBox.FileDialog等控件. 所以本章综合之前的每章的知识点,来做一个图片浏览器,使用的Qt版本为Q ...

  5. 使用BeanUtils.copyProperties踩坑经历

    1. 原始转换 提起对象转换,每个程序员都不陌生,比如项目中经常涉及到的DO.DTO.VO之间的转换,举个例子,假设现在有个OrderDTO,定义如下所示: public class OrderDTO ...

  6. MzzTxx——团队贡献分分配方案

    项目 内容 这个作业属于哪个课程 2021春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 团队贡献分分配规则制定 我在这个课程的目标是 进一步提升工程化开发能力,积累团队协作经验,熟悉全栈 ...

  7. mysql枚举和集合

    create table consumer( id int, name char(16), sex enum('male','female','other'), level enum('vip1',' ...

  8. [设计模式] 读懂UML图

    类之间关系(由强到弱) realize(继承):三角+实线(指向类),继承类(SUV是一种汽车) generalization(实现):三角+虚线(指向接口),实现接口(汽车是一种车) composi ...

  9. Scala 安装与配置

    安装准备 由于 Scala 运行于 Java 平台,因此 Scala 之前需要确保系统安装 JDK Windows 中安装 Scala 1. 下载 scala-2.11.2.msi 安装包 点击安装文 ...

  10. Linux_搭建NFS服务(基础)

    [RHEL8]-NFSserver :[Centos7]-NFSclient !!!测试环境我们首关闭防火墙和selinux(NFSserver和NFSclient都需要) [root@localho ...