我們用 dlib 來(lái)做一下人臉特征識(shí)別贱案。
dlib是什么?
官方的自定義是:
lib is a modern C++ toolkit containing machine learning algorithms and tools for creating complex software in C++ to solve real world problems.
它和 opencv 的關(guān)系還蠻緊密的司光,是 opencv 的有效補(bǔ)充。也有專門(mén)的庫(kù)來(lái)處理 opencv 的格式轉(zhuǎn)換什么的侨糟。
iOS 項(xiàng)目導(dǎo)入 dlib
步驟:1.自己編譯靜態(tài)庫(kù) 2.copy 源碼頭文件 3.下載模型
雖然 CocoadPods 有搜索到 pod靠抑,但是不可用界斜。
github 上項(xiàng)目負(fù)責(zé)人也說(shuō)幕帆,不是官方的負(fù)責(zé)的pod娄蔼。所以這里我們采用下載源碼结窘,自己編譯靜態(tài)庫(kù)的方式很洋。
$ cd examples
$ mkdir build
$ cd build
$ cmake -G Xcode ..
$ cmake --build . --config Release
編譯完之后在 dlib_build
下有一個(gè) dlib.xcodeproj 可以打開(kāi),打靜態(tài)包隧枫。
你需要分別打模擬器和真機(jī)的靜態(tài)庫(kù)喉磁,自己合并。
lipo -create xx1.a xx2.a -output libdlib.a
然后將 dlib 的源碼 copy 出來(lái)悠垛,即頭文件了线定。
然后下載 68 個(gè)點(diǎn)位的人臉特征模型 shape_predictor_68_face_landmarks.dat dlib model file donwload,當(dāng)然你也可以下載5個(gè)點(diǎn)位的模型确买,因?yàn)?8挺大的斤讥,將近100M了。
現(xiàn)在我們有3個(gè)文件了
導(dǎo)入Xcode湾趾,不要將 dlib 這個(gè)文件夾的頭文件導(dǎo)入芭商,否則會(huì)報(bào)錯(cuò)。
Xcode 里填寫(xiě) Header Search Paths
和 Library Search Path
搀缠,將 dlib 的目錄填進(jìn)去铛楣。比如我的是
$(PROJECT_DIR)/FaceDemo/resources/dlib
Xcode 視角
另外網(wǎng)上有一些 blog 說(shuō)要導(dǎo)入一些參數(shù),但我測(cè)試不導(dǎo)也沒(méi)問(wèn)題艺普,可能這就是我后面測(cè)試到的性能問(wèn)題簸州。但官方文檔里的預(yù)處理指令和一些 blog 的又不到一致,我也不太確定這里歧譬,但做為 demo岸浑,也就不這么嚴(yán)格了。官方文檔
頭文件引用
#import <dlib/image_processing.h>
#import <dlib/image_processing/frontal_face_detector.h>
#import <dlib/image_processing/render_face_detections.h>
#import <dlib/opencv.h>
frontal_face_detector.h 是用來(lái)做人臉識(shí)別的瑰步,但我們一般不會(huì)用 dlib 來(lái)做人臉識(shí)別矢洲,所以可以不用導(dǎo)入。
render_face_detections.h 是用來(lái)做人臉特征識(shí)別的
opencv.h 是 dlib 專門(mén)用來(lái)處理 Mat 和 dlib::array2d 數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換的缩焦。
顏色空間
因?yàn)槎际翘幚韴D片有關(guān)读虏,我們會(huì)用到很多像素格式問(wèn)題责静。官方文檔 Image Processing 里講得還是比較清楚的。
如果你使用 RGB盖桥,那么對(duì)應(yīng)灾螃,就應(yīng)該使用 rgb_pixel 和 bgr_pixel 的格式。
opencv 的默認(rèn)格式是 bgr揩徊,所以對(duì)應(yīng)應(yīng)該使用 bgr_pixel
睦焕。但如果是 UIImageToMat 轉(zhuǎn)過(guò)來(lái)的,它是帶 alpha通道的靴拱,所以要使用 rgb_alpha_pixel
。
一個(gè)有趣的點(diǎn)猾普,是 opencv 里默認(rèn)的是 BGR袜炕,但是我們平時(shí)經(jīng)常說(shuō)和用的,都是RGB初家。這就出現(xiàn)了通道順序的問(wèn)題偎窘,如果沒(méi)有注意,可能出現(xiàn)圖片顏色失真的問(wèn)題溜在。找到一篇文章 Satya Mallick: Why does OpenCV use BGR color format ? 解釋大意就是:歷史遺留問(wèn)題陌知。那個(gè)有趣的鐵軌的故事,差不多掖肋∑推希可能當(dāng)時(shí)的廠商們比較通用 BGR,但是現(xiàn)在又比較通用 RGB志笼。雖然如此沿盅,opencv 的庫(kù),還是給出了各種格式之間的轉(zhuǎn)換纫溃,非常貼心易用腰涧。這在使用上應(yīng)該沒(méi)有什么難度,只是需要額外注意這個(gè)問(wèn)題而已紊浩。
dlib 的人臉識(shí)別
cv::Mat cvImg, bgrImg;
UIImageToMat([UIImage imageNamed:@"face5"], cvImg);
cvtColor(cvImg, bgrImg, cv::COLOR_RGBA2BGR);
dlib::frontal_face_detector detector = dlib::get_frontal_face_detector();
dlib::array2d<dlib::bgr_pixel> dlibImg;
dlib::assign_image(dlibImg, dlib::cv_image<dlib::bgr_pixel>(bgrImg));
std::vector<dlib::rectangle> dets = detector(dlibImg);
NSLog(@"人臉個(gè)數(shù) %lu",dets.size());//檢測(cè)到人臉的數(shù)量
dlib 是不允許 帶 alpha 通道的數(shù)據(jù)處理窖铡。所以我們需要在 OpenCV 轉(zhuǎn)換一下。
要注意的一點(diǎn)坊谁,是 iOS 系統(tǒng)和 OpenCV 的顏色空間通道順序是不同的费彼。
UIImage里是 RGB+Alpha 的形式,但是 OpenCV 的顏色空間順序是 BGR
如果這一步呜袁,你的 Mat 是 灰度圖敌买,那么 <dlib::bgr_pixel>
就寫(xiě)成<unsigned char>
來(lái)處理。
總之阶界,不同處理算法的顏色通道順序一定需要注意一下虹钮。
dlib 獲取 landmarks(特征)
特征識(shí)別的速度非沉郑快,并且特別簡(jiǎn)單芙粱。
我們用 dlib::shape_predictor祭玉,它是可以當(dāng)做全局變量使用的,不是一次性的春畔。
NSString *landmarkPath = [[NSBundle mainBundle] pathForResource:@"shape_predictor_68_face_landmarks" ofType:@"dat"];
std::string modelFileString = [landmarkPath UTF8String];
dlib::shape_predictor sp;
dlib::deserialize(modelFileString) >> sp;
將我們之前 OpenCV 優(yōu)化過(guò)的處理加上 dlib
同樣因?yàn)樘幚淼氖腔叶葓D脱货,所以應(yīng)該寫(xiě) <unsigned char>
格式。如果這里律姨,你傳 cv::Mat 是攝像頭的原圖振峻,而不是灰度圖,那么就要用 <dlib::bgr_pixel>
择份。
并且涉及到 cv::Rect 和 dlib::rectangle扣孟、cv::Point 和 dlib::point 的單位轉(zhuǎn)換,需要注意一下荣赶。
double scale = 1.0 / 4.0;
cv::Mat orignalImg, gray, smallImg;
UIImageToMat([UIImage imageNamed:@"face1"], orignalImg);
// 灰度化凤价、縮小、直方圖增強(qiáng)對(duì)比
cvtColor( orignalImg, gray, cv::COLOR_BGRA2GRAY);
resize( gray, smallImg, smallImg.size(), scale, scale, cv::INTER_LINEAR_EXACT );
equalizeHist( smallImg, smallImg );
std::vector<cv::Rect> faces;
cv::Size minimumSize(0, 0);
faceDetector.detectMultiScale(smallImg, faces, 1.2, 6, 0, minimumSize);
dlib::array2d<unsigned char> dlibImage;
dlib::assign_image(dlibImage, dlib::cv_image<unsigned char>(smallImg));
cv::Scalar color = cv::Scalar( rand() & 255, rand() & 255, rand() & 255 );
for ( size_t i = 0; i < faces.size(); i++ ) {
cv::rectangle(orignalImg, cv::Point(faces[i].x / scale, faces[i].y / scale), cv::Point(faces[i].x / scale + faces[i].width / scale, faces[i].y / scale + faces[i].height / scale),
color);
// dlib Landmarks
dlib::rectangle det(faces[i].tl().x,faces[i].tl().y, faces[i].br().x, faces[i].br().y);
dlib::full_object_detection shape = sp(dlibImage, det);
for (unsigned long k = 0; k < shape.num_parts(); k++) {
dlib::point p = shape.part(k);
cv::circle(orignalImg, cv::Point(p.x() / scale, p.y() / scale), 2, color, 2);
}
}
self.resultView.image = MatToUIImage(orignalImg);
測(cè)試
應(yīng)用
當(dāng)你擁有了這些特征點(diǎn)之后拔创,就可以做很多事利诺。
比如測(cè)試某幾個(gè)點(diǎn)的比,看是不是皺眉啦剩燥、眨眼啦慢逾。甚至換臉等等操作(我沒(méi)有試過(guò),猜的)灭红。
參考
dlib: Miscellaneous Preprocessor Directives
Convert OpenCV's Rect to dlib's rectangle?
會(huì)飛的大馬猴: iOS 相機(jī)流人臉識(shí)別(二)-關(guān)鍵點(diǎn)檢測(cè)(face landmark --Dlib 附demo)