一、前言
对于风格转换,2014年siggraph发表了一篇比较不错的论文:《Style Transfer for Headshot Portraits》。本文档涉及很多算法。可以说,如果要重写这篇论文的代码,我已经写到最后了,相当复杂。甚至论文的作者也是通过零碎的代码做到的。因为这篇文章涉及十多个论文算法。我主要讲解这个文档的大致流程。如果你打算完全理解这篇文档,那么你必须非常熟悉图像融合、剪切、筛选、图像变形等基本算法,因为本文是通过将这些基本算法组合在一起来实字塔的图像样式转换,因此主要创新是使用图像融合算法来实字塔图像融合算法的人,应该很熟悉当然,拉普拉斯金字塔和高斯金字塔有些不同,高斯金字塔包含采样,图像融合领域的经典算法,这个有点跑题了。具体的多尺度分解方法如下:
高斯卷积核的第0层卷积半径设置为2,按照2的n次方依次递增,即第0、1、2...的卷积半径分别为依次2,4,8,...高斯卷积,这就是所谓的多尺度,因为卷积核σ不同,所以人们又叫它为多尺度。
根据以下公式构建拉普拉斯的金塔:
a。金塔0~n-1层。金塔从底层开始计算(L=0),每层计算方法如下:
B.金字塔的最后一层(L=n)是:
最后一层也称为残差层。
说白了就是对一张不同卷积半径的图片进行卷积,然后相邻层之间相减,这样得到的多张图片就叫做金字塔。如上式所说,I代表输入图像,G(2)是卷积σ为2的高斯核,然后上面算子的意思是卷积。这样,金字塔的每一层加在一起,就可以发字塔重建。金字塔融合的原理是将金字塔的每一层合并然后重建。本文实塔的每一层进行相关处理,然后将处理后的层相加得到重建结果。
记住,这一步必须为 Input 和 Example 构建一个金字塔。金字塔的层数,论文默认选择6层,最后一层的残差层。
2、计算金字塔每一层的能量图。
此能量计算基于上面计算的金字塔。
能量计算公式如下:
对于Inputimage,根据上面的公式,可以计算出每一层的能量图。上面的公式,简单的说就是把金塔的每一层都平方,然后进行高斯卷积。得到的图像称为能量图
对于Exampleimage,我们还需要计算每一层的能量图(后面变形的时候,我们会变形能量图)。
即先计算能量图,然后对能量图进行W运算。 W指的是变形操作。
3、风格转换
对Inputimage Gold Tower的每一层(最后一个残差层除外)进行风格转换,每一层的计算公式如下:

其中ε是较小的数,取
文章介绍,如果直接使用5b的计算结果代入公司5a,会出塔:
//文献的公式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);
//卷积
vectorL(n);
vectorLG(n);
for (int i=0;i0&&i(100,100);
cv::multiply(lapsack.laplacian_stack[i],lapsack.laplacian_stack[i],sqrI);
cv::Vec3f img001=sqrI.at(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(100,100);
lapsack.pow_map.push_back(dst);
}
/*
for (int i=0;i> 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);* /
}*/
} 上面金塔的高斯模糊半径需要根据自己的需要进行调整。如果您想要更大的转换粒度,请选择更小的模糊半径。
黄金塔的重建部分:
//金字塔重建
cv::Mat CStyleTransfer::Reconstruction(vectorLaplacianStack,cv::Mat residual)
{
cv::Mat result=residual;//=LaplacianStack[LaplacianStack.size()-1].clone();//最后一层
for (int i=0;i 那么计算Gain的代码如下:
//计算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(i,j);
cv::Vec3f pow_input=input_pow.at(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(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;
对于这部分得到的robustgain,模糊半径越大,过渡越自然,不会有过度的不连续性。
效果测试:我会在这里发布。最粗糙的版本,也就是没有对齐,没有蒙版等,只使用论文创新部分算法进行验证测试,因为没有对齐,所以我选择了两个人脸形状和位置看起来像的图片与测试图片相对对齐,如下:

在这张图中,可以看到转换基本OK,但是可以看到在结果图的底部,过渡有点不自然。这时候是因为选择的robustgain step对于模糊半径来说太小了。再看一张结果图:

这是我第一次得到的结果。一开始总觉得是自己的代码有问题,想了很久。从图片上看,已经实现了部分转换,但是转换的不够彻底,肤色还是有点偏黄。这是因为我在计算能量图的时候,是按照作者的公式写的,连卷积的半径都是根据这个公式计算的,最后算出能量图:
//计算能量图
for (int i=0;i(100,100);
cv::multiply(lapsack.laplacian_stack[i],lapsack.laplacian_stack[i],sqrI);
cv::Vec3f img001=sqrI.at(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(100,100);
lapsack.pow_map.push_back(dst);
} 高斯模糊的半径
加倍得到最终结果。于是查了一下作者写的matlab源码。当然,作者源代码中的卷积半径也相对较大。论文作者并没有按照文献实现精确的卷积半径。这篇论文也提到了卷积半径对结果的影响很大,但是论文也没有给出卷积半径的合理计算方法,估计是调整了参数。具体的与纸张相关的测试结果和相关的源代码可以在论文作者的主页上看到,因此在这里我不会赘述。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shumachanpin/article-379264-1.html
要让自己的生活不一样才好