Android移动开发-在Android项目里集成face++人脸识别的实现

“人工智能”是今年比较火的一个名词,甚至大多数人认为“人工智能”是继“IT互联网”以后一个新的浪潮,将会是本世纪最高科技的水准。我的以为,目前国内语音识别作的比较好的是科大讯飞,图像识别(或人脸识别)作的比较好的是face++(旷视)。java

那么,如何在Android项目里集成face++人脸识别的实现呢? 
首先在face++的开发平台网址:https://www.faceplusplus.com.cn/ ,而后在平台注册成为开发者后在点击“应用管理”——建立“API Key”号,以下图所示:android

这里写图片描述

接着再绑定Bundle ID号,以下图所示:git

这里写图片描述

  • layout/activity_main.xml界面布局代码以下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <ImageView  android:id="@+id/id_photo" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:layout_alignTop="@+id/id_detect" android:src="@drawable/face" />

    <TextView  android:id="@+id/id_tip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignTop="@+id/id_detect" android:layout_marginRight="10dp" android:layout_toLeftOf="@+id/id_detect" android:gravity="center" android:text="tip" />

    <Button  android:id="@+id/id_detect" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginRight="10dip" android:layout_toLeftOf="@+id/id_getImg" android:text="检测人脸" />

    <Button  android:id="@+id/id_getImg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:text="选择图片" />

    <FrameLayout  android:id="@+id/id_waiting" android:layout_width="match_parent" android:layout_height="match_parent" android:clickable="true" android:visibility="gone">

        <ProgressBar  android:id="@+id/id_progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" />

        <TextView  android:id="@+id/id_age_and_gender" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/hint" android:drawableLeft="@drawable/male" android:gravity="center" android:text="123" android:textColor="#ffff00ff" android:textSize="22sp" android:visibility="invisible" />
    </FrameLayout>
</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • MainActivity.java逻辑代码以下:
package com.fukaimei.facerecognition;

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

import com.facepp.error.FaceppParseException;
import com.fukaimei.facerecognition.utils.FaceppDetect;

import android.content.Intent;
import android.content.pm.PackageManager;
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.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements OnClickListener {

    private ImageView mPhoto;
    private Button mDetect, mGetImg;
    private TextView mTip;
    private View mWaiting;

    private Canvas canvas;
    private Paint mPaint;
    private String mCurrentPhotoStr;
    private Bitmap mBitmapPhoto;
    private static final int PICK_CODE = 0x110;
    private static final int MSG_SUCCESS = 0x111;
    private static final int MSG_ERROR = 0x112;
    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case MSG_SUCCESS:
                    mWaiting.setVisibility(View.GONE);
                    JSONObject rs = (JSONObject) msg.obj;
                    prepareResultBitmap(rs);

                    mPhoto.setImageBitmap(mBitmapPhoto);

                    break;
                case MSG_ERROR:
                    mWaiting.setVisibility(View.GONE);
                    String errorMsg = (String) msg.obj;
                    if (TextUtils.isEmpty(errorMsg)) {
                        mTip.setText("Error.");
                    } else {
                        mTip.setText(errorMsg);
                    }
                    break;
            }
        }

    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 申请访问SD卡的动态权限
        sdPermissions();
        mPhoto = (ImageView) findViewById(R.id.id_photo);
        mDetect = (Button) findViewById(R.id.id_detect);
        mGetImg = (Button) findViewById(R.id.id_getImg);
        mTip = (TextView) findViewById(R.id.id_tip);
        mWaiting = findViewById(R.id.id_waiting);
        mDetect.setOnClickListener(this);
        mGetImg.setOnClickListener(this);
        mPaint = new Paint();
    }

    // 定义访问SD卡的动态权限
    private void sdPermissions() {
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{
                    android.Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
        }
    }

    /** * 重写onRequestPermissionsResult方法 * 获取动态权限请求的结果,再访问SD卡 */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

        } else {
            Toast.makeText(this, "用户拒绝了权限", Toast.LENGTH_SHORT).show();
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    private void prepareResultBitmap(JSONObject rs) {
        // TODO Auto-generated method stub
        Bitmap bitmap = Bitmap.createBitmap(mBitmapPhoto.getWidth(), mBitmapPhoto.getHeight(),
                mBitmapPhoto.getConfig());
        canvas = new Canvas(bitmap);
        canvas.drawBitmap(mBitmapPhoto, 0, 0, null);

        try {
            JSONArray faces = rs.getJSONArray("face");
            int faceCount = faces.length();
            mTip.setText("find " + faceCount);

            for (int i = 0; i < faceCount; i++) {
                // 拿到单独face对象
                JSONObject face = faces.getJSONObject(i);
                JSONObject positonObj = face.getJSONObject("position");
                // get face position
                float x = (float) positonObj.getJSONObject("center").getDouble("x");
                float y = (float) positonObj.getJSONObject("center").getDouble("y");
                float w = (float) positonObj.getDouble("width");
                float h = (float) positonObj.getDouble("height");

                x = x / 100 * bitmap.getWidth();
                y = y / 100 * bitmap.getHeight();
                w = w / 100 * bitmap.getWidth();
                h = h / 100 * bitmap.getHeight();

                mPaint.setColor(0xffffffff);
                mPaint.setStrokeWidth(3);
                // draw box
                canvas.drawLine(x - w / 2, y - h / 2, x - w / 2, y + h / 2, mPaint);// 左
                canvas.drawLine(x - w / 2, y + h / 2, x + w / 2, y + h / 2, mPaint);// 下
                canvas.drawLine(x + w / 2, y + h / 2, x + w / 2, y - h / 2, mPaint);// 右
                canvas.drawLine(x - w / 2, y - h / 2, x + w / 2, y - h / 2, mPaint);// 上

                // get age and gender
                int age = face.getJSONObject("attribute").getJSONObject("age").getInt("value");
                String gender = face.getJSONObject("attribute").getJSONObject("gender").getString("value");

                Bitmap ageBitmap = buildAgeBitmap(age, gender.equals("Male"));

                int ageWidth = ageBitmap.getWidth();
                int ageHeight = ageBitmap.getHeight();
                if(bitmap.getWidth()<=mPhoto.getWidth() && bitmap.getHeight()<=mPhoto.getHeight()){
                    float ratio = Math.max(bitmap.getWidth() * 1.0f / mPhoto.getWidth(),
                            bitmap.getHeight() * 1.0f / mPhoto.getHeight());

                    ageBitmap = Bitmap.createScaledBitmap(ageBitmap, (int) (ageWidth * ratio),
                            (int) (ageHeight * ratio), false);
                    canvas.drawBitmap(ageBitmap, x - ageBitmap.getWidth() / 2, y - h / 2 - ageBitmap.getHeight(), null);
                }
                mBitmapPhoto = bitmap;
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    };

    private Bitmap buildAgeBitmap(int age, boolean isMale) {
        TextView tv = (TextView) mWaiting.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);
        }
        tv.setDrawingCacheEnabled(true);
        Bitmap bitmap = Bitmap.createBitmap(tv.getDrawingCache());
        tv.destroyDrawingCache();
        return bitmap;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == PICK_CODE) {
            if (data != null) {
                Uri uri = data.getData();
                Cursor cursor = getContentResolver().query(uri, null, null, null, null);
                cursor.moveToFirst();

                int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                mCurrentPhotoStr = cursor.getString(index);
                cursor.close();

                resizePhoto();
                mPhoto.setImageBitmap(mBitmapPhoto);
                mTip.setText("点击检测 ==>");
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    private void resizePhoto() {

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeFile(mCurrentPhotoStr, options);

        double ratio = Math.max(options.outWidth * 1.0d / 1024f, options.outHeight * 1.0d / 1024f);
        options.inSampleSize = (int) Math.ceil(ratio);
        options.inJustDecodeBounds = false;

        mBitmapPhoto = BitmapFactory.decodeFile(mCurrentPhotoStr, options);
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.id_getImg:
                Intent intent = new Intent(Intent.ACTION_PICK);
                intent.setType("image/*");
                startActivityForResult(intent, PICK_CODE);
                break;

            case R.id.id_detect:
                if (mBitmapPhoto != null && !mBitmapPhoto.isRecycled()) {
                    mWaiting.setVisibility(View.VISIBLE);
                    FaceppDetect.detect(mBitmapPhoto, new FaceppDetect.CallBack() {
                        @Override
                        public void success(JSONObject result) {
                            Message msg = mHandler.obtainMessage(MSG_SUCCESS);
                            msg.obj = result;
                            msg.sendToTarget();
                        }

                        @Override
                        public void error(FaceppParseException exception) {

                            Message msg = mHandler.obtainMessage(MSG_ERROR);
                            msg.obj = exception.getErrorMessage();
                            msg.sendToTarget();
                        }
                    });
                } else {
                    Toast.makeText(MainActivity.this, "请选择您要检测的人脸图片", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 而后在 Constant.java逻辑代码下添加在face++平台申请到的APP_KEY码和APP_SECRET码以下:
package com.fukaimei.facerecognition;

public class Constant {

    public static final String APP_KEY = "填写你申请到的APP_KEY码";
    public static final String APP_SECRET = "填写你申请到的APP_SECRET码";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • Demo程序运行效果界面截图以下:

这里写图片描述


Demo程序源码下载地址一(GitHub) 
Demo程序源码下载地址二(Gitee)github