Android-based face recognition app-- Face ++, MVP + Retofit + RxJava + Dagger

Foreword

Recently the company project is relatively empty, it took time to write a face recognition app, you can view your gender, age, color values, emotions and other information, the use of face recognition is Face ++ API. This project uses the MVP architecture, using Retrofit, RxJava, Dagger, EventBus and other frameworks for development and decoupling, use MaterialDesign layout design on the UI.

The main function is to take pictures, then the picture to the Face ++ server, face recognition, access to information is returned, the information processing. The face in the photo marked, and the information displayed.

Man of few words said, first look at the app's effect (Daniel Wu is still handsome ah, ha ha).
Here Insert Picture DescriptionAndroid-based face recognition app-- Face ++, MVP + Retofit + RxJava + DaggerHere Insert Picture Description
The following article describes is the project development process and encountered pit.

process

The entire process of project is nothing more than a simple three-step, take pictures, transfer photos to get data and then process the data show.

Get photos pictures

Camera needs to obtain system privileges, I wrapped a method to determine whether there are photographs App permissions related, if not go to dynamically request permission, and returns false, if it returns 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;
        }
    }

Get permission to take pictures after you can take pictures, but the camera to get photos we need to get through FileProvider. FileProvider related content will not be introduced after Android 7.0 had to use this.

<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>

After taking pictures read from the file, we can get a BitMap object. Here there is a big pit, if the phone is Samsung's words, pictures from the document read out, the resulting picture will be rotated 90 °! ! ! This thief pit ah, tune me for a long time, thought it was his fault the phone, and later checked on the net, but also a moment to ask the older generation, the original Samsung mobile phones have this problem, so that we want to take out of the picture file conduct some processing.

/**
     * 读取图片的旋转的角度
     *
     * @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<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转码方法。

 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<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类中。

/**
 * 面部识别结果的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 进行处理,利用数据中人脸在照片中的坐标位置,我们用方框将人脸标识出来。

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循环遍历。获取人脸在照片中的坐标,利用人脸左上角的坐标以及人脸的宽高,在照片中绘制一个方框将人脸标出。

Here Insert Picture Description
剩余信息我这边采用 RecyclerView 来展示。左右滑动可以查看每张人脸的信息。RecyclerView 的 item 上展示的是简要信息,可以点击 item 进入详情页面查看面部识别的详细信息。RecyclerView 以及详情界面的实现就不作介绍了,很基本的操作。我这边也就只使用了 SharedElement 让界面切换看起来舒服一点。Android-based face recognition app-- Face ++, MVP + Retofit + RxJava + Dagger

Nothing other operations, you can also look at my architecture project. Because the frame with the maintenance of the various decoupling, the number of files increases the code, but the code in a single file becomes less, legible point, which is the purpose of decoupling, but also facilitate later.

Here Insert Picture Description

At last

After writing this APP, I have been thinking about a problem, Yan APP value to Daniel scoring more than 80, 100 Yen value that would be like?

Well, the article written here is over, if you think the article is well written give a praise chant? If you think there need to be improved, please give me a message. Will be serious inquiry, insufficiently corrected. Thank you.

I hope you can read this and look forward to share my future will update the technology of dry cargo, thank you for your support!

+ + Forwarding thumbs attention, the first time to obtain the latest knowledge points

Android architects of the road is long, encourage each other with it!

Guess you like

Origin blog.51cto.com/14332859/2423642