NDK开发前奏 - 实现支付宝人脸识别功能

1. 基于 Android Studio 的 opencv 配置与使用

先推荐一本书**《计算机视觉 - 算法与应用》**,相信用过 OpenCV 的哥们都知道这是用来干啥的,这里我就再也不啰嗦。只说一下他的应用领域:人机互动、物体识别、图像分割、人脸识别、动做识别、运动跟踪、机器人、运动分析、机器视觉、结构分析、汽车安全驾驶等等。此次咱们主要用它来作人脸识别,注意人脸检测和人脸识别是两个概念。html

首先先去官网 https://opencv.org/opencv-3-2.html 下载 Android SDK: sourceforge ,下载下来之后咱们的开发方式目前有两种:一种是基于 OpenCV_3.2.0_Manager.apk 的纯 Java 代码;还有一种方式是配置好 opencv 后利用 Android NDK,使用C++开发android

无论怎样都须要配置依赖 openCV 的开发环境,开发环境都起不来那就白扯了,目前咱们采用的是:Android Studio 3.0.1(最高版本,建议 2.3 及以上) + OpenCV for Android SDK 3.2版本(点我上面的连接就可下载) 。支付宝就有人脸识别功能,相信咱们都用过,在看我搭环境的同时你们不妨思考一下,人脸识别匹配到底匹配的是啥信息? 算法

新建 Android Studio 项目工程,导入这个 module 但注意这是一个 Eclipse 工程,须要本身额外添加 build.gradle 文件。而后找到 native\libs 目录如图所示:
把 armeabi 拷贝到 jniLibs 下面,而后 app 添加依赖第一步就算大功告成。接下来就能够写一个简单的事例代码了。

2. 基于 opencv 的简单测试事例

刚开始咱们就能够作一些简单的项目了,先熟悉 API 再去熟悉原理最后去熟悉算法。好比边缘检测,边缘检测又是啥?若是你要作图形图像识别就要用到他,再说通俗一些好比你要作车牌号识别就要用到他。这里咱们写一下 opencv 处理图片灰度和边缘检测的代码:安全

#include <jni.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <android/bitmap.h>
#include <android/log.h>

#define TAG "JNI_TAG"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)


using namespace cv;

extern "C" {
JNIEXPORT void
JNICALL
Java_com_example_administrator_opencv_OpenCV_cannyCheck(JNIEnv *env, jclass type, jobject src,
                                                        jobject dst);
// bitmap -> mat
Mat bitmap2Mat(jobject pJobject);
// mat -> bitmap
void mat2bitmap(JNIEnv *env, Mat mat, jobject bitmap);
}

Mat bitmap2Mat(JNIEnv *env, jobject bitmap) {
    // 1. 获取图片的宽高,以及格式信息
    AndroidBitmapInfo info;
    AndroidBitmap_getInfo(env, bitmap, &info);
    void *pixels;
    AndroidBitmap_lockPixels(env, bitmap, &pixels);

    Mat mat;

    if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
        LOGD("nMatToBitmap: CV_8UC4 -> RGBA_8888");
        mat = Mat(info.height, info.width, CV_8UC4, pixels);
    } else if (info.format = ANDROID_BITMAP_FORMAT_RGB_565) {
        LOGD("nMatToBitmap: CV_8UC2 -> RGBA_565");
        mat = Mat(info.height, info.width, CV_8UC2, pixels);
    }

    AndroidBitmap_unlockPixels(env, bitmap);
    return mat;
}

void mat2bitmap(JNIEnv *env, Mat src, jobject bitmap) {
    // 1. 获取图片的宽高,以及格式信息
    AndroidBitmapInfo info;
    AndroidBitmap_getInfo(env, bitmap, &info);
    void *pixels;
    AndroidBitmap_lockPixels(env, bitmap, &pixels);

    if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
        Mat tmp(info.height, info.width, CV_8UC4, pixels);
        if (src.type() == CV_8UC1) {
            LOGD("nMatToBitmap: CV_8UC1 -> RGBA_8888");
            cvtColor(src, tmp, COLOR_GRAY2RGBA);
        } else if (src.type() == CV_8UC3) {
            LOGD("nMatToBitmap: CV_8UC3 -> RGBA_8888");
            cvtColor(src, tmp, COLOR_RGB2RGBA);
        } else if (src.type() == CV_8UC4) {
            LOGD("nMatToBitmap: CV_8UC4 -> RGBA_8888");
            src.copyTo(tmp);
        }
    } else {
        // info.format == ANDROID_BITMAP_FORMAT_RGB_565
        Mat tmp(info.height, info.width, CV_8UC2, pixels);
        if (src.type() == CV_8UC1) {
            LOGD("nMatToBitmap: CV_8UC1 -> RGB_565");
            cvtColor(src, tmp, COLOR_GRAY2BGR565);
        } else if (src.type() == CV_8UC3) {
            LOGD("nMatToBitmap: CV_8UC3 -> RGB_565");
            cvtColor(src, tmp, COLOR_RGB2BGR565);
        } else if (src.type() == CV_8UC4) {
            LOGD("nMatToBitmap: CV_8UC4 -> RGB_565");
            cvtColor(src, tmp, COLOR_RGBA2BGR565);
        }
    }

    AndroidBitmap_unlockPixels(env, bitmap);
}


JNIEXPORT void JNICALL
Java_com_example_administrator_opencv_OpenCV_cannyCheck(JNIEnv *env, jclass type, jobject src,
                                                        jobject dst) {
    // 1. bitmap2Mat
    Mat src_mat = bitmap2Mat(env, src);
    Mat gray_mat, dst_mat;
    // 2. 讲图片处理成 Gray 能够提高处理速度
    cvtColor(src_mat, gray_mat, COLOR_BGRA2GRAY);
    // 2.2 3X3降噪处理
    blur(gray_mat, gray_mat, Size(3, 3));
    // 3. 处理边缘检测
    Canny(gray_mat, dst_mat, 50, 30);
    // 4. mat2bitmap
    mat2bitmap(env, dst_mat, dst);
}
复制代码

3. 人脸检测和人脸识别

首先人脸检测和人脸识别是两个概念,人脸检测是检测是否有人脸,而人脸识别是比对人脸特征值的。支付宝支付有人脸识别功能,百度也有我的脸识别的开源工具,实际上是基于服务器的,比较的是两张图片。那么拿 opencv 来讲其实比较的也是图片,在 opencv 中有个很是重要的数据那就是 Mat 。人脸识别比较的真的是图片吗?目前我所知道的是这样,但像 tango 这些自己具备深度学习和机器学习的设备,或许之后就会变得不同,可是这些或多或少都跟硬件有关系。既然知道比较的是什么,那么来咱们就能够来走下逻辑了。bash

  1. 人脸特征的录入  1.1 打开相机检测是否有人脸  1.2 保存人脸特征信息(可保存多份)服务器

  2. 人脸特征匹配识别  2.2 打开相机检测是否有人脸  2.2 根据人脸信息匹配人脸特征值app

知道大体的原理和大体的步骤接下来就好搞了,中途就算有遇到不知道的写代码能够查查官方文档,并不影响开发,若是有想要了解算法也可深刻研究一下。机器学习

// 加载人脸识别的级联分类器
CascadeClassifier cascadeClassifier;

JNIEXPORT void JNICALL
Java_com_darren_ndk_day05_FaceDetection_loadCascade(JNIEnv *env, jobject instance,
                                                    jstring filePath_) {
    const char *filePath = env->GetStringUTFChars(filePath_, 0);
    cascadeClassifier.load(filePath);
    env->ReleaseStringUTFChars(filePath_, filePath);

    LOGE("人脸识别级联分类器加载成功");
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_darren_ndk_day05_FaceDetection_faceDetectionSave(JNIEnv *env, jobject instance,
                                                          jobject bitmap) {

    // opencv 操做图片操做的都是 矩阵 Mat
    // 1. bitmap2Mat
    Mat mat = bitmap2Mat(env, bitmap);

    Mat grayMat;
    // 2. 转成灰度图,提高运算速度,灰度图所对应的 CV_8UC1 单颜色通道,信息量少 0-255 1u
    cvtColor(mat, grayMat, CV_RGBA2GRAY);
    
    // 3. 转换直方图均衡化补偿
    Mat equalizeMat;
    equalizeHist(grayMat, equalizeMat);

    // 4. 检测人脸,这是个大问题
    std::vector<Rect> faces;
    cascadeClassifier.detectMultiScale(equalizeMat, faces, 1.1, 5, 0 | CV_HAAR_SCALE_IMAGE,
                                       Size(160, 160));

    LOGE("检测到人脸的个数:%d", faces.size());
    if (faces.size() == 1) {
        Rect faceRect = faces[0];
        // 画一个框框,标记出人脸
        rectangle(mat, faceRect, Scalar(255, 155, 155), 3);
        mat2Bitmap(env, mat, bitmap);

        // 只裁剪人脸部分的直方均衡补偿
        Mat saveMat = Mat(equalizeMat, faceRect);
        // mat 保存成文件  png ,上传到服务器吧,接着下一张(眨眼,张嘴巴)
        imwrite("xxxx/xxx.png", equalizeMat);
        return 1;
    }
    return 0;
}
复制代码

4. 额外体会

记得曾经的预判是 17 年人工智能会完全火起来,因此16年本身选择了一家主要作 AR 和 VR 的企业,但并不知外面的世界,18 年这一年变化应该会蛮大的,就是不知道会不会完全起火。将来的就业机会确定会愈来愈多,只不过可能都是一些高端就业,要求高了一些而已,不少人说工做难找了,实际上是你的工做难找了。工具

17年共享单车和共享汽车火了,咱们只是一个开发者,有时很难去判断其余东西,前几天看了一篇文章《人民想念周鸿祎》有人说那是 360 本身炒做的,且不管是不是炒做,这其实说的就是一个趋势。17年互联网不管新萌芽的企业仍是正在崛起的企业大部分都选择了战队,要么是 T 队要么是 A 队,B 队却是比较少。因此咱们想要过上好日子,选择大企业何尝不可,这就是一个大趋势而已。学习

越日后走咱们须要思考的问题确定就会越多,面对的压力就会愈来愈大,当咱们站的位置不同,所看到的便会不同这也是一种体验,因此咱们须要阅读大量的书籍,作好规划锻炼好身体以便迎接将来,积极乐观,各自珍重。

视频讲解:https://pan.baidu.com/s/1htG9vDU

相关文章
相关标签/搜索