計(jì)算機(jī)視覺對(duì)我來說是一個(gè)全新的知識(shí)領(lǐng)域触趴,希望能夠逐步入門,從圖像識(shí)別、人臉檢測(cè)等問題的研究探討逐步過渡到三維人臉重建技術(shù)的研究宏所。在知識(shí)空白的情況下,我首先了閱讀一些相關(guān)的論文摊溶,然后選擇了一個(gè)GitHub上的項(xiàng)目爬骤,部署環(huán)境并參照運(yùn)行,在落實(shí)到代碼層的同時(shí)進(jìn)一步研究實(shí)現(xiàn)過程和原理莫换,希望能夠理解更多的內(nèi)容霞玄。
DO:
1.閱讀《OpenCV入門教程》及dlib官方文檔
2.在win10中配置部署OpenCV和dlib
3.在vs2015中運(yùn)行faceswap項(xiàng)目
4.梳理實(shí)現(xiàn)流程骤铃,探究算法
項(xiàng)目地址:
Real-time FaceSwap application built with OpenCV and dlib
初探:
工欲善其事,必先利其器坷剧。在軟件不斷的更新迭代的過程中惰爬,由于不同版本型號(hào)和缺乏經(jīng)驗(yàn),開發(fā)環(huán)境的正確搭建總是一個(gè)有點(diǎn)頭疼的問題听隐。在OpenCV和dlib的開發(fā)環(huán)境配置的過程中补鼻,還是遇到了不少的問題,試遍StackOverflow以及其他社區(qū)的各種可能的解決辦法最后終于成功雅任,后來對(duì)一些問題的總結(jié)記錄在我的另一篇簡(jiǎn)書中风范。——嘗試項(xiàng)目時(shí)遇到的問題和解決方案記錄
簡(jiǎn)述:
要實(shí)現(xiàn)實(shí)時(shí)換臉沪么,首先要調(diào)用相機(jī)硼婿,將相機(jī)緩存的幀中的人的面部特征標(biāo)記出來,這里用到了dlib提供的特征點(diǎn)標(biāo)記方法禽车,定位正臉并返回68個(gè)人臉特征點(diǎn)的位置(landmark)寇漫。兩張人臉對(duì)應(yīng)了兩個(gè)特征部分,接下來想辦法實(shí)現(xiàn)這兩個(gè)特征部分的輪廓對(duì)齊(經(jīng)過平移旋轉(zhuǎn)等變換)殉摔,即實(shí)現(xiàn)人臉對(duì)齊州胳。之后我們通過仿射變換實(shí)現(xiàn)互相交換覆蓋區(qū),再經(jīng)過顏色矯正和邊緣融合就基本實(shí)現(xiàn)了人臉交換逸月。
項(xiàng)目實(shí)現(xiàn)細(xì)節(jié):
1.調(diào)用的主要資源文件:
默認(rèn)的人臉檢測(cè)器:haarcascade_frontalface_default.xml
Dlib68點(diǎn)特征提取模型:shape_predictor_68_face_landmarks.dat
Dlib與OpenCV其他相關(guān)的庫函數(shù)
2.類:
FaceDetectorAndTracker類:實(shí)現(xiàn)相機(jī)捕獲幀中的人臉檢測(cè)栓撞、跟蹤以及得到相應(yīng)的人臉矩形。
FaceSwapper類:實(shí)現(xiàn)了面部特征點(diǎn)的提取碗硬,求出仿射變換所需坐標(biāo)瓤湘,實(shí)現(xiàn)五官區(qū)域提取、面部對(duì)齊并求出變換矩陣恩尾,利用直方圖法實(shí)現(xiàn)了色彩矯正弛说,最后完成邊緣融合完成人臉交換。
3.關(guān)鍵步驟解釋:
1).人臉檢測(cè)
基于OpenCV的級(jí)聯(lián)分類器實(shí)現(xiàn)目標(biāo)檢測(cè)翰意,利用的是樣本的Haar特征木人,級(jí)聯(lián)分類器的計(jì)算特征值的基礎(chǔ)類FeatureEvaluator,功能包括讀操作read猎物、復(fù)制clone虎囚、獲得特征類getFeatureType,分配圖片分配窗口的操作setImage蔫磨、setWindow,創(chuàng)建分類器特征的結(jié)構(gòu)create函數(shù)圃伶。
主要實(shí)現(xiàn)過程:加載級(jí)聯(lián)分類器->讀取視頻流->對(duì)每一幀使用該分類器->得到臉部興趣區(qū)域的矩形向量堤如。
在檢測(cè)人臉時(shí)調(diào)用的一個(gè)關(guān)鍵函數(shù)detectMultiScale如下
//detect()中調(diào)用
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() );
這個(gè)函數(shù)的作用是在輸入圖像中檢測(cè)不同大小的對(duì)象蒲列。檢測(cè)到的對(duì)象作為列表返回的矩形。? ??
參數(shù)說明:@param image CV_8U類型的矩陣搀罢,其中包含檢測(cè)對(duì)象的圖像蝗岖。? ??
@param objects 每個(gè)矩形包含檢測(cè)到的對(duì)象的矩形向量,矩形可能部分在原始圖像之外。
@param scaleFactor參數(shù)指定在每個(gè)圖像比例下圖像大小減少了多少榔至。
@param minNeighbors參數(shù)指定每個(gè)候選矩形應(yīng)該有多少個(gè)要保留的鄰居抵赢。
@param flags參數(shù)與舊函數(shù)中的cvHaarDetectObjects函數(shù)具有相同的含義。它不用于新的級(jí)聯(lián)唧取。
@param minSize最小可能的對(duì)象大小铅鲤。小于這個(gè)值的對(duì)象被忽略。
@param maxSize最大可能的對(duì)象大小枫弟。大于此的對(duì)象將被忽略邢享。如果`maxSize == minSize`模型是單一尺度評(píng)估的。
2).關(guān)鍵點(diǎn)定位提取
對(duì)攝像頭采集到的每一幀圖像緩存后進(jìn)行特征點(diǎn)檢測(cè)并顯示即可淡诗。
使用了官方提供的模型構(gòu)建特征提取器骇塘。
predictor = dlib.shape_predictor(predictor_path)?
獲取特征點(diǎn)坐標(biāo):
shapes[shape_index].part(part_index).x()/y()
shape_index是人臉的序號(hào),如shapes[0]代表的是第一個(gè)人(可以同時(shí)檢測(cè)到很多個(gè)人)韩容,part(i)代表的是第i個(gè)特征點(diǎn)款违,x()和y()是訪問特征點(diǎn)坐標(biāo)的途徑。
68個(gè)點(diǎn)按順序存放了人臉各部位的坐標(biāo)信息群凶,程序中選取了8插爹,36,45作為仿射變換的關(guān)鍵點(diǎn)座掘。(還不太明白原理-_-)
{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] }
3).仿射變換递惋,人臉對(duì)齊
參考了面部對(duì)齊部分的講解。主要用到了OpenCV提供的函數(shù)warpAffine實(shí)現(xiàn)了圖片的變換溢陪。
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).區(qū)域提取
getMasks();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
getWarppedMasks();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
refined_masks = getRefinedMasks();? ? ? ? ?
extractFaces();
首先計(jì)算出變換矩陣M萍虽,然后提取特征部分的mask并把它變換到要覆蓋的位置得到warppedMasks,warppedMasks和它要覆蓋的特征部分取并以保證完全覆蓋形真。最后extractFaces實(shí)現(xiàn)調(diào)整好的mask到對(duì)方幀的互相拷貝杉编。
5).色差矯正(color transfer)
色差矯正的目標(biāo)是使當(dāng)前人臉與要被替換的人臉色彩相近。項(xiàng)目中采用了直方圖調(diào)整的方式:先計(jì)算當(dāng)前圖像和目標(biāo)圖像的顏色直方圖咆霜,然后調(diào)整當(dāng)前圖像與目標(biāo)圖像的一致邓馒,最后將調(diào)整后的直方圖應(yīng)用到當(dāng)前圖像。兩張圖互相經(jīng)過這樣的處理就實(shí)現(xiàn)了色差的矯正蛾坯。
程序中在colorCorrectFaces()函數(shù)中調(diào)用了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() );
使用特定的結(jié)構(gòu)元素侵蝕圖像。該函數(shù)使用指定的結(jié)構(gòu)元素來侵蝕源圖像脉课。(譯自CV文檔)
最小取像素鄰域的形狀:
\f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
侵蝕可以應(yīng)用幾次(迭代)次救军。在多通道圖像的情況下财异,每個(gè)通道都是獨(dú)立處理的。
@param src輸入圖像;通道的數(shù)量可以是任意的唱遭,但深度應(yīng)該是CV_8U戳寸,CV_16U,CV_16S拷泽,CV_32F或CV_64F其中之一疫鹊。
@參數(shù)dst輸出與src相同大小和類型的圖像。
@param kernel 內(nèi)核結(jié)構(gòu)化元素用于侵蝕;如果`element = Mat()`司致,一個(gè)`3 x 3`矩形結(jié)構(gòu)元素被使用拆吆。內(nèi)核可以使用getStructuringElement創(chuàng)建。
元素內(nèi)錨的參數(shù)錨位置;默認(rèn)值(-1蚌吸,-1)表示錨在元素中心锈拨。
@param iterations應(yīng)用erode的次數(shù)。
@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 );
使用歸一化的盒子過濾器模糊圖像佩微。(引用自官方文檔)
該函數(shù)使用內(nèi)核平滑圖像:\ 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]
調(diào)用`blur(src缝彬,dst,ksize哺眯,anchor谷浅,borderType)`相當(dāng)于`boxFilter(src,dst奶卓,src.type()一疯,anchor,true夺姑,borderType)`墩邀。
@param src輸入圖像; 它可以有任意數(shù)量的獨(dú)立處理的通道,但是
深度應(yīng)該是CV_8U盏浙,CV_16U眉睹,CV_16S,CV_32F或CV_64F废膘。
@參數(shù)dst輸出與src相同大小和類型的圖像竹海。
@參數(shù)ksize模糊內(nèi)核大小。
@參數(shù)anchor 默認(rèn)值Point(-1丐黄,-1)表示錨點(diǎn)位于內(nèi)核處
中央斋配。
@參數(shù) borderType邊界模式,用于外推圖像外的像素,參閱cv :: BorderTypes
@sa Filter许起,bilateralFilter十偶,GaussianBlur菩鲜,medianBlur
4.涉及部分函數(shù)說明:
在整個(gè)實(shí)現(xiàn)過程中調(diào)用了OpenCV和dlib庫中的一些函數(shù)實(shí)現(xiàn)關(guān)鍵大部分算法园细。
如
CV_WRAP void detectMultiScale()在輸入圖像中檢測(cè)不同大小的對(duì)象。
CV_EXPORTS_W void matchTemplate()比較模板和重疊的圖像區(qū)域接校。
CV_EXPORTS_W void normalize()規(guī)范化數(shù)組的范數(shù)或值范圍猛频。
CV_EXPORTS_W void minMaxLoc()在數(shù)組中查找全局最小值和最大值。
CV_EXPORTS_W void fillConvexPoly()繪制一個(gè)填充的凸多邊形蛛勉。
CV_EXPORTS_W void warpAffine()將仿射變換應(yīng)用于圖像鹿寻。
CV_EXPORTS_W void blur()使用歸一化的盒子過濾器模糊圖像。
CV_EXPORTS_W void erode()使用特定的結(jié)構(gòu)元素侵蝕圖像诽凌。
關(guān)于這些函數(shù)的作用和具體的參數(shù)介紹我記錄在了我另一篇博客中FaceSwap函數(shù)說明
閱讀文獻(xiàn)及參考鏈接:
Robust real-time face detection
One millisecond face alignment with an ensemble of regression trees
dlib實(shí)現(xiàn)視頻中人臉68特征點(diǎn)提取
曹晨.基于單目視頻相機(jī)的實(shí)時(shí)人臉跟蹤與動(dòng)畫方法研究[D].浙江大學(xué)毡熏,2016
未來展望:
之前沒怎么接觸過計(jì)算機(jī)視覺領(lǐng)域的具體指示,這次reseach對(duì)我來說是一個(gè)不小的挑戰(zhàn)侣诵,發(fā)現(xiàn)其中涉及大量的數(shù)學(xué)知識(shí)痢法,線代,統(tǒng)計(jì)學(xué)杜顺,數(shù)學(xué)分析等等财搁,雖然感到困難重重,但我感覺到巨大的興趣躬络,在看著paper中對(duì)三維人臉重建的講解尖奔,我眼前展開的是一幅美妙的畫面,大牛們神乎其技各顯神通穷当,復(fù)雜的數(shù)學(xué)公式背后蘊(yùn)含著深刻又淳樸的哲理和思想提茁。
作為一個(gè)剛接觸的這方面的本科生,很多理論基礎(chǔ)都不扎實(shí)馁菜,在這個(gè)項(xiàng)目中的每個(gè)環(huán)節(jié)需要了解的更深茴扁,要想透徹的理解算法,一是要看透算法原作者的論文, 二是要讀懂相關(guān)的優(yōu)秀源碼實(shí)現(xiàn)火邓,日后我還需要進(jìn)一步的夯實(shí)基礎(chǔ)丹弱,向這個(gè)方向努力。
另外關(guān)于三維人臉重建(3D face reconstruction)的技術(shù)铲咨,讀了一些論文和當(dāng)前的成果躲胳,感覺超級(jí)有意思,對(duì)這個(gè)領(lǐng)域充滿了好奇和興趣纤勒,之后希望能夠在老師和師兄的指導(dǎo)下逐步深入的學(xué)習(xí)和研究坯苹。I'll? keep trying and do my best!