本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
beginning
之前在十二大深度神经网络中给盆友们简单介绍了GAN网络大家族。其中,CycleGAN算是一个好玩又有趣的存在,它也是很多短视频和美图APP滤镜背后的算法。作为一个图像生成人工智能算法,它可太强大了,能实现图像翻译(黑白照转换为彩色照)、风格迁移(一个城市转换成另一个城市的风格)、动漫图像生成、跨域目标迁移(猫转换为狗)、语音转换(文本转换成逼真的语音)……废话不多说啦,如果你也对CycleGAN感兴趣,想一睹它的风采,让我们一起愉快的学习叭???
- CycleGAN的基础知识,包括网络结构、算法思想、训练过程等
- 调用自带的预训练CycleGAN模型实现野马转斑马、夏天转冬天
- 在自己的数据集上训练自己的CycleGAN模型,将手机拍摄的风景照,转为梵高、莫奈等画作风格
1.CycleGAN介绍
CycleGAN是一种用于图像转换的深度学习模型,它能够在不同领域之间实现图像的风格转换,如将马的图像转换为斑马的图像。CycleGAN基于生成对抗网络(GAN),其核心思想是通过两个互逆的映射来实现图像的转换,并通过循环一致性损失来保持转换的一致性。
-
如上图,生成器将域A中的图像转换到域B中,将域B中的图像转换到域A中。每个域都有一个对应的判别器网络,用于判别域A中的图像,用于判别域B中的图像;
-
生成器网络通常采用U-Net结构,这是一种编码-解码结构,具有跳跃连接。编码器部分负责将输入图像编码为低维特征表示,解码器部分负责将特征表示解码为目标域的图像。通过跳跃连接,可以将较低层的信息传递给解码器,帮助提高细节保留和转换质量;
-
判别器网络可以是基于Patch的判别器,它不仅评估整个图像的真实性,还会评估图像中的局部区域。这有助于生成更真实且细节丰富的转换结果。
1)对抗性损失函数:生成器网络和判别器网络之间的对抗性损失函数如下:
- 生成器损失:希望生成的图像能够骗过判别器,因此它的目标是最小化对生成图像的判别结果;
- 判别器损失:的目标是正确地区分真实图像和生成图像,因此它的目标是最小化真实图像和生成图像之间的差异
类似地,可以定义和之间的对抗性损失函数。
2)循环一致性损失函数:为了保持转换的一致性,CycleGAN还引入了循环一致性损失,定义如下:
- 转换域A到域B再转换回域A的循环一致性损失:最小化原始域A图像与经过转换后再转换回域A的图像之间的差异;
- 转换域B到域A再转换回域B的循环一致性损失:最小化原始域B图像与经过转换后再转换回域B的图像之间的差异。
其中,理解循环一致性损失对于学习CycleGAN至关重要,那就让我们再深刻理解一哈:对于域A中的图像a和经过转换后再转换回域A的图像a’,应该满足。同样,对于域B中的图像b和经过转换后再转换回域B的图像b’,应该满足。通过最小化这两个循环一致性损失,可以保证转换的一致性。
CycleGAN的包括以下步骤:
- 初始化生成器网络和以及判别器网络和的参数;
- 使用真实样本对生成器网络进行训练。优化生成器网络的目标是生成真实域B中的图像,并骗过判别器;
- 使用真实样本对判别器网络进行训练。判别器的目标是准确地区分真实域B图像和生成的域B图像;
- 类似地,进行域A的训练,包括对和的训练;
- 使用循环一致性损失来保持转换的一致性,最小化原始域图像与经过相应转换后再转换回来的图像之间的差异;
- 交替进行以上步骤,直到生成器网络和判别器网络收敛。
CycleGAN的训练通常需要大量的数据和迭代次数,才能生成高质量、逼真的图像转换结果。
盆友们懂了基本原理之后,让我们开始动手实现一个属于自己的CycleGAN模型叭???
2.CycleGAN照片转梵高莫奈油画
2.1配置MMGeneration环境
MMGeneration是开源人工智能算法体系OpenMMLab的图像生成模型工具箱,它内置了一些常用经典前沿的生成对抗网络GAN,包括非条件GAN、条件GAN等等。OpenMMLab整个的算法体系的好处就在于他们的算法研究员已经帮你复现过这些算法了,你可以直接用训练好的模型文件进行预测,也可以拿来训练自己的数据集,真真是帮科研狗的大忙啦???
这么好用,赶紧放个MMGeneration的链接:github.com/open-mmlab/…
下面就跟着一步步安装配置:
# 安装 Pytorch
!pip3 install install torch==1.10.1+cu113 torchvision==0.11.2+cu113 torchaudio==0.10.1+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html
# 安装 mmcv -full
!pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.10.0/index.html
# 安装其它工具包
!pip install ipywidgets tqdm imageio-ffmpeg ninja matplotlib numpy opencv-python prettytable -i https://pypi.tuna.tsinghua.edu.cn/
# 删掉原有的mmgeneration文件夹(如有)
!rm -rf mmgeneration
# 从github上下载最新的 mmgeneration 源代码
!git clone https://github.com/open-mmlab/mmgeneration.git
# 进入 mmgeneration 主目录
import os
os.chdir('mmgeneration')
!pip install -r requirements.txt
!pip install -v -e .
接着创建一些文件夹:
# 创建 outputs 文件夹,用于存放生成结果
os.mkdir('outputs')
# 创建 data 文件夹,用于存放图像数据集
os.mkdir('data')
# 创建 checkpoints 文件夹,用于存放模型权重文件
os.mkdir('checkpoints')
# 创建 work_dirs 文件夹,用于存放训练结果及趣味 demo 输出结果
os.mkdir('work_dirs')
创建好之后,下载一些图像素材到data目录用于测试(照片自己随意下载即可):
# 创建 memory 文件夹,
!mkdir data/memory
## 素材图片,来源:pexel
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220322-mmgeneration/images/horse.jpeg -O data/horse.jpeg
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220322-mmgeneration/images/zebras.jpeg -O data/zebras.jpeg
最后检查是否安装成功:
import torch, torchvision
import mmcv
from mmgen.apis import init_model, sample_unconditional_model
print('mmgen载入成功!')
如果代码没有中断报错,输出 mmgen载入成功!,则说明 MMGeneration 安装成功!
# 指定 config配置文件路径
config_file = 'configs/styleganv2/stylegan2_c2_lsun-church_256_b4x8_800k.py'
# 指定预训练模型权重文件路径
checkpoint_file = 'https://download.openmmlab.com/mmgen/stylegan2/official_weights/stylegan2-church-config-f-official_20210327_172657-1d42b7d1.pth'
# 初始化generatvie
model = init_model(config_file, checkpoint_file, device='cuda:0')
# 生成图像测试
fake_imgs = sample_unconditional_model(model, 4)
fake_imgs.shape
如果代码没有中断报错,模型权重下载完成,且输出torch.Size([4, 3, 256, 256])
,则说明 MMGeneration 运行成功,可以开始后续的代码实战啦✌✌✌
2.2调用预训练CycleGAN模型
MMGeneration提供了很多CycleGAN的预训练模型,包括建筑外立面图转成设计语义图、冬天转夏天、斑马转野马等(但是没有我们今天要实现的照片转莫奈油画的模型,所以我们一会儿要自己训练???)。MMGeneration调用预训练模型有两种方式,一种是命令行方式,一种是Python API方式调用
- :
!python demo/translation_demo.py -h
比如用命令行实现夏天转冬天:
!python demo/translation_demo.py \
configs/cyclegan/cyclegan_lsgan_resnet_in_summer2winter_b1x1_250k.py \
https://download.openmmlab.com/mmgen/cyclegan/refactor/cyclegan_lsgan_resnet_in_1x1_246200_summer2winter_convert-bgr_20210902_165932-fcf08dc1.pth \
data/campus.jpg \
--target-domain winter \
--save-path outputs/F1/F1_summer2winter.jpg \
--device cuda:0
show_img_from_path('data/campus.jpg')
show_img_from_path('outputs/F1/F1_summer2winter.jpg')
上述命令指定一个CycleGAN的config配置文件,再指定一个cyclegan的checkpoint模型权重文件,输入一张照片campus,指定要把它转成冬天target-domain winter,再指定输出图像的保存位置,运行就会得到:
大家看运行后得到的图片,会发现一个问题:用命令行方式得到的图片是一个256×256的正方形,这和原图输入的大小不一致,所以并不推荐大家用这种命令行的方式调用,改用下面的Python API方式调用。
def gen_vis_fake_img(input_path, output_path='outputs/F1/', target_domain='winter', figsize=15):
# 读入输入图像,获取高宽尺寸
input_img = cv2.imread(input_path)
# 生成另外一个图像域的图像,注意 target_domain 要设置正确
fake_imgs = sample_img2img_model(model, input_path, target_domain=target_domain)
# 获取生成图像尺寸
img_size = fake_imgs.shape[2]
# 分别抽取RGB三通道图像
RGB = np.zeros((img_size, img_size, 3))
RGB[:,:,0] = fake_imgs[0][2]
RGB[:,:,1] = fake_imgs[0][1]
RGB[:,:,2] = fake_imgs[0][0]
# 将生成图转为输入图像大小
RGB = cv2.resize(RGB, dsize=(input_img.shape[1],input_img.shape[0]))
# 像素值归一化为0-255的uint8自然图像
RGB = 255 * (RGB - RGB.min()) / (RGB.max()-RGB.min())
# 像素值转为整数
RGB = RGB.astype('uint8')
# 导出生成的图像文件
cv2.imwrite(output_path+target_domain+'_'+input_path.split('/')[-1], cv2.cvtColor(RGB, cv2.COLOR_BGR2RGB))
plt.figure(figsize=(figsize, figsize))
# 展示原始输入图像
plt.subplot(1,2,1)
plt.title('input')
input_RGB = cv2.cvtColor(input_img, cv2.COLOR_BGR2RGB)
plt.xticks([])
plt.yticks([])
plt.imshow(input_RGB)
# 展示生成图
plt.subplot(1,2,2)
plt.title(target_domain)
plt.xticks([])
plt.yticks([])
plt.imshow(RGB)
plt.show()
上述代码中,写了一个函数,这个函数的核心是sample_img2img_model()用来实现图像的转换,再把图像resize重新缩放到原图尺寸的大小,就可以使得生成的图片和原图的大小是一样的。例如用Python API方式实现夏天转冬天:
# 夏天转冬天
# 指定config文件路径
config_file = 'configs/cyclegan/cyclegan_lsgan_resnet_in_summer2winter_b1x1_250k.py'
# 指定预训练模型权重文件路径
checkpoint_file = 'https://download.openmmlab.com/mmgen/cyclegan/refactor/cyclegan_lsgan_resnet_in_1x1_246200_summer2winter_convert-bgr_20210902_165932-fcf08dc1.pth'
# 输出图像宽高像素尺寸
img_size = 256
model = init_model(config_file, checkpoint_file, device='cuda:0')
gen_vis_fake_img(input_path='data/gouqi_island.jpg', output_path='outputs/F1/', target_domain='winter', figsize=15)
上述代码先导入模型,指定config配置文件和checkpoint权重文件,init初始化该模型,最后直接调用上面写好的函数gen_vis_fake_img()就可以了。运行之后就可以得到:
大家看,用这种调用方式是不是就可以和原图大小保持一致了腻,转换效果还是不错滴。CycleGAN嘛它本身是一个cycle(循环),既可以夏天转冬天,也可以冬天转夏天,只需要改变一下target_domain就可以啦???
同理,也可以实现野马转斑马,只需要修改指定的config配置文件和checkpoint权重文件,将其换成斑马的文件,同时target_domain=zebra就能轻松实现啦(什么?!还有盆友问配置权重文件在哪里指定下载,在上面提到的MMGeneration工具箱里呀:github.com/open-mmlab/…
同时还有建筑立面设计语义图 和 立面图照片互转:
上述这些模型都是MMGeneration的研究员已经帮我们训练复现好的,我们可以直接拿来用,做预测、评估等。除了可以对单张图片进行预测,还可以对视频进行预测。其实视频预测的原理非常简单,就是把视频拆成一帧一帧的画面,对每一帧画面逐一的进行我们刚刚的这一套方法,然后把得到的每一帧的转换之后的画面重新拼起来,汇成一个新视频。
那如何训练我们自己的CycleGAN呢?这就是接下来我们要讲的。
2.3训练自己的CycleGAN模型
在进行训练之前,我们肯定是先要有大量相对应的数据集,其实在CycleGAN的官网提供了很多数据集的下载链接,比如街景、野马转斑马、苹果转橘子、照片转油画等等,链接放在这里啦:常用CycleGAN 数据集⛳⛳⛳嫌下载速度慢的可以直接运行下面的代码块:
# 下载 vangogh2photo(梵高转照片) 图像数据集压缩包,还可下载莫奈、塞尚、浮世绘数据集
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220322-mmgeneration/dataset/vangogh2photo.zip -O data/vangogh2pho
# 解压至 data/vangogh2photo 目录
!unzip -o data/vangogh2photo.zip -d data/vangogh2photo/
解压之后,在数据集文件目录./data/vangogh2photo/vangogh2photo(两个喔)下会得到四个文件夹:testA、testB、trainA、trainB。其中,A为梵高的画作,B为风景照片,test为测试集,train为训练集。所以现在的CycleGAN就是要把A的图像域和B的图像域进行互相的迁移。
首先下载config配置文件:(其实配置文件就包含了训练模型算法需要知道的一切,比如模型结构)
# vangogh2photo
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220322-mmgeneration/cyclegan_lsgan_resnet_in_facades_b1x1_80k_v
下载好之后就会有一个py文件,里面包含了模型用的什么损失函数、数据集、数据增强、优化器等等。如果你要训练自己的数据集,那么就需要修改domain_a和domain_b、数据集的目录dataroot、exp_name这三处,其他的就照搬这个模板。
接下来就是漫长的训练过程了,大概要运行8小时左右(睡一觉就好啦)。训练过程中,在 work_dirs/experiments/experiments_name目录下,training_samples保存了训练过程中达到不同迭代次数时的训练效果,ckpt/experiments_name保存了训练过程中达到不同迭代次数时的模型权重文件。
# vangogh2photo
!bash tools/dist_train.sh configs/cyclegan/cyclegan_lsgan_resnet_in_facades_b1x1_80k_vangogh2photo.py 1 --work-dir work_dirs/exper
在这里我们选择用Python API的方式调用,代码如下:
# 照片转梵高
# 指定config文件路径
config_file = 'configs/cyclegan/cyclegan_lsgan_resnet_in_facades_b1x1_80k_vangogh2photo.py'
# 指定预训练模型权重文件路径
# checkpoint_file = 'work_dirs/experiments/cyclegan_vangogh2photo/ckpt/cyclegan_vangogh2photo/latest.pth'
checkpoint_file = 'https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220322-mmgeneration/checkpoints/cyclegan_vangogh2photo_iter_80000.pth'
# 输出图像宽高像素尺寸
img_size = 256
model = init_model(config_file, checkpoint_file, device='cuda:0')
# 指定输入图像
input_path = 'data/memory/memory_san.jpg'
# 生成梵高油画,注意 target_domain 要设置正确
fake_imgs = sample_img2img_model(model, input_path, target_domain='vangogh')
# 分别抽取RGB三通道图像,归一化为0-255的uint8自然图像
RGB = np.zeros((img_size, img_size, 3))
RGB[:,:,0] = fake_imgs[0][2]
RGB[:,:,1] = fake_imgs[0][1]
RGB[:,:,2] = fake_imgs[0][0]
# 将生成图转为输入图像大小
RGB = cv2.resize(RGB, dsize=(input_img.shape[1],input_img.shape[0]))
# 像素值归一化
RGB = 255 * (RGB - RGB.min()) / (RGB.max()-RGB.min())
# 像素值转为整数
RGB = RGB.astype('uint8')
# 导出生成的图像文件
cv2.imwrite('outputs/F4_photo2vangogh.jpg', cv2.cvtColor(RGB, cv2.COLOR_BGR2RGB))
然后展示原始输入图像和生成图,会得到以下 图片转梵高油画 的结果:
上述这个过程也可以封装成一个函数(类似上文讲过的gen_vis_fake_img()函数),然后我直接调用这个函数,输入图像的路径,指定模型,指定target_domain=vangogh,就可以实现相同的效果啦。另外插一句,从转化的图中可以发现梵高喜欢用橙色,不管是向日葵、稻田还是太阳,都用大量橙色来渲染,所以CycleGAN捕捉到这个特点;同样,梵高画云彩的时候喜欢把轮廓勾勒得特别清晰,这一点CycleGAN也捕捉到了。(所以说盆友们,机器是骗不了人滴???)
同样,也可以把梵高画作转换成图片,下面是通过Python API的方式调用的:
gen_vis_fake_img('data/vangogh2photo/vangogh2photo/testA/00056.jpg', model, target_domain='photo', figsize=10)
从上面这两张图可以看到,转换后的照片和我们肉眼看到的大千世界不太一样呢,总感觉梵高画作转照片 的效果为什么不如 照片转梵高画作 的效果好腻?CycleGAN 不应该是对称的吗????注意哈,这里的”好“是非常主观的一个概念,很难去评价,大家可以思考一下为什么会造成这种差异腻?有人回答说,人类对不同图像域之间转换的敏感程度是不一样的,照片转成梵高的画作它的特征是非常明显的,所以我们人一看就是梵高的风格;而梵高的风格转成照片,我们人类每天都在看真实的自然世界,所以我们无法对转换结果进行非常敏感的判别~~~
除了梵高画作,还可以对莫奈、塞尚、浮世绘画作进行转化,步骤和上文梵高的一样,这里就不过多赘述啦,放个转化结果:(内容来源b站up主同济子豪兄)
3.CycleGAN小结
当我们谈论图像转换时,CycleGAN就像是一位神奇的魔法师,他可以将马变成斑马,苹果变成橙子,甚至可以将蒲公英的图像变成夏日海滩的风景!这个有趣而强大的模型可以让我们看到不同领域之间的惊人转变。
CycleGAN作为魔法师,他有两个法宝:生成器和判别器。首先,我们来看看生成器。它就像一个有着无尽创造力的画家,他可以用一抹魔术般的笔触,将一张马的照片转变成一个逼真的斑马图像。这是如何实现的呢?生成器通过学习如何从输入图像中提取特征,并将其转换为另一个领域的图像。它非常聪明,可以捕捉不同领域之间微妙的差异,使得转换后的图像看起来非常自然。
接下来,我们有了判别器,他就像是一位神奇的侦探,总能分辨出真假。判别器的任务是区分生成器产生的假图像和真实的图像。但是,生成器可不容易被识破!它会不断改进自己的技能,使得生成的图像与真实图像越来越难以区分。判别器每天都在面对一个艰巨的任务,因为他必须找出生成器的破绽。
但是,这个故事还没有结束!CycleGAN引入了循环一致性的概念,就像是一个魔法的时间循环。这意味着我们可以通过两个方向进行转换。如果我们将马转换成斑马,那么我们也可以通过斑马转换回马!这实际上就像是画家在画一张画,然后擦掉再重画一次,而我们最终将看到的是完美无瑕的原始图像。
CycleGAN的应用:图像风格转换、跨域图像转换、动漫图像生成、风格迁移、视频风格迁移、目标转换、图像修复以及语音转换等
ending
看到这里相信盆友们都对CycleGAN有了一个全面深入的了解啦???很开心能把学到的知识以文章的形式分享给大家。如果你也觉得我的分享对你有所帮助,please一键三连嗷!!!下期见