人物肖像风格转换

原文地址:http://blog.csdn.net/hjimce/article/details/45534333

作者:hjimce

一、前言

对于风格转换,2014年siggraph上面出了一篇比较不错的paper:《Style Transfer for Headshot Portraits》 ,这篇文献涉及到的算法非常多,可以说,如果要把这篇paper的代码从头到尾写过一遍,相当复杂。即使是paper作者本人,也只是通过代码拼凑实现的。因为这篇文章涉及到十几篇paper的算法。我这边主要讲解这篇文献的总流程,如果你打算把这篇文献完全看懂,那么对于图像融合、抠图、sift、图像变形等这些基础算法都要非常熟悉,因为这篇paper就是通过这些基础算法组合在一起实现的。当然如果自己把这篇paper搞过一遍,那么真的是可以学到好多经典算法。

先看一下paper的效果图:

这篇paper如果不考虑速度问题,那么真的是有很广泛的工程应用,paper实现的功能:输入一副图片Input,然后在输入一张Example图片,然后通过算法,可以把input图片的光照风格转换成Example图片的风格,感觉牛逼哄哄的样子。

它的工程应用,文章为我们列出了几个,我在这里简单讲解一下它的可能工程应用:

1、首先是化妆,我们知道现在天天P图、美妆等一系列软件化妆软件最近挺火的,天天p图的就是因为武媚娘妆而成名。利用本篇算法我们可以实现基于模板的化妆算法:

如图所示,也就是输入一张用户图片Input,然后输入一张化妆好的模板图片,可以实现用户图片Input的化妆,把Example的化妆结果,传输到Input上。不过这个我个人感觉很难达到工程应用,因为它对图片的对齐要求很高,而且用户输入的图片的背景也会跟着Example的背景进行转换,所以这就是为什么paper中演示的图片实例,基本上都是用单一背景图片。当然虽然不能达到工程应用,但是学了这篇paper我们可以学到很多东西,所以非常值得学习,特别是对于搞图片美化、美容软件的算法人,更必须学习。

2、瞳孔光照转换

这个在现有的美图秀秀、天天p图就有类似的功能,就是“亮眼”功能,当然这篇paper的亮眼比较高级,因为它不仅仅可以实现亮眼,而且可以把Exmaple的眼睛风格转换到Input图片上,如果Example的眼睛是蓝色的,那么Input也会跟着转换,牛逼哄哄。在paper的主页上,有很多眼睛光照转换结果,看一下下面一张效果图片:

总之,学完这篇paper你会感觉自己学到了n多种算法,虽然因为背景问题,很难被用于工程APP中,但是对于我们学算法的,必须好好解读。

二、算法流程

开始这篇paper之前,我们需要知道paper的主要创新点,paper的主要创新点是提出了基于拉普拉金字塔的图像风格转换,因此主要创新点是利用图像融合算法,实现风格转换。下面开始讲解算法流程:

1、对齐阶段

人脸对齐,这一步涉及到稠密sift对齐、基于特征线的图像变形算法,对应的文献分别为:《Sift flow: Dense correspondence across scenes and its applications》、《Feature-based image metamorphosis》。这一步算法主要流程:

(1)先通过人脸特征点检测算法,检测到68个face landmark,这一步如果要自己实现,可以用AAM算法,或者用CNN,相关的人脸特征点检测的paper很多,近几年单单face++就发表了好几篇高精度人脸特征点检测的算法。基于CNN人脸特征点定位精度,从《Deep Convolutional Network Cascade for Facial Point Detection》的效果看是精度挺高的,不过要实现这一步,对于我们仅仅只是为了实现风格转换,要花很长时间。所以还是建议直接到face++官网注册免费人脸特征点检测吧。

(2)以face landmark作为控制顶点,利用图像变形算法,对Example图像进行变形,把Example的人脸特征点对齐到Input的人脸特征点上,这一步我们又称之为粗对齐。对齐算法采用《Feature-based image metamorphosis》,这个变形算法,测试了一下,相比于《As-rigid-as-possible_shape_manipulation》、《Image Deformation Using Moving Least Squares》变形算法来说,其特点是可以实现保证图像人脸变形弧度比较大的时候,脸型的曲线过度比较自然。因此这个变形算法,可以用于瘦脸、眼睛放大等算法中。

(3)精对齐。这一步使用Dense sift flow进行对齐。其实我感觉这一步可以省了,因为用Dense sift flow进行对齐,有的时候感觉对齐效果很不好。

通过对齐步骤,我们可以把Example图片,变形到与Input一样形状,如下图所示:

最左边的图片便是Example变形结果了。图像对齐这一步不是paper的创新点,我们可以选择粗略的看一下就好了。

2、融合阶段

这一步的算法很重要,因为它是paper的主要创新点,是最值得我们学习的地方,需要好好琢磨,因为这一步就是实现风格转换的原理实现。主要这一步算法的主要流程如下:

算法总流程图

(1)构建拉普拉斯金字塔。对两幅图像:input、Example分别进行多尺度分解,说的简单一点就是构造拉普拉斯金子塔。这个算法如果了解金字塔图像融合算法的人,应该是挺熟悉的。当然拉普拉斯金字塔和高斯金字塔有点区别,高斯金字塔包含采样,在图像融合领域里的一大经典算法,这个扯得有点离题了。具体多尺度分解方法如下:

以高斯卷积核的卷积半径第0层为2,根据2的n次方依次递增。也就是第0,1,2……的卷积半径依次为2,4,8,……进行高斯卷积,这就是所谓的多尺度,因为卷积核σ不同,所以人们又把它称之为多尺度。

根据如下公式进行构建拉普拉斯金子塔:

a.金子塔0~n-1层。金子塔从底层开始(L=0)计算,每一层的计算方法如下:

b.金字塔最后一层(L=n)

最后一层,我们又称之为残差层。

说白了就是,用不同的卷积半径对一张图片分别进行卷积,然后进行相邻层之间相减,这样得到的多张图片就称之为金字塔了。如上面的公式说是,I代表输入图片,G(2)就是卷积σ为2的高斯核,然后上面的运算符就是卷积的意思了。这样把金字塔的每一层相加在一起,你可以发现刚好等于原图像I,而这个过程就称之为金字塔重建。而金字塔融合的原理,就是对金字塔的每一层进行融合处理,然后进行重建。而本篇paper实现光照风格转换的原理,就是对金子塔的每一层进行相关处理,然后再把处理后的每一层相加在一起,就可以得到重建结果。

记住,这一步对Input、Example都要构建金字塔。金字塔的层数,paper默认选择了6层,还有最后一层的残差层。

2、计算金字塔每层的能量图。

这个能量的计算,是根据上面计算得到的金字塔进行计算的。

能量计算公式如下:

对于Input image 根据上面的式子,就可以计算出每一层的能量图了。上面的公式,说的简单一点,就是对金子塔的每一层进行平方,然后在进行高斯卷积。得到的图像,有称之为能量图

对于Example image来说,我们也需要计算出其每一层的能量图(后面变形的时候,在对能量图进行变形)。

也就是先计算能量图,然后对能量图进行W运算,W指的是变形操作。

3、风格转换

对Input image金子塔的每一层(除了最后一层残差层之外),进行风格转换,每一层的计算公式如下:

其中ε为较小的数,取,文章介绍,如果直接使用5b的计算结果,代入公司5a中,会出现一些相关的问题。因此在计算完5b之后,还需进一步的处理:

其中:

最后一层残差层,直接为Example的残差层图像。

看上面的算法流程,需要结合算法总流程图。上面的卷积核大小,并不一定要是2^n,卷积半径大小,对效果影响挺大的,这个paper有讲到,当卷积半径太小的时候,会出现转换过度。如果卷积半径太大了,又会出现转换不足的现象,可以看一下下面的半径对结果的影响图:

当然paper为了使得过度更好,还用了mask,需要用到grab cut算法。不过那都是效果提升阶段了。其实算法上面的这一步骤,就可以看见比较粗糙的风格转换结果了,因此我就讲到这里。

三、算法实现

我这里只贴一下我自己写的paper的主要创新点部分,也就是上面的步骤2(融合阶段),因为只要实现了这一步,就可以看效果了。OK,融合阶段算法首先是构造拉普拉斯金子塔

<span style="font-size:18px;">//文献的公式1  创建拉普拉斯金字塔
void CStyleTransfer::CreateLaplacianStack(LAPSACK&lapsack,cv::Mat Img,int n,float first_sigma,int rsize)
{lapsack.ori_img=Img;//颜色空间转换,文献中提到才lab空间进行处理,效果会比较好,因此需要先转换成lab空间cv::Mat tempimg;//=Img;cv::cvtColor(Img,tempimg,CV_RGB2Lab);//卷积vector<cv::Mat>L(n);vector<cv::Mat>LG(n);for (int i=0;i<n;i++){float sigma=pow(first_sigma,i+1);cv::Mat lgtemp;cv::GaussianBlur(tempimg,lgtemp,cv::Size(sigma+1,sigma+1),sigma);lgtemp.convertTo(LG[i],CV_32FC3);}//构建金字塔for (int i=0;i<n;i++){if (i==0){cv::Mat convertfloat;tempimg.convertTo(convertfloat,CV_32FC3);L[i]=convertfloat-LG[i];}else if(i>0&&i<n){L[i]=LG[i-1]-LG[i];}   }lapsack.laplacian_stack=L;lapsack.residual=LG[LG.size()-1];//Reconstruction(lapsack.laplacian_stack,lapsack.residual);//计算能量图for (int i=0;i<lapsack.laplacian_stack.size();i++){cv::Mat sqrI;cv::Vec3f img00=lapsack.laplacian_stack[i].at<cv::Vec3f>(100,100);cv::multiply(lapsack.laplacian_stack[i],lapsack.laplacian_stack[i],sqrI);cv::Vec3f img001=sqrI.at<cv::Vec3f>(100,100);float sigma=pow(first_sigma,i+1);cv::Mat dst;cv::GaussianBlur(sqrI,dst,cv::Size(sigma+1,sigma+1),sigma);cv::Vec3f img0011=dst.at<cv::Vec3f>(100,100);lapsack.pow_map.push_back(dst);}/*for (int i=0;i<lapsack.laplacian_stack.size();i++){string str;std::stringstream stream;stream<< i; //将int输入流stream >> str; //从stream中抽取前面插入的int值/ *cvNamedWindow(str.c_str());cv::Mat convert;lapsack.laplacian_stack[i].convertTo(convert,CV_8UC3);cv::cvtColor(convert,convert,CV_Lab2RGB);imshow(str,convert);* /}*/}</span>

上面的金子塔的高斯模糊半径,需要根据自己需要进行调整,如果你希望转换的粒度大一些,那么就把模糊半径选择的小一些。

金子塔重建部分:

<span style="font-size:18px;">//金字塔重建
cv::Mat CStyleTransfer::Reconstruction(vector<cv::Mat>LaplacianStack,cv::Mat residual)
{cv::Mat result=residual;//=LaplacianStack[LaplacianStack.size()-1].clone();//最后一层for (int i=0;i<LaplacianStack.size();i++){result=result+LaplacianStack[i];}cv::Mat r;result.convertTo(r,CV_8UC3);cv::cvtColor(r,r,CV_Lab2RGB);
//  cv::normalize(result,r, 0, 1.,cv::NORM_MINMAX);imshow("reconstrution",r);return result;
}</span>

然后计算Gain的部分的代码如下:

<span style="font-size:18px;">//计算Gain
cv::Mat CStyleTransfer::RobustGain(cv::Mat input_pow,cv::Mat example_pow,int layer)
{int heigth=input_pow.rows;int width=input_pow.cols;cv::Mat outimg(heigth,width,CV_32FC3);float thetah=4;float thetal=0.25;float beta=3;for (int i=0;i<heigth;i++){for (int j=0;j<width;j++){float s=0.01*0.01;cv::Vec3f pow_example=example_pow.at<cv::Vec3f>(i,j);cv::Vec3f pow_input=input_pow.at<cv::Vec3f>(i,j)+cv::Vec3f(s,s,s);cv::Vec3f gain;//(1,1,1);cv::divide(pow_example,pow_input,gain);cv::sqrt(gain,gain);cv::Vec3f mingain=cv::Vec3f(min(gain[0],thetah),min(gain[1],thetah),min(gain[2],thetah));cv::Vec3f maxgain=cv::Vec3f(max(mingain[0],thetal),max(mingain[1],thetal),max(mingain[2],thetal));outimg.at<cv::Vec3f>(i,j)=maxgain;}}float sigma=3*pow(2.f,layer+1);cv::Mat robustgain;cv::GaussianBlur(outimg,robustgain,cv::Size(sigma+1,sigma+1),sigma);return robustgain;
</span>

这一部分得到的robustgain,模糊半径选得越大,那么过度越自然,不会出现过度不连续的现象。
效果测试:我这边贴一下,最粗糙的版本,也就是不经过对齐,不经过mask等处理,仅仅使用了paper的创新点部分的算法,进行验证测试,因为没有经过对齐,所以我选择了两张脸型,还有位置看起来比较对齐的图片作为测试图片,如下:

这个图片可以看到转换上基本可以了,然而你可以看到在结果图片的最下方,过渡有点不自然,这个时候,就是因为选择的robustgain这一步的模糊半径选择太小了。在看另外一张结果图片:

这个就是我第一次得到的结果,刚开始一直以为自己代码写错了,想了n久。从图片上看,它实现了部分的转换,但是转换又不够彻底,皮肤的颜色还是有点黄。这是因为我在计算能量图的时候,根据作者的公式进行写,连卷积的半径也是根据公式来,最后经过把计算能量图的:

<span style="font-size:18px;">  //计算能量图for (int i=0;i<lapsack.laplacian_stack.size();i++){cv::Mat sqrI;cv::Vec3f img00=lapsack.laplacian_stack[i].at<cv::Vec3f>(100,100);cv::multiply(lapsack.laplacian_stack[i],lapsack.laplacian_stack[i],sqrI);cv::Vec3f img001=sqrI.at<cv::Vec3f>(100,100);float sigma=pow(first_sigma,i+1);cv::Mat dst;cv::GaussianBlur(sqrI,dst,cv::Size(sigma+1,sigma+1),sigma);cv::Vec3f img0011=dst.at<cv::Vec3f>(100,100);lapsack.pow_map.push_back(dst);}</span>

中的高斯模糊的半径扩大了一倍,才得到最后的结果。于是去查看了作者写的matlab源码,果然,作者源码中的卷积半径也是比较大的,paper作者也并不是根据文献所写的一模一样的卷积半径进行实现,这个paper也提到了,卷积半径对于结果的影响非常大,然而papaer也没有给出比较合理的卷积半径的计算方法,估计是经过调参。具体paper相关的测试效果还有相关的源码在paper作者的主页上可以看到,这里就不罗嗦了。

**********************作者:hjimce   时间:2015.5.6  联系QQ:1393852684   地址:http://blog.csdn.net/hjimce 原创文章,转载请保留本行信息********************

 参考文献:

1、《Style Transfer for Headshot Portraits》

图像处理(九)人物肖像风格转换-Siggraph 2014相关推荐

  1. 吴恩达深度学习笔记13-Course4-Week4【人脸识别和神经风格转换】

    人脸识别(Face Recognition) 一.人脸识别简介(What is face recognition?) 人脸验证:输入一张带人名/ID的人脸图片,验证是不是那个人.一对一问题. 人脸识别 ...

  2. 深度学习笔记 第四门课 卷积神经网络 第四周 特殊应用:人脸识别和神经风格转换...

    本文是吴恩达老师的深度学习课程[1]笔记部分. 作者:黄海广[2] 主要编写人员:黄海广.林兴木(第四所有底稿,第五课第一二周,第三周前三节).祝彦森:(第三课所有底稿).贺志尧(第五课第三周底稿). ...

  3. 4.4)深度卷积网络:人脸识别和神经风格转换

    目录 1)What is face recognition? 2)One-shot learning 3)Siamese network 4)Triplet Loss(重点) 5)Face Verif ...

  4. 《Pytorch - 神经风格转换》

    上一篇我们学习了神经风格转换的详细内容,现在我们找了个网上的例子,一起运行分析下,具体实现过程是如何操作的. 一:代码细节步骤解析 第一步:获取当前可用的设备信息,CPU还是GPU # 获得当前的设备 ...

  5. 计算机视觉系列-图像风格转换

    计算机视觉系列-图像风格转换 目录 图像风格转换论文 论文链接 图像风格转换网络 论文复现代码 运行结果 图像风格转换论文 以不同的方式呈现图像的语义内容样式是图像处理中的一个难点.以前方法的限制因素 ...

  6. 深度学习项目-风格转换

    深度学习项目-风格转换 神经风格转换 1.导入包 2.加载VGG模型 3.搭建神经风格算法 4. 风格权值 5.解决优化问题 神经风格转换 神经风格转换(Neural Style Transfer,N ...

  7. 调用百度API实现图像风格转换

    目录 1.作者介绍 2.基本概念 2.1 人工智能云服务与百度智能云 2.2 图像风格转换 3.调用百度API实现图像风格转换 3.1 配置百度智能云平台 3.2 环境配置 3.3 完整代码实现 3. ...

  8. 图像合成与风格转换实战

    图像合成与风格转换实战 神经式转移 Neural Style Transfer 如果使用社交分享应用程序或者碰巧是个业余摄影师,对过滤器很熟悉.滤镜可以改变照片的颜色样式,使背景更清晰或人的脸更白.然 ...

  9. PyTorch进行神经风格转换/迁移(Neural-Transfer:图像风格迁移)

    前言 文章目录 前言 1.介绍 2. 基本原理 3 准备工作 4 加载素材 1.介绍 本教程主要讲解如何实现由Leon A. Gatys,Alexander S. Ecker和Matthias Bet ...

最新文章

  1. ViewPager+Fragment实现TabHost(可复用的类)
  2. python packages limited ram_python import自己创建的框架下的子模块—pychram和cmd正确执行脚本的两种方法...
  3. boost::fusion::as_nview用法的测试程序
  4. Pollar Rho算法
  5. 电脑延迟关机命令Shutdown详解
  6. identifier __ldg is undefined
  7. sql 如何设置行级锁_SQL Server中的行级安全性简介
  8. 山特服务器硬盘480g,【02311VHS N480SSDW2SPA 480GB SATA SSD 华为服务器固态硬盘】价格_厂家 - 中国供应商...
  9. Basic Sensor Calibration (1) -- 加速计传感器校准
  10. SQL点滴25—T-SQL面试语句,练练手
  11. hdu 4475 Downward paths (找规律)
  12. rm删除命令源码分析
  13. python富翁与穷人_穷人和富人就差1%的运气——python模拟社会财富分配游戏
  14. 校验两个时间段是否重合
  15. C++编程练习3--给出年、月、日,计算该日是该年的第几天
  16. 单源最短路径dijkstra算法
  17. 零跑汽车迎难而上,坚持全域自研战略指引
  18. 【数据分析】数据分析方法论
  19. 手机上怎样压缩照片?分享一下双端简单的操作
  20. 基于vue的学生租房及自习室预约管理系统

热门文章

  1. 三校生计算机应用基础模块,三校生高考《计算机应用基础》电子教案.doc
  2. 《小马哥讲Spring核心编程思想》-第一章学习笔记(1)
  3. 永磁同步电机三相等效电路图_三相永磁同步电动机的控制电路的制作方法
  4. 硬件和软件对虚拟化的支持
  5. 智慧景区场馆(票务)系统解决方案
  6. ARM裸机的知识总结(4) ------- 利用GPIO控制LED
  7. 自动回复mysql数据库设计_微信自动回复数据库设计思路【微擎】
  8. 搜索专题小结:迭代加深搜索
  9. Docker GUI调研
  10. Docker从无到有