前言

最近公司项目比较空,花了点时间写了个人脸识别的app,可以查看你的性别、年龄、颜值、情绪等信息,利用的是 Face++ 的人脸识别API。本项目采用了 MVP 的架构,使用了 Retrofit、RxJava、Dagger、EventBus 等框架进行开发和解耦,利用 MaterialDesign 进行UI上的布局设计。

主要的功能就是拍照,然后将照片传至 Face++ 服务器,进行人脸识别,获取返回的信息,对信息进行处理。将人脸在照片上标出,并将信息展示出来。

话不多说,先来看一下 app 的效果(吴彦祖还是帅啊,哈哈)。

面部识别主界面

面部识别详情界面

多人脸识别

项目我已经放在 github 上,clone 下来即可编译运行。github 地址: reggie1996 - FaceDetect 。下面文章主要介绍的是本项目的开发过程和碰到的坑。

过程

项目的整个流程很简单无非就是三步,拍照片,传照片获取数据,然后对数据进行处理展示。

拍照获取照片

拍照需要获取系统权限,我封装了一个方法,来判断App是否有拍照相关的权限,如果没有就去动态请求权限,并返回 false,如果有就返回 true。

public static boolean checkAndRequestPermission(Context context, int requestCode) {

if (context.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED

|| context.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED

|| context.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

((Activity) context).requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, requestCode);

return false;

}else {

return true;

}

}

获取到拍照权限后就可以拍照了,但是拍照得到的照片我们需要通过 FileProvider 获取。FileProvider 相关的内容就不作介绍了,Android 7.0 之后都得用这个。

android:name="android.support.v4.content.FileProvider"

android:authorities="com.chaochaowu.facedetect.provider"

android:exported="false"

android:grantUriPermissions="true">

android:name="android.support.FILE_PROVIDER_PATHS"

android:resource="@xml/file_paths" />

拍照之后从文件中读取照片,我们可以得到一个 BitMap 对象。这里就有一个很大的坑,如果手机是三星的话,照片从文件里读出来,最后得到的照片会被旋转 90°!!!,这个贼坑啊,调了我好久,以为是自己手机的故障,后来网上查了一下,也请教了一下前辈,原来三星的手机都有这个问题,所以说我们要对文件中取出来的照片进行一下处理。

/**

* 读取图片的旋转的角度

*

* @param path 图片绝对路径

* @return 图片的旋转角度

*/

public static int getBitmapDegree(String path) {

int degree = 0;

try {

// 从指定路径下读取图片,并获取其EXIF信息

ExifInterface exifInterface = new ExifInterface(path);

// 获取图片的旋转信息

int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,

ExifInterface.ORIENTATION_NORMAL);

switch (orientation) {

case ExifInterface.ORIENTATION_ROTATE_90:

degree = 90;

break;

case ExifInterface.ORIENTATION_ROTATE_180:

degree = 180;

break;

case ExifInterface.ORIENTATION_ROTATE_270:

degree = 270;

break;

default:

degree = 0;

break;

}

} catch (IOException e) {

e.printStackTrace();

}

return degree;

}

/**

* 将图片按照某个角度进行旋转

*

* @param bm 需要旋转的图片

* @param degree 旋转角度

* @return 旋转后的图片

*/

public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {

Bitmap returnBm = null;

// 根据旋转角度,生成旋转矩阵

Matrix matrix = new Matrix();

matrix.postRotate(degree);

try {

// 将原始图片按照旋转矩阵进行旋转,并得到新的图片

returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);

} catch (OutOfMemoryError | Exception e) {

e.printStackTrace();

}

if (returnBm == null) {

returnBm = bm;

}

if (bm != returnBm) {

bm.recycle();

}

return returnBm;

}

封装了两个方法,依次调用可以解决三星手机照片的问题。两个方法主要的工作就是,得到取出来的照片被旋转的角度,然后再将角度旋转回去,就可以得到原来的照片。因为并不是所有的手机在获取照片时,照片都会被旋转,所以得先判断一下照片有没有被旋转,再决定是否需要将它旋转调整。

行,这样最后就获得到了正确的 BitMap 照片,可以进行下一步了。

传照片获取数据

传照片获取数据,主要是运用了 Retrofit 和 RxJava 的封装。请求的参数可以参考 Face++ 的官方文档。

/**

* retrofit 面部识别请求的网络服务

* @author chaochaowu

*/

public interface FaceppService {

/**

* @param apikey

* @param apiSecret

* @param imageBase64

* @param returnLandmark

* @param returnAttributes

* @return

*/

@POST("facepp/v3/detect")

@FormUrlEncoded

Observable getFaceInfo(@Field("api_key") String apikey,

@Field("api_secret") String apiSecret,

@Field("image_base64") String imageBase64,

@Field("return_landmark") int returnLandmark,

@Field("return_attributes") String returnAttributes);

}

照片需要进行 base64 转码后上传至服务器,封装了一个照片base64转码方法。

public static String base64(Bitmap bitmap){

ByteArrayOutputStream baos = new ByteArrayOutputStream();

bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

byte[] bytes = baos.toByteArray();

return Base64.encodeToString(bytes, Base64.DEFAULT);

}

处理完成之后就可以进行网络请求获取数据。

@Override

public void getDetectResultFromServer(final Bitmap photo) {

String s = Utils.base64(photo);

faceppService.getFaceInfo(BuildConfig.API_KEY, BuildConfig.API_SECRET, s, 1, "gender,age,smiling,emotion,beauty")

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Observer() {

@Override

public void onSubscribe(Disposable d) {

mView.showProgress();

}

@Override

public void onNext(FaceppBean faceppBean) {

handleDetectResult(photo,faceppBean);

}

@Override

public void onError(Throwable e) {

mView.hideProgress();

}

@Override

public void onComplete() {

mView.hideProgress();

}

});

}

Face++ 服务器会对我们上传的照片进行处理,分析照片中的人脸信息,并以 json 形式返回,返回的数据将被放入我们定义的bean类中。

/**

* 面部识别结果的bean

* @author chaochaowu

*/

public class FaceppBean {

/**

* image_id : Dd2xUw9S/7yjr0oDHHSL/Q==

* request_id : 1470472868,dacf2ff1-ea45-4842-9c07-6e8418cea78b

* time_used : 752

* faces : [{"landmark":{"mouth_upper_lip_left_contour2":{"y":185,"x":146},"contour_chin":{"y":231,"x":137},"right_eye_pupil":{"y":146,"x":205},"mouth_upper_lip_bottom":{"y":195,"x":159}},"attributes":{"gender":{"value":"Female"},"age":{"value":21},"glass":{"value":"None"},"headpose":{"yaw_angle":-26.625063,"pitch_angle":12.921974,"roll_angle":22.814377},"smile":{"threshold":30.1,"value":2.566890001296997}},"face_rectangle":{"width":140,"top":89,"left":104,"height":141},"face_token":"ed319e807e039ae669a4d1af0922a0c8"}]

*/

private String image_id;

private String request_id;

private int time_used;

private List faces;

...显示部分内容

bean 类中有人脸识别得到的 性别、年龄、颜值、情绪等信息,还有每张人脸在照片中的坐标位置。接下来的工作就是对这些数据进行处理。

获取信息后的数据处理

数据的处理主要就两件事,一个是将数据以文字的形式展现,这个很简单,就不介绍了,还有一个就是将人脸在照片中标示出来,这个需要对 BitMap 进行处理,利用数据中人脸在照片中的坐标位置,我们用方框将人脸标识出来。

private Bitmap markFacesInThePhoto(Bitmap bitmap, List faces) {

Bitmap tempBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

Canvas canvas = new Canvas(tempBitmap);

Paint paint = new Paint();

paint.setColor(Color.RED);

paint.setStyle(Paint.Style.STROKE);

paint.setStrokeWidth(10);

for (FaceppBean.FacesBean face : faces) {

FaceppBean.FacesBean.FaceRectangleBean faceRectangle = face.getFace_rectangle();

int top = faceRectangle.getTop();

int left = faceRectangle.getLeft();

int height = faceRectangle.getHeight();

int width = faceRectangle.getWidth();

canvas.drawRect(left, top, left + width, top + height, paint);

}

return tempBitmap;

}

封装了一个方法,运用 Canvas 在照片上进行绘制,因为照片中的人脸可能不止一个,所以用for循环遍历。获取人脸在照片中的坐标,利用人脸左上角的坐标以及人脸的宽高,在照片中绘制一个方框将人脸标出。

剩余信息我这边采用 RecyclerView 来展示。左右滑动可以查看每张人脸的信息。RecyclerView 的 item 上展示的是简要信息,可以点击 item 进入详情页面查看面部识别的详细信息。RecyclerView 以及详情界面的实现就不作介绍了,很基本的操作。我这边也就只使用了 SharedElement 让界面切换看起来舒服一点。具体的实现可以看 github 上的代码。

其他就没什么操作了,还可以看一下我的项目架构。由于用了各种框架进行解耦,所以代码文件数量变多了,但是单个文件中的代码会变少一点,清晰易读一点,这也是解耦的目的,也方便之后的维护。

具体实现的细节可以看 github 上面的代码~

最后

写完这个APP后,我一直在思考一个问题,APP给吴彦祖的颜值打分80多,那100分的颜值会是怎样?

感兴趣的朋友可以把代码下载下来玩一下,测一下自己或者是朋友的颜值,嘿嘿。github 地址: reggie1996 - FaceDetect

最后祝大家生活愉快~

android app trifit_Android人脸识别app——基于Face++,MVP+Retofit+RxJava+Dagger高度解耦相关推荐

  1. Android人脸识别app——基于Face++,MVP+Retofit+RxJava+Dagger高度解耦

    热文导读|  点击标题阅读 金九银十跳槽季如何进阶找到合适满意的工作? 给你惊喜!来看看国内大公司官网隐藏的彩蛋 那些一毕业就选择华为的人,后来都怎么样了 作者:reggie1996 (文末附gith ...

  2. 基于Android系统的人脸识别签到软件

    项目名称:   基于Android系统的人脸识别签到软件 目  录 1 项目介绍..... 1 1.1 项目背景.... 1 1.2 产品特点.... 2 1.3 可行性分析.... 2 1.3.1 ...

  3. 人脸识别App面临的安全风险

    中国信通院:2020年人脸识别技术在App应用中的隐私安全研究报告 节选 人脸识别App面临的安全风险 人脸识别技术应用在提升身份认证便捷度和效率的同时,也给个人隐私和数据保护带来了巨大的挑战.通过评 ...

  4. 人脸识别APP技术开发是如何实现的?

    人脸识别技术目前已经非常成熟,识别率也越来越高.人脸识别技术与其他生物特征识别技术相吃比,在实际应用中具有天然独到的优势:通过摄像头直接获取,可以非接触的方式完成识别过程,方便快捷. 目前已应用在金融 ...

  5. 人脸识别APP技术开发

    人脸识别技术目前已经非常成熟,识别率也越来越高.人脸识别技术与其他生物特征识别技术相吃比,在实际应用中具有天然独到的优势:通过摄像头直接获取,可以非接触的方式完成识别过程,方便快捷. 目前已应用在金融 ...

  6. 基于Android端的照片比对系统,基于Android系统的人脸识别系统

    [文章摘要] 当前随着基于Android系统的移动终端设备的广泛应用,以及图像采集设备的普遍集成,使得Android系统的图像采集设备除了具有照相.摄像功能以外,正在扩展新的实用型功能.其中,利用An ...

  7. 如何在AidLearning中快速开发人脸识别APP

    如何在AidLearning中快速开发人脸识别APP 双击Examples进入案例中心,点击FaceID进入人脸识别代码编辑状态. - 提示:案例中的代码不要随意改动,因为AID默认运行前会保存代码文 ...

  8. 实现人脸识别APP(二)

    导读 上一篇文章介绍了人脸识别组成模块,这篇文章主要介绍一下如何来实现一个人脸识别应用,代码我也开源了,在文章最后有开源地址 项目结构 这个项目主要实现了人脸检测和人脸识别两个模块,人脸检测是基于re ...

  9. 人脸识别4:Android InsightFace实现人脸识别Face Recognition(含源码)

    人脸识别4:Android InsightFace实现人脸识别Face Recognition(含源码) 目录 人脸识别4:Android InsightFace实现人脸识别Face Recognit ...

最新文章

  1. 文巾解题 1035. 不相交的线
  2. Java源码分析:深入探讨Iterator模式
  3. BOOST_SCOPE_EXIT_TPL宏相关的测试程序
  4. mysql jdbc allow_mysql的jdbc.url携带allowMultiQueries=true参数的作用及其原理
  5. Makefile中支持的函数大全
  6. 家装强电弱电布线图_家装电路改造注意事项是什么?深圳广田家透彻分析全搞定...
  7. @SuppressWarnings(resource)
  8. Simulink中Simscape—Fluids模块(原Simhydraulics)学习记录
  9. html5中的h1,HTML5语义 - 产品列表中的h1用法
  10. 非平稳时间序列突变检测 -- Bernaola Galvan分割算法
  11. python处理excel数据计算_Python处理Excel数据
  12. 马哥 linux 活动,马哥linux | Linux系统性能和使用活动监控工具 sysstat
  13. Racket 的诞生与发展
  14. 解答03:Smith圆为什么能“上感下容 左串右并”?
  15. 英语学习/词典app——top5
  16. 非诚勿扰php男嘉宾,非诚勿扰 php
  17. Direct3D显示视频
  18. 药店java源码_基于jsp的药店-JavaEE实现药店 - java项目源码
  19. Nvidia官网查询显卡详细参数
  20. 实现 Java 发送短信功能

热门文章

  1. Verdi中文教程(VCS+加法器案例)
  2. java链表实现一元多项式的合并同类项以及加法
  3. 仿新版支付宝账单页面滑动时月份栏被下一个月给顶上去
  4. 问题记录--Win11中文家庭版利用VMware安装虚拟机linux系统Ubuntu
  5. 迅雷7.9.11.4626 优化版
  6. 女朋友说异性相吸,所以产生真爱!我觉得得用python验证一下
  7. 易经与计算机科学,电子计算机与《易经》有啥关系
  8. 把小数点 变成整数的做法
  9. [LeetCode]390.消除游戏
  10. 高德开放平台实现批量自定义marker和信息窗体显示