Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android人脸识别app——基于Face++,MVP+Retofit+RxJava+Dagger高度解耦

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

作者头像
Android技术干货分享
发布于 2019-07-26 07:35:04
发布于 2019-07-26 07:35:04
1.2K00
代码可运行
举报
文章被收录于专栏:Android技术分享Android技术分享
运行总次数:0
代码可运行

前言

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

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

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

下面文章主要介绍的是本项目的开发过程和碰到的坑。

过程

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

拍照获取照片

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 之后都得用这个。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.chaochaowu.facedetect.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
     * 读取图片的旋转的角度
     *
     * @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++ 的官方文档。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * retrofit 面部识别请求的网络服务
 * @author chaochaowu
 */
public interface FaceppService {

    /**
     * @param apikey
     * @param apiSecret
     * @param imageBase64
     * @param returnLandmark
     * @param returnAttributes
     * @return
     */
    @POST("facepp/v3/detect")
    @FormUrlEncoded
    Observable<FaceppBean> 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转码方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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);
    }

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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<FaceppBean>() {
                    @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类中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 面部识别结果的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<FacesBean> faces;
    ...显示部分内容

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

获取信息后的数据处理

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private Bitmap markFacesInThePhoto(Bitmap bitmap, List<FaceppBean.FacesBean> 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 地址

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.07.24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
使用face-api.js实现人脸识别(一)
  Face-api.js 是一个 JavaScript API,是基于 tensorflow.js 核心 API 的人脸检测和人脸识别的浏览器实现。它实现了一系列的卷积神经网络(CNN),针对网络和移动设备进行了优化。非常牛逼,简单好用
阿新
2019/11/01
5.1K0
使用face-api.js实现人脸识别(一)
Android WebView选择图片、发送图片
主要代码来自:http://blog.csdn.net/woshinia/article/details/19030437 有删改
yechaoa
2022/06/10
1.1K0
Android手机拍照后照片被旋转或者需要旋转的问题
1、读取图片的旋转属性 /**  * 读取图片的旋转的角度  *  * @param path  *            图片绝对路径  * @return 图片的旋转角度  */ private int getBitmapDegree(String path) {     int degree = 0;     try {         // 从指定路径下读取图片,并获取其EXIF信息         ExifInterface exifInterface = n
wust小吴
2019/07/08
3.5K0
工具系列 | H5如何实现人脸识别
人脸识别技术在当下已经十分成熟,但主要在移动端和专有设备应用上较为普及,而在Web端并不多见,本着学习的目的从零实现web端的人脸登录功能。
Tinywan
2020/07/23
4K0
工具系列 | H5如何实现人脸识别
.NET做人脸识别并分类
在游乐场、玻璃天桥、滑雪场等娱乐场所,经常能看到有摄影师在拍照片,令这些经营者发愁的一件事就是照片太多了,客户在成千上万张照片中找到自己可不是件容易的事。在一次游玩等活动或家庭聚会也同理,太多了照片导致挑选十分困难。
梁规晓
2019/11/28
7930
Android实现滑块拼图验证码功能
滑块拼图验证码应该算是很常见的功能了,验证码是可以区分用户是人还是机器。可以防止破解密码、刷票等恶意行为。本文将介绍Android拼图滑块验证码控件的实现过程。希望能帮助到大家。
SoullessCoder
2021/02/22
2.9K0
Android旋转相机拍摄的照片
前两天在做一个图片选择器的需求,其中的拍照部分要求调用系统相机拍照后允许用户逆时针旋转图片。
雪之下Perqin
2021/08/26
1.5K0
WebView 的 input 上传照片的兼容问题
问题 前几天接到的一个需求,是关于第三方理财产品的 H5 上传照片问题。 对方说他们的新的需求,需要接入方配合上传资产照片的需求,测试之后发现我们这边的 app 端,IOS 端上传没有问题,而 Android 端则点击没有任何反应。 对方 H5 调用的方式是通过<input type='file' accept='image/*'/>的方式调用,本来以为这个问题很简单,就是 app 端没有设置相机权限,造成的点击无反应情况,而实际上加了之后发现,并非简单的权限问题。 解决问题 因为 Android 的版本
非著名程序员
2018/02/09
2.3K0
关于OpenCV for Python入门-face_recognition实现人脸识别
face_recognition是世界上最简洁的人脸识别库,你可以使用Python和命令行工具提取、识别、操作人脸。
python与大数据分析
2022/05/19
4750
关于OpenCV for Python入门-face_recognition实现人脸识别
初级篇,利用 Android 搭建一个简易的人脸识别APP
初始,准备工作 安装Android sudio 开发工具 搭建android 运行环境(java14) 腾讯云OCR产品开通,及其秘钥获取 一、建立一个Android空项目及目录层次介绍 (1)android 空项目以及环境搭建,请参考:Windows + Android studio 搭建基础的Android 环境(Java SE 14) (2)目录结构介绍 image.png .gredia以及idea目录 :目录下放置的都是as自动生成的文件,本章无需关系 app 目录 : 本章用到的文件目录,仅
HI hero
2020/04/27
4.1K1
初级篇,利用 Android 搭建一个简易的人脸识别APP
android 拍照,图库,相册,上传
在android开发中, 在一些编辑个人信息的时候,经常会有头像这么一个东西,就两个方面,调用系统相机拍照,调用系统图库获取图片.但是往往会遇到各种问题:
再见孙悟空_
2023/02/10
1.9K0
基于MTCNN和MobileFaceNet实现的人脸识别
本教程是教程是介绍如何使用Tensorflow实现的MTCNN和MobileFaceNet实现的人脸识别,并不介绍如何训练模型。关于如何训练MTCNN和MobileFaceNet,请阅读这两篇教程 MTCNN-Tensorflow 和 MobileFaceNet_TF ,这两个模型都是比较轻量的模型,所以就算这两个模型在CPU环境下也有比较好的预测速度,众所周知,笔者比较喜欢轻量级的模型,如何让我从准确率和预测速度上选择,我会更倾向于速度,因本人主要是研究深度学习在移动设备等嵌入式设备上的的部署。好了,下面就来介绍如何实现这两个模型实现三种人脸识别,使用路径进行人脸注册和人脸识别,使用摄像头实现人脸注册和人脸识别,通过HTTP实现人脸注册和人脸识别。
夜雨飘零
2020/07/19
2.8K1
Android多媒体-人脸识别
Google 于2006年8月收购Neven Vision 公司 (该公司拥有 10 多项应用于移动设备领域的图像识别的专利),以此获得了图像识别的技术,并不是常快应用到免费的 Picasa 相冊管理程序中,提供基于人脸识别的相片管理功能,另外还推出了一个新项目叫Goggle ,能从照片中识别世界各地的地标建筑,相同Google 也把人脸识别功能加入�到了Android 中。只是因为个人隐私等相关因素,Google Goggles好像临时屏蔽了人脸识别功能 。
全栈程序员站长
2022/07/11
1K0
Android多媒体-人脸识别
拍照/选择相册,图片压缩旋转处理
最近做项目需要用到拍照和选择相册照片,并显示出来imageview 上,然后压缩上传到服务器中,这本是一个非常常见的功能,但对于图片的处理确实一个技术活,稍微不注意会出现oom,图片压缩也要处理的刚刚好,不能浪费用户的流量,也不能过分的压缩使图片失真,这真的不简单,好在开源中国的安卓端app以开源,本人特意从开源中国整理了这个demo,分享给大家。
方志朋
2022/11/30
9420
相关推荐
使用face-api.js实现人脸识别(一)
更多 >
领券
一站式MCP教程库,解锁AI应用新玩法
涵盖代码开发、场景应用、自动测试全流程,助你从零构建专属AI助手
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验