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

相關文章
相關標籤/搜索