Android 使用FACE++架构包实现人脸识别

今天给大家带来一个通过使用Face++来实现人脸识别的功能。

我们先去这个Face++官网看看:http://www.faceplusplus.com.cn

我们点开案例可以看到众多我们熟知的软件都是使用的这个公司所提供的SDK。

然后我们点击开发者中心中的开发工具与sdk下载我们所需要的sdk。

之后再点击我的应用中的创建应用之后他会给我们两个密钥。

要保存这两个值我们在程序中要用到它们

我今天实现的是实现面部捕捉并且识别性别和年龄来看一下效果图

  

闲话不多说我们来看看实现

1.工具类Constant用来存放密钥

public class Constant {
     //设置两个之前获取的两个常量
     public static final String Key="2029451928755e97039b8138cfa8f8ca";
     public static final String Secret="5RNoYATB9vqpA5kerpmo6bp2Aw9fxMl0";
}  

2.工具类InternetDetect

import android.graphics.Bitmap;

import com.facepp.error.FaceppParseException;
import com.facepp.http.HttpRequests;
import com.facepp.http.PostParameters;

import org.json.JSONObject;

import java.io.ByteArrayOutputStream;

public class InternetDetect {
    public interface CallBack
    {
        void success(JSONObject jsonObject);

        void error(FaceppParseException exception);

    }
    public static void dectect(final Bitmap bitmap, final CallBack callBack)
    {
        //因为这里要向网络发送数据是耗时操作所以要在新线程中执行
        new Thread(new Runnable() {
            @Override
            public void run() {
                /*1.设置请求
                * 2.创建一个Bitmap
                * 3.创建字符数组流
                * 4.将bitmap转换为字符并传入流中
                * 5.新建字符数组接受流
                * 6.创建发送数据包
                * 7.创建接受数据包
                *
                * */
                try {

                    HttpRequests httpRequests=new HttpRequests(Constant.Key,Constant.Secret,true,true);
                    //从0,0点挖取整个视图,后两个参数是目标大小
                    Bitmap bitmapsmall = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight());
                    //这里api要求传入一个字节数组数据,因此要用字节数组输出流
                    ByteArrayOutputStream stream=new ByteArrayOutputStream();
                    /*Bitmap.compress()方法可以用于将Bitmap-->byte[]
                      既将位图的压缩到指定的OutputStream。如果返回true,
                      位图可以通过传递一个相应的InputStream BitmapFactory.decodeStream(重建)
                      第一个参数可设置JPEG或PNG格式,第二个参数是图片质量,第三个参数是一个流信息*/
                    bitmapsmall.compress(Bitmap.CompressFormat.JPEG, 100, stream);
                    byte[] arrays=stream.toByteArray();
                    //实现发送参数功能
                    PostParameters parameters=new PostParameters();
                    //发送数据
                    parameters.setImg(arrays);
                    //服务器返回一个JSONObject的数据
                    JSONObject jsonObject=httpRequests.detectionDetect(parameters);
                    System.out.println("jsonObject:"+jsonObject.toString());
                    if(callBack!=null)
                    {
                        //设置回调
                        callBack.success(jsonObject);
                    }
                } catch (FaceppParseException e) {

                    System.out.println("error");
                    e.printStackTrace();
                    if(callBack!=null)
                    {
                        callBack.error(e);
                    }
                }

            }
        }).start();
    }

}

3.MainActivity
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.facepp.error.FaceppParseException;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends ActionBarActivity implements View.OnClickListener {
    private static final int PICK_CODE =1;
    private ImageView myPhoto;
    private Button getImage;
    private Button detect;
    private TextView tip;
    private View mWaitting;
    private String ImagePath=null;
    private Paint mypaint;
    private Bitmap myBitmapImage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mypaint=new Paint();
        initViews();
        initEvent();
    }
    private void initViews()
    {
        myPhoto=(ImageView)findViewById(R.id.id_photo);
        getImage=(Button)findViewById(R.id.get_image);
        detect=(Button)findViewById(R.id.detect);
        tip=(TextView)findViewById(R.id.id_Tip);
        mWaitting=findViewById(R.id.id_waitting);
        tip.setMovementMethod(ScrollingMovementMethod.getInstance());

    }
    private void initEvent()
    {
        getImage.setOnClickListener(this);
        detect.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {

        switch (v.getId())
        {
            case R.id.get_image:
                //获取系统选择图片intent
                Intent intent=new Intent(Intent.ACTION_PICK);
                intent.setType("image/*");
                //开启选择图片功能响应码为PICK_CODE
                startActivityForResult(intent,PICK_CODE);
                break;
            case R.id.detect:
                //显示进度条圆形
                mWaitting.setVisibility(View.VISIBLE);
                //这里需要注意判断用户是否没有选择图片直接点击了detect按钮
                //否则会报一个空指针异常而造成程序崩溃
                if(ImagePath!=null&&!ImagePath.trim().equals(""))
                {
                    //如果不是直接点击的图片则压缩当前选中的图片
                    resizePhoto();
                }else
                {
                    //否则将默认的背景图作为bitmap传入
                    myBitmapImage=BitmapFactory.decodeResource(getResources(),R.drawable.test1);
                }
                //设置回调
                InternetDetect.dectect(myBitmapImage, new InternetDetect.CallBack() {
                    @Override
                    public void success(JSONObject jsonObject) {
                        Message message=Message.obtain();
                        message.what=MSG_SUCESS;
                        message.obj=jsonObject;
                        myhandler.sendMessage(message);
                    }
                    @Override
                    public void error(FaceppParseException exception) {
                        Message message=Message.obtain();
                        message.what=MSG_ERROR;
                        message.obj=exception;
                        myhandler.sendMessage(message);
                    }
                });
                break;
        }

    }
    //设置响应intent请求
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if(requestCode==PICK_CODE)
        {
            if(intent!=null)
            {
                //获取图片路径
                //获取所有图片资源
                Uri uri=intent.getData();
                //设置指针获得一个ContentResolver的实例
                Cursor cursor=getContentResolver().query(uri,null,null,null,null);
                cursor.moveToFirst();
                //返回索引项位置
                int index=cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                //返回索引项路径
                ImagePath=cursor.getString(index);
                cursor.close();
                //这个jar包要求请求的图片大小不得超过3m所以要进行一个压缩图片操作
                resizePhoto();
                myPhoto.setImageBitmap(myBitmapImage);
                tip.setText("Click Detect==>");
            }
        }
    }

    private void resizePhoto() {
        //得到BitmapFactory的操作权
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 如果设置为 true ,不获取图片,不分配内存,但会返回图片的高宽度信息。
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(ImagePath,options);
        //计算宽高要尽可能小于1024
        double ratio=Math.max(options.outWidth*1.0d/1024f,options.outHeight*1.0d/1024f);
        //设置图片缩放的倍数。假如设为 4 ,则宽和高都为原来的 1/4 ,则图是原来的 1/16 。
        options.inSampleSize=(int)Math.ceil(ratio);
        //我们这里并想让他显示图片所以这里要置为false
        options.inJustDecodeBounds=false;
        //利用Options的这些值就可以高效的得到一幅缩略图。
        myBitmapImage=BitmapFactory.decodeFile(ImagePath,options);
    }

    private static final int MSG_SUCESS=11;
    private static final int MSG_ERROR=22;
    private Handler myhandler=new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what)
            {
                case MSG_SUCESS:
                    //关闭缓冲条
                    mWaitting.setVisibility(View.GONE);
                    //拿到新线程中返回的JSONObject数据
                    JSONObject rsobj= (JSONObject) msg.obj;
                    //准备Bitmap,这里会解析JSONObject传回的数据
                    prepareBitmap(rsobj);
                    //让主线程的相框刷新
                    myPhoto.setImageBitmap(myBitmapImage);
                    break;
                case MSG_ERROR:
                    mWaitting.setVisibility(View.GONE);
                    String errormsg= (String) msg.obj;
                    break;
            }
        }

    };

    private void prepareBitmap(JSONObject JS) {
        //新建一个Bitmap使用它作为Canvas操作的对象
        Bitmap bitmap=Bitmap.createBitmap(myBitmapImage.getWidth(),myBitmapImage.getHeight(),myBitmapImage.getConfig());
        //实例化一块画布
        Canvas canvas=new Canvas(bitmap);
        //把原图先画到画布上面
        canvas.drawBitmap(myBitmapImage, 0, 0, null);
        //解析传回的JSONObject数据
        try {
                //JSONObject中包含着众多JSONArray,但是我们这里需要关键字为face的数组中的信息
                JSONArray faces=JS.getJSONArray("face");
                //获取得到几个人脸
                int faceCount=faces.length();
                //让提示文本显示人脸数
                tip.setText("find"+faceCount);
               //下面对每一张人脸都进行单独的信息绘制
                for(int i=0;i<faceCount;i++)
                {
                    //拿到每张人脸的信息
                    JSONObject face=faces.getJSONObject(i);
                    //拿到人脸的详细位置信息
                    JSONObject position=face.getJSONObject("position");
                    float x=(float)position.getJSONObject("center").getDouble("x");
                    float y=(float)position.getJSONObject("center").getDouble("y");
                    float w=(float)position.getDouble("width");
                    float h=(float)position.getDouble("height");
                    //注意这里拿到的各个参数并不是实际的像素值,而是一个比例,都是相对于整个屏幕而言的比例信息
                    //因此我们使用的时候要进行一下数据处理
                    x=x/100*bitmap.getWidth();
                    y=y/100*bitmap.getHeight();
                    w=w/100*bitmap.getWidth();
                    h=h/100*bitmap.getHeight();
                    //设置画笔颜色
                    mypaint.setColor(0xffffffff);
                    //设置画笔宽度
                    mypaint.setStrokeWidth(3);
                    //绘制一个矩形框
                    canvas.drawLine(x-w/2,y-h/2,x-w/2,y+h/2,mypaint);
                    canvas.drawLine(x-w/2,y-h/2,x+w/2,y-h/2,mypaint);
                    canvas.drawLine(x+w/2,y-h/2,x+w/2,y+h/2,mypaint);
                    canvas.drawLine(x-w/2,y+h/2,x+w/2,y+h/2,mypaint);
                    //得到年龄信息
                    int age=face.getJSONObject("attribute").getJSONObject("age").getInt("value");
                    //得到性别信息
                    String gender=face.getJSONObject("attribute").getJSONObject("gender").getString("value");
                    System.out.println("age:"+age);
                    System.out.println("gender:"+gender);
                    //现在要把得到的文字信息转化为一个图像信息,我们写一个专门的函数来处理
                    Bitmap ageBitmap=buildAgeBitmap(age,("Male").equals(gender));
                    //进行图片提示气泡的缩放,这个很有必要,当人脸很小的时候我们需要把提示气泡也变小
                    int agewidth=ageBitmap.getWidth();
                    int agehight=ageBitmap.getHeight();
                    if(bitmap.getWidth()<myPhoto.getWidth()&&bitmap.getHeight()<myPhoto.getHeight())
                    {
                        //设置缩放比
                        float ratio=Math.max(bitmap.getWidth()*1.0f/
                                myPhoto.getWidth(),bitmap.getHeight()*1.0f/myPhoto.getHeight());

                        //完成缩放
                        ageBitmap=Bitmap.createScaledBitmap(ageBitmap,(int)(agewidth*ratio*0.8),(int)(agehight*ratio*0.5),false);
                    }
                    //在画布上画出提示气泡
                    canvas.drawBitmap(ageBitmap,x-ageBitmap.getWidth()/2,y-h/2-ageBitmap.getHeight(),null);
                    //得到新的bitmap
                    myBitmapImage=bitmap;
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }

    private Bitmap buildAgeBitmap(int age, boolean isMale) {
        //这里要将文字信息转化为图像信息,如果拿Canvas直接画的话操作量太大
        //因此这里有一些技巧,将提示气泡设置成一个TextView,他的背景就是气泡的背景
        //他的内容左侧是显示性别的图片右侧是年龄
        TextView tv= (TextView) mWaitting.findViewById(R.id.id_age_and_gender);
        //这里要记得显示数字的时候后面最好跟一个""不然有时候会显示不出来
        tv.setText(age + "");
        if(isMale)
        {
            //判断性别
            tv.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.male),null,null,null);

        }else
        {
            tv.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.female),null,null,null);
        }

        //使用setDrawingCacheEnabled(boolean flag)提高绘图速度
        //View组件显示的内容可以通过cache机制保存为bitmap
        //这里要获取它的cache先要通过setDrawingCacheEnable方法把cache开启,
        // 然后再调用getDrawingCache方法就可 以获得view的cache图片了。
        tv.setDrawingCacheEnabled(true);
        Bitmap bitmap=Bitmap.createBitmap(tv.getDrawingCache());
        //关闭许可
        tv.destroyDrawingCache();
        return bitmap;
    }

}

布局文件比较简单因此代码我就不上传了

这里我用红色字体标出我在实现的过程中遇到的问题希望大家可以避免:

1.在写按键响应的时候要注意判断用户是否没有选择图片而直接点击了detect按钮,这样的话避免了获取图片时存在空指针异常的问题

2.上传图片时要保证图片小于3M因此注意图片的压缩

3.气泡处理有技巧,把它当作Textview更方便

4.在绘制气泡的时候也要注意缩放不然的话当人脸很小的时候气泡会占据整个图片导致用户体验降低

希望对大家有所帮助,欢迎转载但要标明出处,谢谢!

有什么不足的地方可以留言给我我会尽快回复并改正!

欢迎关注我的博客:http://www.bubblyyi.com

点我下载人脸识别源码

时间: 2024-10-14 16:10:10

Android 使用FACE++架构包实现人脸识别的相关文章

Android使用Face++架构包实现人脸识别

欢迎转载,转载时请标明出处:http://blog.csdn.net/android_for_james/article/details/51016170 今天给大家带来一个通过使用Face++来实现人脸识别的功能 我们先去这个Face++官网看看:http://www.faceplusplus.com.cn 我们点开案例可以看到众多我们熟知的软件都是使用的这个公司所提供的SDK 然后我们点击开发者中心中的开发工具与sdk下载我们所需要的sdk 之后再点击我的应用中的创建应用之后他会给我们两个密

android人脸识别——HowOld测测你的年龄和性别

引言 这段时间微软的HowOldRobot 测试年龄的网站非常火,访问量已经爆棚了!不过,这个测试也有很多比较坑爹的地方.比如:..... 再比如... 好了 言归正传!今天我们就来看看android中怎么利用人脸识别功能来实现我们自己的HowOld APP (PS:本人也是借鉴了网上大神的视频和资料 然后自己加以改进) Face++ API 想要使用人脸识别功能,我们需要调用Face++中的一些API来完成工作.Face++的官网地址是:http://www.faceplusplus.com.

Android中 人脸识别FaceDetector简单实例

Android SDK从1.0版本中(API level 1)就已经集成了简单的人脸识别功能,通过调用FaceDetector 我们可以在Android平台上实现Bitmap多人脸识别(一张图中可以有多个人脸).它查找人脸的原理是:找眼睛.它返回的人脸数据face,通过调用public float eyesDistance (),public void getMidPoint (PointF point),我们可以得到探测到的两眼间距,以及两眼中心点位置(MidPoint).public flo

Android 实现人脸识别教程[运用虹软人脸识别SDK]

基于虹软人脸识别引擎,在Android平台上实现人脸识别功能,即使在离线的情况下依旧运行,不被人采集个人照片的感觉,还是爽爽的.经过整个测试过来,虹软的人脸识别还是很强大的,人脸检测可以控制在20ms之内,人脸识别大概在200ms左右.今天就来分享一下开发经验 项目的目标 我们需要实现一个人脸识别功能.简单来说,就是机的后置摄像头,识别摄像头中实时拍到的人脸信息,如果人库注册过,则显示识别后的人脸信息,如登记的名字:如果不在,提示未注册. 这个功能具有多个应用场景,比如,火车站或者打卡和门禁系统

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

前言 最近公司项目比较空,花了点时间写了个人脸识别的app,可以查看你的性别.年龄.颜值.情绪等信息,利用的是 Face++ 的人脸识别API.本项目采用了 MVP 的架构,使用了 Retrofit.RxJava.Dagger.EventBus 等框架进行开发和解耦,利用 MaterialDesign 进行UI上的布局设计. 主要的功能就是拍照,然后将照片传至 Face++ 服务器,进行人脸识别,获取返回的信息,对信息进行处理.将人脸在照片上标出,并将信息展示出来. 话不多说,先来看一下 app

结合活体检测,人脸识别在Android、IOS、服务器中的应用

随着深度学习方法的应用,人工智能的发展,人脸识别技术的识别率已经得到质的提升,通过反复开发试验,目前我司的人脸识别技术率已经达到99%.人脸识别技术与其他生物特征识别技术相吃比,在实际应用中具有天然独到的优势:通过摄像头直接获取,在非接触的方式完成识别过程.通过人脸识别与证件识别的比对,目前我司的人脸识别技术已应用在金融.教育.景区.旅运.社保等领域. 人脸识别技术简介 人脸识别技术主要分为两部分: 第一部为前端人脸活体检测技术,主要支持android.ios平台,在前端通过眨眼.张嘴.摇头.点

Android静态图片人脸识别的完整demo(附完整源码)

Demo功能:利用android自带的人脸识别进行识别,标记出眼睛和人脸位置.点击按键后进行人脸识别,完毕后显示到imageview上. 第一部分:布局文件activity_main.xml [html] view plaincopyprint? <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.co

Android多媒体-人脸识别

1. 相关背景 Google 于2006年8月收购Neven Vision 公司 (该公司拥有 10 多项应用于移动设备领域的图像识别的专利),以此获得了图像识别的技术,并不是常快应用到免费的 Picasa 相冊管理程序中,提供基于人脸识别的相片管理功能,另外还推出了一个新项目叫Goggle ,能从照片中识别世界各地的地标建筑,相同Google 也把人脸识别功能加入?到了Android 中.只是因为个人隐私等相关因素,Google Goggles好像临时屏蔽了人脸识别功能 . 2. Androi

android 使用讯飞人脸识别api报错:java.lang.UnsatisfiedLinkError

1.在做一个人脸识别的项目,使用的是讯飞的api,编辑器为AS2.0,运行时报如下错误: FATAL EXCEPTION: main                  Process: com.admin.smartcam, PID: 13821                  java.lang.UnsatisfiedLinkError: Native method not found: com.faceplusplus.api.Native.setMinFaceSize:(II)I