python实现CylcleGAN人脸转卡通图【文末源码】

CycleGan人脸转为卡通图

引言 近几天一个GitHub项目火遍了朋友圈,那就是卡通头像AI生成小程序。如下图所见:

而这个项目的基本原理是用python搭建的GAN算法模型,进行训练得出。

而所谓的GAN就是指生成对抗网络深度学习模型。网络中有生成器G(generator)和鉴别器(Discriminator)。有两个数据域分别为X,Y。G负责把X域中的数据拿过来拼命地模仿成真实数据并把它们藏在真实数据中,而D就拼命地要把伪造数据和真实数据分开。经过二者的博弈以后,G的伪造技术越来越厉害,D的鉴别技术也越来越厉害。直到D再也分不出数据是真实的还是G生成的数据的时候,这个对抗的过程达到一个动态的平衡。

而CycleGAN本质上是两个镜像对称的GAN,构成了一个环形网络。两个GAN共享两个生成器,并各自带一个判别器,即共有两个判别器和两个生成器。一个单向GAN两个loss,两个即共四个loss。

可以实现无配对的两个图片集的训练是CycleGAN与Pixel2Pixel相比的一个典型优点。但是我们仍然需要通过训练创建这个映射来确保输入图像和生成图像间存在有意义的关联,即输入输出共享一些特征。

简而言之,该模型通过从域DA获取输入图像,该输入图像被传递到第一个生成器GeneratorA→B,其任务是将来自域DA的给定图像转换到目标域DB中的图像。然后这个新生成的图像被传递到另一个生成器GeneratorB→A,其任务是在原始域DA转换回图像,这里可与自动编码器作对比。这个输出图像必须与原始输入图像相似,用来定义非配对数据集中原来不存在的有意义映射。

在本次的项目中就是利用了CycleGAN进行搭建模型。模型训练数据集如下:

一、实验前的准备:

首先我们使用的 python 版本是 3.6.5 所用到的库有 pytorch TensorFlow ,用来训练和加载神经网络常见的框架; face-alignment 用来是用来提取人脸特征的常用库;

dlib 是一个机器学习的开源库,包含了机器学习的很多算法,使用起来很方便,直接包含头文件即可,并且不依赖于其他库(自带图像编解码库源码)。 Dlib 可以帮助您创建很多复杂的机器学习方面的软件来帮助解决实际问题。目前 Dlib 已经被广泛的用在行业和学术领域 , 包括机器人 , 嵌入式设备 , 移动电话和大型高性能计算环境。

二、模型的训练

1、数据集处理和准备:

训练数据包括真实照片和卡通画像,为降低训练复杂度,我们对两类数据进行了如下预处理:

检测人脸及关键点。

根据关键点旋转校正人脸。

将关键点边界框按固定的比例扩张并裁剪出人脸区域。

使用人像分割模型将背景置白。

为了形成匹配效果,需要准备一些卡通人物图片和真实的人脸图片进行训练

2、模型的训练:

模型的训练使用 pythontrain.py --dataset photo2cartoon 进行训练即可。

3、神经网络结构搭建:

整个算法的搭建正如上面可见,需要有生成器和判别器。使用论文提出的一种 Soft-AdaLIN SoftAdaptive Layer-InstanceNormalization )归一化方法,在反规范化时将编码器的均值方差(照片特征)与解码器的均值方差(卡通特征)相融合。

模型结构方面,在 U-GAT-IT 的基础上,在编码器之前和解码器之后各增加了 2 hourglass 模块,渐进地提升模型特征抽象和重建能力。

部分代码如下:

classResnetGenerator(nn.Module):

def__init__(self, ngf=64, img_size=256, light=False):

super(ResnetGenerator, self).__init__()

self.light =light

self.ConvBlock1 = nn.Sequential(nn.ReflectionPad2d(3),

nn.Conv2d(3, ngf, kernel_size=7, stride=1,padding=0, bias=False),

nn.InstanceNorm2d(ngf),

nn.ReLU(True))

self.HourGlass1 = HourGlass(ngf, ngf)

self.HourGlass2 = HourGlass(ngf, ngf)

#Down-Sampling

self.DownBlock1 = nn.Sequential(nn.ReflectionPad2d(1),

nn.Conv2d(ngf, ngf*2, kernel_size=3, stride=2,padding=0, bias=False),

nn.InstanceNorm2d(ngf * 2),

nn.ReLU(True))

self.DownBlock2 = nn.Sequential(nn.ReflectionPad2d(1),

nn.Conv2d(ngf*2, ngf*4, kernel_size=3, stride=2,padding=0, bias=False),

nn.InstanceNorm2d(ngf*4),

nn.ReLU(True))

# EncoderBottleneck

self.EncodeBlock1 = ResnetBlock(ngf*4)

self.EncodeBlock2 = ResnetBlock(ngf*4)

self.EncodeBlock3 = ResnetBlock(ngf*4)

self.EncodeBlock4 = ResnetBlock(ngf*4)

# ClassActivation Map

self.gap_fc= nn.Linear(ngf*4, 1)

self.gmp_fc= nn.Linear(ngf*4, 1)

self.conv1x1= nn.Conv2d(ngf*8, ngf*4, kernel_size=1, stride=1)

self.relu =nn.ReLU(True)

# Gamma,Beta block

ifself.light:

self.FC= nn.Sequential(nn.Linear(ngf*4, ngf*4),

nn.ReLU(True),

nn.Linear(ngf*4, ngf*4),

nn.ReLU(True))

else:

self.FC= nn.Sequential(nn.Linear(img_size//4*img_size//4*ngf*4, ngf*4),

nn.ReLU(True),

nn.Linear(ngf*4, ngf*4),

nn.ReLU(True))

# DecoderBottleneck

self.DecodeBlock1 = ResnetSoftAdaLINBlock(ngf*4)

self.DecodeBlock2 = ResnetSoftAdaLINBlock(ngf*4)

self.DecodeBlock3 = ResnetSoftAdaLINBlock(ngf*4)

self.DecodeBlock4 = ResnetSoftAdaLINBlock(ngf*4)

#Up-Sampling

self.UpBlock1 = nn.Sequential(nn.Upsample(scale_factor=2),

nn.ReflectionPad2d(1),

nn.Conv2d(ngf*4, ngf*2, kernel_size=3, stride=1,padding=0, bias=False),

LIN(ngf*2),

nn.ReLU(True))

self.UpBlock2 = nn.Sequential(nn.Upsample(scale_factor=2),

nn.ReflectionPad2d(1),

nn.Conv2d(ngf*2, ngf, kernel_size=3, stride=1,padding=0, bias=False),

LIN(ngf),

nn.ReLU(True))

self.HourGlass3 = HourGlass(ngf, ngf)

self.HourGlass4 = HourGlass(ngf, ngf, False)

self.ConvBlock2 = nn.Sequential(nn.ReflectionPad2d(3),

nn.Conv2d(3, 3, kernel_size=7, stride=1,padding=0, bias=False),

nn.Tanh())

defforward(self, x):

x =self.ConvBlock1(x)

x =self.HourGlass1(x)

x =self.HourGlass2(x)

x =self.DownBlock1(x)

x =self.DownBlock2(x)

x =self.EncodeBlock1(x)

content_features1 = F.adaptive_avg_pool2d(x, 1).view(x.shape[0], -1)

x =self.EncodeBlock2(x)

content_features2 = F.adaptive_avg_pool2d(x, 1).view(x.shape[0], -1)

x =self.EncodeBlock3(x)

content_features3 = F.adaptive_avg_pool2d(x, 1).view(x.shape[0], -1)

x =self.EncodeBlock4(x)

content_features4 = F.adaptive_avg_pool2d(x, 1).view(x.shape[0], -1)

gap =F.adaptive_avg_pool2d(x, 1)

gap_logit =self.gap_fc(gap.view(x.shape[0], -1))

gap_weight =list(self.gap_fc.parameters())[0]

gap = x *gap_weight.unsqueeze(2).unsqueeze(3)

gmp =F.adaptive_max_pool2d(x, 1)

gmp_logit =self.gmp_fc(gmp.view(x.shape[0], -1))

gmp_weight =list(self.gmp_fc.parameters())[0]

gmp = x *gmp_weight.unsqueeze(2).unsqueeze(3)

cam_logit =torch.cat([gap_logit, gmp_logit], 1)

x =torch.cat([gap, gmp], 1)

x =self.relu(self.conv1x1(x))

heatmap =torch.sum(x, dim=1, keepdim=True)

ifself.light:

x_ =F.adaptive_avg_pool2d(x, 1)

style_features = self.FC(x_.view(x_.shape[0], -1))

else:

style_features = self.FC(x.view(x.shape[0], -1))

x =self.DecodeBlock1(x, content_features4, style_features)

x =self.DecodeBlock2(x, content_features3, style_features)

x =self.DecodeBlock3(x, content_features2, style_features)

x =self.DecodeBlock4(x, content_features1, style_features)

x =self.UpBlock1(x)

x =self.UpBlock2(x)

x =self.HourGlass3(x)

x =self.HourGlass4(x)

out =self.ConvBlock2(x)

return out,cam_logit, heatmap

4、提取人脸特征:

为了提取人脸特征以达到加载到网络中的目的,我们需要正确框出人脸同时计算特征距离,以方便后面训练模型师损失函数的调用。

代码如下:

classFaceFeatures(object):

def__init__(self, weights_path, device):

self.device= device

self.model =MobileFaceNet(512).to(device)

self.model.load_state_dict(torch.load(weights_path))

self.model.eval()

def infer(self,batch_tensor):

# crop face

h, w =batch_tensor.shape[2:]

top = int(h/ 2.1 * (0.8 - 0.33))

bottom =int(h - (h / 2.1 * 0.3))

size =bottom - top

left = int(w/ 2 - size / 2)

right = left+ size

batch_tensor= batch_tensor[:, :, top: bottom, left: right]

batch_tensor= F.interpolate(batch_tensor, size=[112, 112], mode='bilinear',align_corners=True)

features =self.model(batch_tensor)

returnfeatures

defcosine_distance(self, batch_tensor1, batch_tensor2):

feature1 =self.infer(batch_tensor1)

feature2 =self.infer(batch_tensor2)

return 1 -torch.cosine_similarity(feature1, feature2)

三、模型测试

在训练好模型后,我们使用 pythontest.py --photo_path ./images/1.jpg --save_path./images/2.png 测试生成图片。其中 1.jpg 是原始图片,最终会生成 2.jpg 图片。

使用 pythondata_process.py --data_path YourPhotoFolderPath --save_pathYourSaveFolderPath 批量生成

1、调用模型:

调用模型首先要使用 torch 进行加载模型,读取神经网络参数。在对原始图片提取人脸特征的基础上,加载进网络进行生成即可。因为这里我们还需要对生成的数据进行转换成图片,我们这里还需要使用 numpy opencv 进行图片的转化。因为加载如模型和模型生成的必然是数据,而我们需要将生成器产生的数据再转换为图片,就用到了这两个库。

代码如下:

class Photo2Cartoon:

def__init__(self):

self.pre =Preprocess()

self.device= torch.device("cuda:0" if torch.cuda.is_available() else"cpu")

self.net =ResnetGenerator(ngf=32, img_size=256, light=True).to(self.device)

params =torch.load('./models/photo2cartoon_weights.pt',map_location=self.device)

self.net.load_state_dict(params['genA2B'])

definference(self, img):

# facealignment and segmentation

face_rgba =self.pre.process(img)

if face_rgbais None:

print('can not detect face!!!')

returnNone

face_rgba =cv2.resize(face_rgba, (256, 256), interpolation=cv2.INTER_AREA)

face =face_rgba[:, :, :3].copy()

mask =face_rgba[:, :, 3][:, :, np.newaxis].copy() / 255.

face =(face*mask + (1-mask)*255) / 127.5 - 1

face =np.transpose(face[np.newaxis, :, :, :], (0, 3, 1,2)).astype(np.float32)

face =torch.from_numpy(face).to(self.device)

# inference

withtorch.no_grad():

cartoon= self.net(face)[0][0]

#post-process

cartoon =np.transpose(cartoon.cpu().numpy(), (1, 2, 0))

cartoon =(cartoon + 1) * 127.5

cartoon =(cartoon * mask + 255 * (1 - mask)).astype(np.uint8)

cartoon =cv2.cvtColor(cartoon, cv2.COLOR_RGB2BGR)

returncartoon

if __name__ =='__main__':

img =cv2.cvtColor(cv2.imread(args.photo_path), cv2.COLOR_BGR2RGB)

c2p =Photo2Cartoon()

cartoon =c2p.inference(img)

if cartoon isnot None:

cv2.imwrite(args.save_path, cartoon)

到这里,我们整体的程序就搭建完成,下面为我们程序的运行结果:

源码地址:https://gitcode.net/qq_42279468/python-cylclegan.git

《辐射避难所ol》新手注意的几个问题自带福利

前几天在某条刷新闻遇到大量关于《辐射ol》的推广,由于本人之前非常喜欢玩辐射系列游戏.就玩了这个游戏,根据自己的见解写了这篇攻略.

先说福利安卓用户:卡号:qzszyK610W48XGHA17PJ0

qtyzzK6155R29YPTS0STJ

qqdtyK61CUF9E62HT37R1

qtyhhK61WBN1B8SF8PJPD

qqdyyK6LYTX6CYL9T2E0V

17173K6KW0UH21BWX0R4W

qdtqK6HMYSPKYF2NSTSG

qdtqK6KHD9XN6W8XEFG0

含八卡礼包一个号能用一次里面东西挺多我就不一一列举

2苹果用户没卡有首抽号3个先私先得

1 新玩家进入游戏由于任务是跟着你的主控室等级开放、一些活动也是基于主控室,所以优先升级主控室.

2关于资源的重要性个人认为新手阶段是电力大于食物大于瓶盖大于水,到了养老的阶段可以将电力放到最后一位、瓶盖和水往前挪一位,按个人需求(电力升级房间/食物升级居民等级/瓶盖装备升级、购买材料/水升级天赋)

3关于回忆室能造不要耽搁,每天好多瓶盖和可以兑换英雄碎片、研究中心可以放一放造不造看心情(房间重构在主控室,资源房间两个小房间放在一起变成中级三个可以变成高级,重构可以拆分)

4ssr英雄一开始获取难度要高一点可以小氪、不建议大氪.(游戏里每时每刻都有人在哀嚎)第一个10连抽第一天可以获得第二个10连抽在第三天慢的话就四天抽到ssr最好.(祝福不要抽到7天送的)

5游戏最重要的就是奶妈,决定的你推图和爬塔的程度.送的5小强艾丽可以用到李医生的出现或者直接圣父 (单奶量大反应底遇到推图boss很无力、尤其大型副本的时候)

6副本黑商除了海报可以买一下其他的不要碰.

7竞技场每天21点前可以冲刺一下把次数用完可以换ssr碎片不算多慢

8每日任务要清一下每日120旧货币

我玩了一周抽到了一个ssr看样子还是有些非.

让我看看你的呗?

相关问答

能帮我换算一下框架眼镜度数么?隐形眼镜右眼:sph

[回答]病情分析:你好,没有用隐形眼镜来置换成框架眼镜的度数的。意见建议:佩戴框架眼镜需要进行医学验光,度数在右眼375度,左眼400度左右

玩腾讯NBA2KOnline时,电脑画面卡住,但是声音还在继续(正常),...

回复Cyl728电脑用几年了?清理下灰尘吧cpu或显卡过热了有用(0)回复相关问题玩NBA2KOL画面卡是怎么回事,电脑不可能跟不上的,台式机989浏览3回答玩N...

通用临时额度什么意思?-其他理财知识问答-我爱卡

[回答]临时额度的资格是实时动态评估的。临时额度是银行为了解决客户临时消费需要而提供的,一般临时额度在固定额度的10%到50%之间,具体可以获得多少临时...

宏观经济学为什么投资水平提高,收入增长率也将提高?从图中...

[回答]当人均收入与人均资本不变时,经济就处于稳定状态。人均收入与人均资本的稳态值y*和k*就是向新工人提供资本和重置已损耗机器所必须的投资与经济产生...

yy影视频道大全(中国)IOS/手机APP官网下载V3.73.17;

推荐大家使用/liuzhou/CyLkcsi124947“对我们来说,这是一次危机,也是一次大考。经过艰苦努力,目前疫情防控形势积极向好的态势正在拓展。”疫情发生后,以习近...

用菲利普斯模型来解释货币财政政策详细宏观经济学-汇财吧专...

[回答]菲利普斯曲线表明失业与通货膨胀存在一种交替关系的曲线,通货膨胀率高时,失业率低;通货膨胀率低时,失业率高。菲利普斯曲线是用来表示失业与通货膨...

在宏观经济学中,基础货币H等于非银行部门持有的通货加上法定...

[回答]由法定准备率Rd的定义推出k=1/Rd由于超额准备率Re的存在,使得存款的派生过程有漏出,得k=1/(Rd+Re)假如客户将得到的贷款不全部存入银行,而抽出一定...