人脸识别算法原理过程详解
本文为转载内容,由于找不到源作者链接,故特此说明。
人脸识别各算法详解
最近,由于工作需要,为了找到一款高效的人脸识别算法,对各种人脸识别算法都研究了一番,以下记录的是各算法的理论基础。
一.MTCNN
本文章主要介绍MTCNN算法的流程,MTCNN主要由三个框架组成,分别是PNet,RNet,ONet。下面将分别介绍这三个部分。
PNet
由上面这张图可以得到,一张12x12x3的图片最终的输出的结果是1x1x32的特征图,再分成三条支路,用于人脸分类、边框回归、人脸特征点定位。
RNet
1>. dx,dy:bbox的相对本身起点坐标(0,0)。2>. edx,edy:bbox的相对本身终点坐标(tmpw-1, tmph-1)。3>. x,y : 原始图片的bbox起点。4>. ex,ey:原始图片的bbox结束点。
在生成的过程还会有检测,避免bbox的坐标超出原始图片或者为负值;接下来遍历这个数组,将里面的bbox从原始图片里面抠出来,resize成24x24同时进行归一化。
完成了前面的操作后就是将24x24的图片喂入RNet了,下图表示的是RNet的结构:
可以看出RNet最后是采用的全连接层,这也是为什么喂入的图片统一成24x24大小。
ONet
将48x48x3的图片喂入ONet后输出的是3x3x128的特征图,经过全连接层后同样是有着三条支路。三条支路的损失函数与PNet、RNet一样,但是三个损失函数的权重比为1:0.5:1。
二.PFLD
PFLD算法,目前主流数据集上达到最高精度、ARM安卓机140fps,模型大小仅2.1M!
其中,黄色曲线包围的是主网络,用于预测特征点的位置;
绿色曲线包围的部分为辅网络,在训练时预测人脸姿态(有文献表明给网络加这个辅助任务可以提高定位精度,具体参考原论文),这部分在测试时不需要。
对于上述影响精度的挑战,修改loss函数在训练时关注那些稀有样本,而提高计算速度和减小模型size则是使用轻量级模型。
Loss函数设计
主网络
辅网络
三. 虹软人脸识别算法
虹软属于人脸检测技术,能够帮助您检测并且定位到影像(图片或者视频)中的人脸。
本文将以这三个库为基础,从人脸注册开始,到人脸识别结束。全程演示人脸识别的流程。
人脸信息是保存在AFR_FSDKFace类中的。这的主要结构为
public static final int FEATURE_SIZE = 22020;byte[] mFeatureData;
如果要进行人脸注册,我们需要定义另外一个类来把人脸信息和姓名关联起来。
class FaceRegist {String mName;List<AFR_FSDKFace> mFaceList;public FaceRegist(String name) {mName = name;mFaceList = new ArrayList<>();}
}
包含特征信息的长度和内容的byte数组。
我们把这些功能定义在类FaceDB中。FaceDB需要包含引擎定义,初始化,把人脸信息保存在版本库和从版本库中读出人脸信息这些功能
初始化引擎
为了程序结构性考虑,我们将人脸识别相关的代码独立出来一个类FaceDB,并定义必要的变量
public static String appid = "bCx99etK9Ns4Saou1EbFdC18xHdY9817EKw****";public static String ft_key = "CopwZarSihp1VBu5AyGxfuLQdRMPyoGV2C2opc****";public static String fd_key = "CopwZarSihp1VBu5AyGxfuLXnpccQbWAjd86S8****";public static String fr_key = "CopwZarSihp1VBu5AyGxfuLexDsi8yyELdgsj4****";String mDBPath;List<FaceRegist> mRegister;AFR_FSDKEngine mFREngine;AFR_FSDKVersion mFRVersion;
public FaceDB(String path) {mDBPath = path;mRegister = new ArrayList<>();mFRVersion = new AFR_FSDKVersion();mUpgrade = false;mFREngine = new AFR_FSDKEngine();AFR_FSDKError error = mFREngine.AFR_FSDK_InitialEngine(FaceDB.appid, FaceDB.fr_key);if (error.getCode() != AFR_FSDKError.MOK) {Log.e(TAG, "AFR_FSDK_InitialEngine fail! error code :" + error.getCode());} else {mFREngine.AFR_FSDK_GetVersion(mFRVersion);Log.d(TAG, "AFR_FSDK_GetVersion=" + mFRVersion.toString());}}
public void destroy() {if (mFREngine != null) {mFREngine.AFR_FSDK_UninitialEngine();}}
实现人脸增加和读取功能
通常人脸库会存放在数据库中,本次我们使用List来进行简单的模拟,并将其保存在文本文件中,需要时从文本中读取,保存时写入到文件中。
public void addFace(String name, AFR_FSDKFace face) {try {//check if already registered.boolean add = true;for (FaceRegist frface : mRegister) {if (frface.mName.equals(name)) {frface.mFaceList.add(face);add = false;break;}}if (add) { // not registered.FaceRegist frface = new FaceRegist(name);frface.mFaceList.add(face);mRegister.add(frface);}if (!new File(mDBPath + "/face.txt").exists()) {if (!saveInfo()) {Log.e(TAG, "save fail!");}}//save nameFileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true);ExtOutputStream bos = new ExtOutputStream(fs);bos.writeString(name);bos.close();fs.close();//save featurefs = new FileOutputStream(mDBPath + "/" + name + ".data", true);bos = new ExtOutputStream(fs);bos.writeBytes(face.getFeatureData());bos.close();fs.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
public boolean loadFaces(){if (loadInfo()) {try {for (FaceRegist face : mRegister) {Log.d(TAG, "load name:" + face.mName + "'s face feature data.");FileInputStream fs = new FileInputStream(mDBPath + "/" + face.mName + ".data");ExtInputStream bos = new ExtInputStream(fs);AFR_FSDKFace afr = null;do {if (afr != null) {if (mUpgrade) {//upgrade data.}face.mFaceList.add(afr);}afr = new AFR_FSDKFace();} while (bos.readBytes(afr.getFeatureData()));bos.close();fs.close();}return true;} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}} else {if (!saveInfo()) {Log.e(TAG, "save fail!");}}return false;}
实现业务逻辑
实现人脸注册功能
人脸识别的前提条件就是人脸信息要先注册到人脸库中,注册人脸库
第一步当然是获取待注册的照片,我们可以可以使用摄像头,也可以使用照片。我们使用AlertDialog弹出选择框
new AlertDialog.Builder(this).setTitle("请选择注册方式").setIcon(android.R.drawable.ic_dialog_info).setItems(new String[]{"打开图片", "拍摄照片"}, this).show();
switch (which){case 1://摄像头Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE");ContentValues values = new ContentValues(1);values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");mPath = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT, mPath);startActivityForResult(getImageByCamera, REQUEST_CODE_IMAGE_CAMERA);break;case 0://图片Intent getImageByalbum = new Intent(Intent.ACTION_GET_CONTENT);getImageByalbum.addCategory(Intent.CATEGORY_OPENABLE);getImageByalbum.setType("image/jpeg");startActivityForResult(getImageByalbum, REQUEST_CODE_IMAGE_OP);break;default:;
}
if (requestCode == REQUEST_CODE_IMAGE_OP && resultCode == RESULT_OK) {mPath = data.getData();String file = getPath(mPath);//TODO: add image coversion}
在上面的代码中,我们获取到了我们需要的图像数据bmp,把图片取出来
我们在Application类用函数 decodeImage中实现这段代码
public static Bitmap decodeImage(String path) {Bitmap res;try {ExifInterface exif = new ExifInterface(path);int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);BitmapFactory.Options op = new BitmapFactory.Options();op.inSampleSize = 1;op.inJustDecodeBounds = false;//op.inMutable = true;res = BitmapFactory.decodeFile(path, op);//rotate and scale.Matrix matrix = new Matrix();if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {matrix.postRotate(90);} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {matrix.postRotate(180);} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {matrix.postRotate(270);}Bitmap temp = Bitmap.createBitmap(res, 0, 0, res.getWidth(), res.getHeight(), matrix, true);Log.d("com.arcsoft", "check target Image:" + temp.getWidth() + "X" + temp.getHeight());if (!temp.equals(res)) {res.recycle();}return temp;} catch (Exception e) {e.printStackTrace();}return null;
}
public class AFD_FSDKFace {Rect mRect;int mDegree;}
在此之前我们需要注意虹软人脸SDK使用的图像格式是NV21的格式,所以我们需要将获取到的图像转化为对应的格式。在Android_extend.jar中提供了对应的转换函数
byte[] data = new byte[mBitmap.getWidth() * mBitmap.getHeight() * 3 / 2];ImageConverter convert = new ImageConverter();convert.initial(mBitmap.getWidth(), mBitmap.getHeight(), ImageConverter.CP_PAF_NV21);if (convert.convert(mBitmap, data)) {Log.d(TAG, "convert ok!");}convert.destroy();
现在我们就可以调用AFD_FSDK_StillImageFaceDetection方法了
err = engine.AFD_FSDK_StillImageFaceDetection(data, mBitmap.getWidth(), mBitmap.getHeight(), AFD_FSDKEngine.CP_PAF_NV21, result);
绘出人脸框
在List<AFD_FSDKFace>中保存了检测到的人脸的位置信息和深度信息。
我们可以将检测到的人脸位置信息在图片上用一个矩形框绘制出来表示检测到的人脸信息。
Canvas canvas = mSurfaceHolder.lockCanvas();if (canvas != null) {Paint mPaint = new Paint();boolean fit_horizontal = canvas.getWidth() / (float)src.width() < canvas.getHeight() / (float)src.height() ? true : false;float scale = 1.0f;if (fit_horizontal) {scale = canvas.getWidth() / (float)src.width();dst.left = 0;dst.top = (canvas.getHeight() - (int)(src.height() * scale)) / 2;dst.right = dst.left + canvas.getWidth();dst.bottom = dst.top + (int)(src.height() * scale);} else {scale = canvas.getHeight() / (float)src.height();dst.left = (canvas.getWidth() - (int)(src.width() * scale)) / 2;dst.top = 0;dst.right = dst.left + (int)(src.width() * scale);dst.bottom = dst.top + canvas.getHeight();}canvas.drawBitmap(mBitmap, src, dst, mPaint);canvas.save();canvas.scale((float) dst.width() / (float) src.width(), (float) dst.height() / (float) src.height());canvas.translate(dst.left / scale, dst.top / scale);for (AFD_FSDKFace face : result) {mPaint.setColor(Color.RED);mPaint.setStrokeWidth(10.0f);mPaint.setStyle(Paint.Style.STROKE);canvas.drawRect(face.getRect(), mPaint);}canvas.restore();mSurfaceHolder.unlockCanvasAndPost(canvas);break;}
}
将人脸注册到人脸库
检测到了人脸,我们可以输入相应的描述信息,加入到人脸库中。
public void addFace(String name, AFR_FSDKFace face) {try {//check if already registered.boolean add = true;for (FaceRegist frface : mRegister) {if (frface.mName.equals(name)) {frface.mFaceList.add(face);add = false;break;}}if (add) { // not registered.FaceRegist frface = new FaceRegist(name);frface.mFaceList.add(face);mRegister.add(frface);}if (!new File(mDBPath + "/face.txt").exists()) {if (!saveInfo()) {Log.e(TAG, "save fail!");}}//save nameFileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true);ExtOutputStream bos = new ExtOutputStream(fs);bos.writeString(name);bos.close();fs.close();//save featurefs = new FileOutputStream(mDBPath + "/" + name + ".data", true);bos = new ExtOutputStream(fs);bos.writeBytes(face.getFeatureData());bos.close();fs.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
}
err = engine.AFD_FSDK_UninitialFaceEngine();
Log.d("com.arcsoft", "AFD_FSDK_UninitialFaceEngine =" + err.getCode());
<?xml version="1.0" encoding="utf-8"?>
<com.guo.android_extend.widget.CameraSurfaceViewandroid:id="@+id/surfaceView"android:layout_width="1dp"android:layout_height="1dp"/><com.guo.android_extend.widget.CameraGLSurfaceViewandroid:id="@+id/glsurfaceView"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerInParent="true"/><ImageViewandroid:id="@+id/imageView"android:layout_width="120dp"android:layout_height="120dp"android:layout_marginLeft="10dp"android:layout_marginTop="10dp"/><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@+id/imageView"android:layout_alignRight="@+id/imageView"android:layout_below="@+id/imageView"android:layout_marginTop="10dp"android:text="@string/app_name"android:textAlignment="center"/><TextViewandroid:id="@+id/textView1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@+id/imageView"android:layout_alignRight="@+id/imageView"android:layout_below="@+id/textView"android:layout_marginTop="10dp"android:text="@string/app_name"android:textAlignment="center"/>
因为引擎需要的图像格式是NV21的,所以需要将摄像头中的图像格式预设置为NV21
public Camera setupCamera() {// TODO Auto-generated method stubmCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);try {Camera.Parameters parameters = mCamera.getParameters();parameters.setPreviewSize(mWidth, mHeight);parameters.setPreviewFormat(ImageFormat.NV21);for( Camera.Size size : parameters.getSupportedPreviewSizes()) {Log.d(TAG, "SIZE:" + size.width + "x" + size.height);}for( Integer format : parameters.getSupportedPreviewFormats()) {Log.d(TAG, "FORMAT:" + format);}List<int[]> fps = parameters.getSupportedPreviewFpsRange();for(int[] count : fps) {Log.d(TAG, "T:");for (int data : count) {Log.d(TAG, "V=" + data);}}mCamera.setParameters(parameters);} catch (Exception e) {e.printStackTrace();}if (mCamera != null) {mWidth = mCamera.getParameters().getPreviewSize().width;mHeight = mCamera.getParameters().getPreviewSize().height;}return mCamera;
}
从摄像头识别人脸,需要使用FT库,FT库在人脸跟踪算法上对人脸检测部分进行了优化,是专门为视频处理而优化的库。
初始化人脸检测引擎(FT)
和FD一样,我们需要初始化人脸识别FT引擎。
Log.d(TAG, "AFT_FSDK_InitialFaceEngine =" + err.getCode());
err = engine.AFT_FSDK_GetVersion(version);
Log.d(TAG, "AFT_FSDK_GetVersion:" + version.toString() + "," + err.getCode());
在摄像头的预览事件处理函数中,先调用FT的人脸识函数函数,然后再调用FR中的人脸信息特征提取数函数。
AFT_FSDKError err = engine.AFT_FSDK_FaceFeatureDetect(data, width, height, AFT_FSDKEngine.CP_PAF_NV21, result);AFR_FSDKError error = engine.AFR_FSDK_ExtractFRFeature(mImageNV21, mWidth, mHeight, AFR_FSDKEngine.CP_PAF_NV21,mAFT_FSDKFace.getRect(), mAFT_FSDKFace.getDegree(), result);
这里面的result中保存了人脸特征信息。我们可以将其保存下来或下来并与系统中的其它信息进行对比。
AFR_FSDKMatching score = new AFR_FSDKMatching();
float max = 0.0f;
String name = null;
for (FaceDB.FaceRegist fr : mResgist) {for (AFR_FSDKFace face : fr.mFaceList) {error = engine.AFR_FSDK_FacePairMatching(result, face, score);Log.d(TAG, "Score:" + score.getScore() + ", AFR_FSDK_FacePairMatching=" + error.getCode());if (max < score.getScore()) {max = score.getScore();name = fr.mName;}}
}
当score的特征信息大于0.6时,我们就可以认为匹配到了人脸。显示人脸匹配信息。
上面的循环中,可以看到,是遍历了真个库进行寻找。我们的目的是为了演示,实际情况下,我们可以在找到一个匹配值比较高的人脸后,就跳出循环。
人脸识别算法原理过程详解相关推荐
- 抖音推荐算法原理全文详解
阅读目录 一.系统概览 二.内容分析 三.用户标签 四.评估分析 五.内容安全 抖音推荐算法原理全文详解 本次分享将主要介绍今日头条推荐系统概览以及内容分析.用户标签.评估分析,内容安全等原理. 回到 ...
- 今日头条推荐算法原理全文详解之一
本次分享将主要介绍今日头条推荐系统概览以及内容分析.用户标签.评估分析,内容安全等原理. 今日头条推荐算法原理全文详解 今日头条 数据分析 产品经理 产品 好文分享 第1张 一.系统概览 推荐系统,如 ...
- 今日头条推荐算法原理全文详解之四
三.用户标签 内容分析和用户标签是推荐系统的两大基石.内容分析涉及到机器学习的内容多一些,相比而言,用户标签工程挑战更大. 今日头条推荐算法原理全文详解 今日头条 数据分析 产品经理 产品 好文分享 ...
- 今日头条、抖音推荐算法原理全文详解
之前头条召开了一场分享交流会.资深算法架构师.中国科学技术大学计算机博士曹欢欢,在今日头条总部带来了题为<让算法公开透明>的分享,消除社会各界对算法的一些误解,同时接受意见和建议. 本次分 ...
- 目标检测 /yolo算法原理的详解
前言 谈到计算机视觉时,我们都会联想到图像分类,图像分类是计算机视觉最基本的任务之一,在图像分类的基础上,我们还有更复杂的任务,比如目标检测,物体定位,图像分割等,本文主要讲目标检测,目标检测是分类与 ...
- 【推荐系统】今日头条推荐算法原理全文详解
如今,算法分发已经逐步成为信息平台.搜索引擎.浏览器.社交软件等几乎所有软件的标配,但同时也开始面临各种不同的质疑.挑战与误解. 2018年1月,今日头条资深算法架构师曹欢欢博士,首次公开今日头条的算 ...
- 人脸识别:FaceNet论文详解
引入 随着深度学习的出现,CV领域突破很多,甚至掀起了一股CV界的创业浪潮,当次风口浪尖之时,Google岂能缺席.特贡献出FaceNet再次刷新LFW上人脸验证的效果记录. 本文是阅读FaceNet ...
- KCF跟踪算法原理 入门详解
一.算法介绍 KCF全称为Kernel Correlation Filter 核相关滤波算法.是在2014年由Joao F. Henriques, Rui Caseiro, Pedro Martins ...
- opencvkcf算法改进_KCF跟踪算法原理 入门详解
一.算法介绍 KCF全称为Kernel Correlation Filter 核相关滤波算法.是在2014年由Joao F. Henriques, Rui Caseiro, Pedro Martins ...
最新文章
- xcode6 dyld_sim is not owned by root
- 华为旗舰陆续升级鸿蒙系统,华为鸿蒙重磅来袭:今年4月起 华为旗舰手机将陆续升级鸿蒙系统!...
- python利器怎么编程-Python任务调度利器之APScheduler详解
- C#获取数据库表信息,列信息
- tableau做rfm分析_RFM模型客户关系管理
- CG-CTF-Web-bypass again
- lua判断字符不为空或空格_Lua判断字符串前缀是否为指定字符的3种方法
- 蔚来:首台ET7白车身合肥工厂下线
- 使用FileUpload上传图片到数据库
- JAVA中super和this关键字的区别
- 基于边缘的主动轮廓模型——从零到一用python实现snake
- 解决Ubuntu 显卡驱动升级导致的 显卡驱动和内核版本不匹配的问题
- March 7th Wednesday (三月 七日 水曜日)
- CocoaLumberjack的ios应用开发使用指南
- unity加速传感器的应用
- 用Multisim对振幅调制器进行仿真
- 中国石油天然气股份有限公司2008年半年度报告摘要(业绩公告)
- 亚马逊能做些什么提升自己的产品成为爆款呢?
- tekla自动出图——mark
- 区块链学习笔记21——智能合约