美文网首页822思享实验室
基于OpenCV和dlib的人脸交换实现探究

基于OpenCV和dlib的人脸交换实现探究

作者: HellyCla | 来源:发表于2017-12-16 14:41 被阅读433次

    计算机视觉对我来说是一个全新的知识领域,希望能够逐步入门,从图像识别、人脸检测等问题的研究探讨逐步过渡到三维人脸重建技术的研究。在知识空白的情况下,我首先了阅读一些相关的论文,然后选择了一个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)。两张人脸对应了两个特征部分,接下来想办法实现这两个特征部分的轮廓对齐(经过平移旋转等变换),即实现人脸对齐。之后我们通过仿射变换实现互相交换覆盖区,再经过颜色矫正和边缘融合就基本实现了人脸交换。

    项目实现细节:

    整体框架图

    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] }

    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!

    相关文章

      网友评论

        本文标题:基于OpenCV和dlib的人脸交换实现探究

        本文链接:https://www.haomeiwen.com/subject/qikjwxtx.html