基於OpenCV和dlib的人臉交換實現探究

weixin_34402408發表於2017-12-16

計算機視覺對我來說是一個全新的知識領域,希望能夠逐步入門,從影象識別、人臉檢測等問題的研究探討逐步過渡到三維人臉重建技術的研究。在知識空白的情況下,我首先了閱讀一些相關的論文,然後選擇了一個GitHub上的專案,部署環境並參照執行,在落實到程式碼層的同時進一步研究實現過程和原理,希望能夠理解更多的內容。

DO:

1.閱讀《OpenCV入門教程》及dlib官方文件

2.在win10中配置部署OpenCV和dlib

3.在vs2015中執行faceswap專案

4.梳理實現流程,探究演算法



專案地址:

Real-time FaceSwap application built with OpenCV and dlib


初探:

工欲善其事,必先利其器。在軟體不斷的更新迭代的過程中,由於不同版本型號和缺乏經驗,開發環境的正確搭建總是一個有點頭疼的問題。在OpenCV和dlib的開發環境配置的過程中,還是遇到了不少的問題,試遍StackOverflow以及其他社群的各種可能的解決辦法最後終於成功,後來對一些問題的總結記錄在我的另一篇簡書中。——嘗試專案時遇到的問題和解決方案記錄


簡述:

要實現實時換臉,首先要呼叫相機,將相機快取的幀中的人的面部特徵標記出來,這裡用到了dlib提供的特徵點標記方法,定位正臉並返回68個人臉特徵點的位置(landmark)。兩張人臉對應了兩個特徵部分,接下來想辦法實現這兩個特徵部分的輪廓對齊(經過平移旋轉等變換),即實現人臉對齊。之後我們通過仿射變換實現互相交換覆蓋區,再經過顏色矯正和邊緣融合就基本實現了人臉交換。

專案實現細節:


5806754-94b495ea8e67cd17.jpg
整體框架圖

1.呼叫的主要資原始檔

預設的人臉檢測器:haarcascade_frontalface_default.xml

Dlib68點特徵提取模型:shape_predictor_68_face_landmarks.dat

Dlib與OpenCV其他相關的庫函式

2.

FaceDetectorAndTracker類:實現相機捕獲幀中的人臉檢測、跟蹤以及得到相應的人臉矩形。

FaceSwapper類:實現了面部特徵點的提取,求出仿射變換所需座標,實現五官區域提取、面部對齊並求出變換矩陣,利用直方圖法實現了色彩矯正,最後完成邊緣融合完成人臉交換。

3.關鍵步驟解釋:

1).人臉檢測

基於OpenCV的級聯分類器實現目標檢測,利用的是樣本的Haar特徵,級聯分類器的計算特徵值的基礎類FeatureEvaluator,功能包括讀操作read、複製clone、獲得特徵類getFeatureType,分配圖片分配視窗的操作setImage、setWindow,建立分類器特徵的結構create函式。

主要實現過程:載入級聯分類器->讀取視訊流->對每一幀使用該分類器->得到臉部興趣區域的矩形向量。

在檢測人臉時呼叫的一個關鍵函式detectMultiScale如下

//detect()中呼叫

CV_WRAP void detectMultiScale( InputArray image, CV_OUT std::vector& objects, double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, Size minSize = Size(), Size maxSize = Size() );

這個函式的作用是在輸入影象中檢測不同大小的物件。檢測到的物件作為列表返回的矩形。    

引數說明:@param image CV_8U型別的矩陣,其中包含檢測物件的影象。    

@param objects 每個矩形包含檢測到的物件的矩形向量,矩形可能部分在原始影象之外。

@param scaleFactor引數指定在每個影象比例下影象大小減少了多少。

@param minNeighbors引數指定每個候選矩形應該有多少個要保留的鄰居。

@param flags引數與舊函式中的cvHaarDetectObjects函式具有相同的含義。它不用於新的級聯。

@param minSize最小可能的物件大小。小於這個值的物件被忽略。

@param maxSize最大可能的物件大小。大於此的物件將被忽略。如果`maxSize == minSize`模型是單一尺度評估的。

2).關鍵點定位提取

對攝像頭採集到的每一幀影象快取後進行特徵點檢測並顯示即可。

使用了官方提供的模型構建特徵提取器。

predictor = dlib.shape_predictor(predictor_path) 

獲取特徵點座標:

shapes[shape_index].part(part_index).x()/y()

shape_index是人臉的序號,如shapes[0]代表的是第一個人(可以同時檢測到很多個人),part(i)代表的是第i個特徵點,x()和y()是訪問特徵點座標的途徑。

68個點按順序存放了人臉各部位的座標資訊,程式中選取了8,36,45作為仿射變換的關鍵點。(還不太明白原理-_-)

{IdxRange jaw; // [0 , 16]

IdxRange rightBrow; // [17, 21]

IdxRange leftBrow; // [22, 26]

IdxRange nose; //[27, 35]

IdxRange rightEye; // [36, 41]

IdxRange leftEye; // [42, 47]

IdxRange mouth;// [48, 59]

IdxRange mouth2; // [60, 67] }

5806754-4854a29d6daa91c0.jpg
landmarks 68個關鍵點位置圖


3).仿射變換,人臉對齊

參考了面部對齊部分的講解。主要用到了OpenCV提供的函式warpAffine實現了圖片的變換。

void FaceSwapper::getTransformationMatrices()                                            {   trans_ann_to_bob = cv::getAffineTransform(affine_transform_keypoints_ann, affine_transform_keypoints_bob); cv::invertAffineTransform(trans_ann_to_bob, trans_bob_to_ann); }

4).區域提取

getMasks();                                               

getWarppedMasks();                                   

refined_masks = getRefinedMasks();         

extractFaces();

首先計算出變換矩陣M,然後提取特徵部分的mask並把它變換到要覆蓋的位置得到warppedMasks,warppedMasks和它要覆蓋的特徵部分取並以保證完全覆蓋。最後extractFaces實現調整好的mask到對方幀的互相拷貝。

5).色差矯正(color transfer)

色差矯正的目標是使當前人臉與要被替換的人臉色彩相近。專案中採用了直方圖調整的方式:先計算當前影象和目標影象的顏色直方圖,然後調整當前影象與目標影象的一致,最後將調整後的直方圖應用到當前影象。兩張圖互相經過這樣的處理就實現了色差的矯正。

程式中在colorCorrectFaces()函式中呼叫了specifyHistogram()完成了該功能。

6).邊緣融合

a).影象填充/侵蝕cv::erode

CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue() );

使用特定的結構元素侵蝕影象。該函式使用指定的結構元素來侵蝕源影象。(譯自CV文件)

最小取畫素鄰域的形狀:

\f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]

侵蝕可以應用幾次(迭代)次。在多通道影象的情況下,每個通道都是獨立處理的。

@param src輸入影象;通道的數量可以是任意的,但深度應該是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F其中之一。

@引數dst輸出與src相同大小和型別的影象。

@param kernel 核心結構化元素用於侵蝕;如果`element = Mat()`,一個`3 x 3`矩形結構元素被使用。核心可以使用getStructuringElement建立。

元素內錨的引數錨位置;預設值(-1,-1)表示錨在元素中心。

@param iterations應用erode的次數。

@param borderType畫素外插方法,參閱cv :: BorderTypes

@param borderValue邊界值

@sa dilate,morphologyEx,getStructuringElement

b).邊緣模糊處理cv::blur

CV_EXPORTS_W void blur( InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT );

使用歸一化的盒子過濾器模糊影象。(引用自官方文件)

該函式使用核心平滑影象:\ f {1} {\ texttt {ksize.width * ksize.height}} \ begin {bmatrix} 1&1&1& cdots&1&1 \\ 1&1& 1&1 cdots&1&1 \\ \ hdotsfor {6} \\ 1&1&1& cdots&1&1 \\ \ end {bmatrix} \ f]

呼叫`blur(src,dst,ksize,anchor,borderType)`相當於`boxFilter(src,dst,src.type(),anchor,true,borderType)`。

@param src輸入影象; 它可以有任意數量的獨立處理的通道,但是

深度應該是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F。

@引數dst輸出與src相同大小和型別的影象。

@引數ksize模糊核心大小。

@引數anchor 預設值Point(-1,-1)表示錨點位於核心處

中央。

@引數 borderType邊界模式,用於外推影象外的畫素,參閱cv :: BorderTypes

@sa Filter,bilateralFilter,GaussianBlur,medianBlur

4.涉及部分函式說明:

在整個實現過程中呼叫了OpenCV和dlib庫中的一些函式實現關鍵大部分演算法。

CV_WRAP void detectMultiScale()在輸入影象中檢測不同大小的物件。

CV_EXPORTS_W void matchTemplate()比較模板和重疊的影象區域。

CV_EXPORTS_W void normalize()規範化陣列的範數或值範圍。

CV_EXPORTS_W void minMaxLoc()在陣列中查詢全域性最小值和最大值。

CV_EXPORTS_W void fillConvexPoly()繪製一個填充的凸多邊形。

CV_EXPORTS_W void warpAffine()將仿射變換應用於影象。

CV_EXPORTS_W void blur()使用歸一化的盒子過濾器模糊影象。

CV_EXPORTS_W void erode()使用特定的結構元素侵蝕影象。

關於這些函式的作用和具體的引數介紹我記錄在了我另一篇部落格中FaceSwap函式說明


閱讀文獻及參考連結:

基於仿射變換的多姿態人臉矯正和識別

Robust real-time face detection

One millisecond face alignment with an ensemble of regression trees

人臉識別應用之“變臉”

dlib實現視訊中人臉68特徵點提取

變臉

淺析人臉檢測之Haar分類器方法

曹晨.基於單目視訊相機的實時人臉跟蹤與動畫方法研究[D].浙江大學,2016


未來展望:

之前沒怎麼接觸過計算機視覺領域的具體指示,這次reseach對我來說是一個不小的挑戰,發現其中涉及大量的數學知識,線代,統計學,數學分析等等,雖然感到困難重重,但我感覺到巨大的興趣,在看著paper中對三維人臉重建的講解,我眼前展開的是一幅美妙的畫面,大牛們神乎其技各顯神通,複雜的數學公式背後蘊含著深刻又淳樸的哲理和思想。

作為一個剛接觸的這方面的本科生,很多理論基礎都不紮實,在這個專案中的每個環節需要了解的更深,要想透徹的理解演算法,一是要看透演算法原作者的論文, 二是要讀懂相關的優秀原始碼實現,日後我還需要進一步的夯實基礎,向這個方向努力。

另外關於三維人臉重建(3D face reconstruction)的技術,讀了一些論文和當前的成果,感覺超級有意思,對這個領域充滿了好奇和興趣,之後希望能夠在老師和師兄的指導下逐步深入的學習和研究。I'll  keep trying and do my best!

相關文章