美妝的第一步是人臉檢測,找特征點。關於人臉檢測,有很多成熟的庫,我列舉目前發現到的開源項目(注意軟件所用的協議)。如果大家發現有漏掉的,可以發消息給我。
STASM www.milbo.users.sonic.net/stasm/
dlib http://dlib.net/
OpenFace https://cmusatyalab.github.io/openface/
SeetaFaceEngine https://github.com/seetaface/SeetaFaceEngine
Linkface https://www.linkface.cn/doc/api/v1/face_detection
Face Detection Data Set and Benchmark
STASM 目前最新4.1.0版本,發布於2013/12/27,教之前的3.1版本而言,更快更好地擬合。STASM 采用 ASM(Active Shape Model) 訓練人臉模型,采用的是BSD協議。對於正臉(可以帶表情)識別的不錯,檢測出77個特征點。
打開 jni/stasm/stasm.h 文件,打開宏 TRACE_IMAGES,也就是 #define TRACE_IMAGES 1 ,WIN32下調試時,在 jni/build/ 目錄下可以查看每一步形狀的走勢變化,別忘了在Release下關閉。我用 Obama 的圖片制作了一張GIF。
PerfectShow 工程中,使用 Feature::detectFace() 檢測單張人臉,Feature::detectFaces() 來檢測多張人臉,他們分別調用了 STASM 庫里面的 stasm_search_single (apps/minimal.cpp) 和 stasm_search_auto (apps/minimal2.cpp)。考慮到朋友圈最忌諱的事就是發照片只給自己化妝,卻不給旁邊的朋友美化的情形,所以在照片中有多個人的場景時,需要提供選取人臉分別對每個人化妝的流程。這個功能后期需要添加,暫時未完成。
STASM在額頭兩邊缺少特征點(見下圖),顯得圍成的臉部輪廓線不夠圓滑。所以,我用旋轉的方法在兩邊多各補了兩個特征點。上圖中的划線對應函數 Feature::mark(),在調試的時候有幫助,對用戶而言是不可見的。用到的技術有,Catmull-Rom樣條曲線(Catmull-Rom spline),德洛內三角剖分(Delaunay Triangulization),最小二乘法(Least Mean Square method)。
特征點連接起來的線段不夠好,對后期眉毛、嘴巴的化妝有影響。數值分析里有講到生成光滑曲線的幾種插值方法,這里我選擇了 Catmull-Rom 樣條曲線,樣條曲線是為了產生光滑的邊緣,對應 jni/venus/opencv_utility.h 里的 catmullRomSpline() 函數。
與 Delaunay “對偶”的一個人叫 Voronoi,Delaunay 三角化與 Voronoi 圖都是計算幾何里面的。這種三角划分最大化最小角,讓划分區域集中,避免長條三角形的出現。因為人臉特征點的相對位置一樣,Delaunay 出來的三角形拓撲也幾乎一樣,說“幾乎”是因為由於偏頭動作、大笑等表情,會影響到最終的三角拓撲結構。所以不同的人臉不能共用這個結果。為了計算的統一,我將這個特征點的索引序列 const std::vector<Vec3b> Feature::triangle_indices 保存下來了。上一篇文章的圖,參考論文 Digital Face Makeup by Example 的思路,從妝廊里選擇一副妝容應用到用戶臉上時,需要對三角化后的妝容圖片每個三角形面片進行拉伸,與用戶臉上的三角塊一致后,就可以應用妝容了。
最小二乘法的發現歸功於數學小王子高斯。我在工程中用到了最小二乘法。一般人臉是左右對稱的,腮紅應用妝容的時候,會作鏡像處理貼上去。在貼之前,需要找到人臉的對稱軸才能鏡像。一個簡單的做法是,取人臉檢測(Haar特征 haarcascade_mcs_lefteye.xml,haarcascade_mcs_righteye.xml)到的眼角內點或外點的垂直平分線。利用眼珠子計算肯定是不行的,因為眼珠子會左右轉動,計算出的結果不准確。而眼角,也可能會由於偏頭、眨眼、眯眼等原因,人臉檢測出的位置也可能有幾度的偏差。考錄到左右特征點連線的中點會落到對稱軸上,最終我嘗試把臉上左右對稱的特征點(剔除了像眼珠子一樣在臉上位置可能變動的特征點)求中點,擬合求出對稱軸。大家可以看到第一張 Obama 圖中的落到對稱軸附近的叉叉就是中點,綠線是擬合出來的對稱軸。OpenCV 里可以直接調用函數 cv::fitLine,工程代碼里對應 Feature::getSymmetryAxis()。
公司也考慮過做美甲項目,這同樣也涉及到檢測特征點。
手冊的第四章里有介紹,如何構建關於手掌分類器的模型,有興趣的請移步。
dlib代碼地址 https://github.com/davisking/dlib,一直都在更新,里面除了人臉識別,還有其他機器學習的算法。因為用到了 C++11 的一些特性,而 Visual Studio 對 C++11 的支持較落后,所以要求 VS2015 以上。dlib 檢測的特征點比 STASM 更准確,工程文件對應 face_landmark_detection_ex.cpp 我用的是已經訓練好的數據 shape_predictor_68_face_landmarks.dat,給出了68個特征點,相比 STASM 而言,沒有額頭特征點,眉毛沒有構成封閉的多邊形。網上也有標定好196個特征點的 Helen 數據庫,我也測試過,因為它可以標定的區域更密集,從理論上說化妝效果也應該會更好。
給分類器文件瘦身
考慮到移動端的性能因素,shape_predictor_68_face_landmarks.dat 這個文件太大,BZIP壓縮后61M,解壓后有95M,直接打包在APK里,可能不太好。應該更適合聯網下載,這是 dlib 輸給 STASM 的一個地方,STASM 只需要幾兆左右的空間,相關文件 haarcascade_frontalface_alt2.xml,haarcascade_mcs_lefteye.xml,haarcascade_mcs_righteye.xml,而且可以通過處理,縮減到一半大小。
對於網上提供的這些文件,XML文件以文本形式打開,會發現里面有很多注釋和空白字符,這些都是為了方便理解數據,但是對於運行時則無用,占內存不說,還增加了解析時間,所以可以去掉。浮點數據類型,用文本形式存儲的話很占空間,這個可以選擇截斷長度,對檢測結果影響不大。所以在后期,我給分類器做了瘦身處理。haarcascade_mcs_mouth.xml 大小702 KB,刪除注釋和多余的空格字符后344KB;haarcascade_mcs_righteye.xml大小1.35 MB,同樣的操作后662 KB,還是很可觀的,差不多縮減到一半。浮點數的存儲,二進制比文本更節省空間,可以嘗試降低浮點數精度儲存,比如-1.2275323271751404E-02 替換成-1.2275E-2。