1. 基于 Android Studio 的 opencv 配置與使用
先推薦一本書《計算機視覺 - 算法與應用》,相信用過 OpenCV 的哥們都知道這是用來干啥的,這里我就不再啰嗦。只說一下他的應用領域:人機互動、物體識別、圖像分割、人臉識別、動作識別、運動跟蹤、機器人、運動分析、機器視覺、結構分析、汽車安全駕駛等等。這次我們主要用它來做人臉識別,注意人臉檢測和人臉識別是兩個概念。
首先先去官網 https://opencv.org/opencv-3-2.html 下載 Android SDK: sourceforge ,下載下來以后我們的開發方式目前有兩種:一種是基于 OpenCV_3.2.0_Manager.apk 的純 Java 代碼;還有一種方式是配置好 opencv 后利用 Android NDK,使用C++開發。
不管怎樣都需要配置依賴 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 這些本身具有深度學習和機器學習的設備,或許以后就會變得不一樣,但是這些或多或少都跟硬件有關系。既然知道比較的是什么,那么來我們就可以來走下邏輯了。
人臉特征的錄入
?1.1 打開相機檢測是否有人臉
?1.2 保存人臉特征信息(可保存多份)人臉特征匹配識別
?2.2 打開相機檢測是否有人臉
?2.2 根據人臉信息匹配人臉特征值
知道大致的原理和大致的步驟接下來就好搞了,中途就算有遇到不知道的寫代碼可以查查官方文檔,并不影響開發,如果有想要了解算法也可深入研究一下。
// 加載人臉識別的級聯分類器
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 隊倒是比較少。所以我們想要過上好日子,選擇大企業未嘗不可,這就是一個大趨勢而已。
越往后走我們需要思考的問題肯定就會越多,面對的壓力就會越來越大,當我們站的位置不一樣,所看到的便會不一樣這也是一種體驗,所以我們需要閱讀大量的書籍,做好規劃鍛煉好身體以便迎接未來,積極樂觀,各自珍重。