關於人臉檢測和識別,應用的範圍是很是廣的,其實以前的《NDK開發前奏 - 實現支付寶人臉識別功能》 也有提到,只是那時並未具體的去分析算法和實現原理,這裏筆者打算一步一步來分析和實現人臉識別,首先咱們得要明確人臉檢測和人臉識別是兩個不一樣的概念,人臉檢測是檢測有人臉,人臉識別是匹配你是你,他們所採用的算法也是不同的,這篇文章是基於人臉檢測來實現人臉識別。咱們先來看下已經實現了的人臉檢測效果: 算法
人臉馬賽克你們能夠忽略,並非這裏要關注的內容,是由於長得太帥了怕你們嫉妒。馬賽克效果實現你們能夠參考《圖形圖像處理 - 手寫 QQ 說說圖片處理效果》。首先咱們不妨來思考一下,要實現像支付寶的人臉識別和員工刷臉簽到等等,這樣的一些應用功能,咱們須要用到哪些知識?須要通過哪些步驟呢?其實這裏分爲兩步,第一步是樣本數據採集,第二步是檢測和匹配。不過咱們得先來看幾個概念:均值,標準差,協方差矩陣,特徵值,特徵向量,PCA降維。bash
1. 均值,標準差,協方差矩陣ui
這幾個都是機率論中的概念,咱們隨便舉一個例子來算下便可,假設個人 Mat 數據以下:spa
Mat src = (Mat_<int>(3, 3) << 50, 50, 50, 60, 60, 60, 70, 70, 70);
複製代碼
均值:[60]code
標準差:[8.164965809277252]orm
協方差矩陣:[200, 200, 200; 200, 200, 200; 200, 200, 200]cdn
2. 特徵值,特徵向量視頻
關於特徵值與特徵向量這是線性代數的概念,仍是老套路拿個例子過來算下,能算出來就能夠了,同時你們也能夠參考這篇文章: en.wikipedia.org/wiki/Eigenv…xml
3. PCA降維blog
人臉識別確定須要採集人臉樣本,也就是相機採集過來的 Mat 數據,那麼大量的數據怎麼處理呢?這裏就須要 PCA 降維了:
4. 樣本訓練
接下來就是代碼層面的東西了,代碼是很簡單的就那麼幾句話,但關鍵其實仍是在於理解裏面的原理:
FaceDetection_trainingPattern(JNIEnv *env, jobject instance) {
// 訓練樣本,這一步是在數據採集作的
// train it
vector<Mat> faces;
vector<int> labels;
// 樣本比較少
for (int i = 1; i <= 5; ++i) {
for (int j = 1; j <= 5; ++j) {
Mat face = imread(format("/storage/emulated/0/s%d/%d.pgm", i, j), 0);
if (face.empty()) {
LOGE("face mat is empty");
continue;
}
// 確保大小一致
resize(face, face, Size(128, 128));
faces.push_back(face);
labels.push_back(i);
}
}
for (int i = 1; i <= 8; ++i) {
Mat face = imread(format("/storage/emulated/0/face_%d.png", i), 0);
if (face.empty()) {
LOGE("face mat is empty");
continue;
}
resize(face, face, Size(128, 128));
faces.push_back(face);
labels.push_back(11);
}
// 訓練方法
Ptr<BasicFaceRecognizer> model = EigenFaceRecognizer::create();
// 採集了八張,同一我的 label 同樣
model->train(faces, labels);
// 訓練樣本是 xml ,本地
model->save("/storage/emulated/0/face_darren_pattern.xml");// 存的是處理的特徵數據
LOGE("樣本訓練成功");
}
複製代碼
5. 匹配識別
FaceDetection_faceDetection(JNIEnv *env, jobject instance,
jlong nativeObj) {
Mat *src = reinterpret_cast<Mat *>(nativeObj);
int width = src->rows;
int height = src->cols;
Mat grayMat;
// 2. 轉成灰度圖,提高運算速度,灰度圖所對應的 CV_8UC1 單顏色通道,信息量少 0-255 1u
cvtColor(*src, grayMat, COLOR_BGRA2GRAY);
// 4. 檢測人臉,這是個大問題
// 參數 1.1 會採起上採樣和降採樣 ,縮放比例
// 參數 3 檢測多少次
// 參數 Size(width / 2, height / 2) 最小臉的大小
std::vector<Rect> faces;
cascadeClassifier.detectMultiScale(grayMat, faces, 1.1, 3, 0, Size(width / 2, height / 2));
if (faces.size() != 1) {
mosaicFace(*src);
return;
}
// 把臉框出來
Rect faceRect = faces[0];
rectangle(*src, faceRect, Scalar(255, 0, 0, 255), 4, LINE_AA);
// 不斷檢測,錄入 10 張,張張嘴巴,眨眨眼睛 ,保證準確率
// 還須要注意一點,確保人臉大小一致,reSize(128,128) ,確保收集到的人臉眼睛儘可能在一條線上
// 與服務端進行比對,是否是我
// 用一個計數器,這裏咱們作及時的
Mat face = (*src)(faceRect).clone();
resize(face, face, Size(128, 128));
cvtColor(face, face, COLOR_BGRA2GRAY);
// 直方均衡,harr 檢測人臉
int label = model->predict(face);
// 訓練的時候存的是 11
if (label == 11) {
// 識別到了本身
LOGE("識別到了本身");
putText(*src, "Darren", Point(faceRect.x + 20, faceRect.y - 20),
HersheyFonts::FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 0, 255),2,LINE_AA);
} else {
// 不是本身
LOGE("不是本身");
putText(*src, "UnKnow", Point(faceRect.x + 20, faceRect.y - 20),
HersheyFonts::FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 0, 255),2,LINE_AA);
}
// 速度, 準確率, 人臉儘可能正常
mosaicFace((*src)(faceRect));
}
複製代碼
視頻地址:pan.baidu.com/s/1lF92ev7_… 視頻密碼:jc4k