paddleGAN是百度飞桨(PaddlePaddle)的一个图像生成开发套件,它集成了多种先进的算法和预训练模型,包括风格迁移、超分辨率、动漫画生成、图片上色、人脸属性编辑、妆容迁移等算法。
AnimeGANv2 照片动漫化
以下是paddleGAN在gitee上对AnimeGANv2的介绍与模型的训练和测试(docs/zh_CN/tutorials/animegan.md · PaddlePaddle/PaddleGAN - Gitee.com)
1.1 原理介绍
AnimeGAN基于2018年CVPR论文CartoonGAN基础上对其进行了一些改进,主要消除了过度风格化以及颜色伪影区域的问题。对于具体原理可以参见作者知乎文章。AnimeGANv2是作者在AnimeGAN的基础上添加了total variation loss的新模型。
1.2 如何使用
1.2.1 快速体验
安装PaddleGAN之后运行如下代码即生成风格化后的图像output_dir/anime.png,其中PATH_OF_IMAGE为你需要转换的图像路径。
from ppgan.apps import AnimeGANPredictor
predictor = AnimeGANPredictor()
predictor.run(PATH_OF_IMAGE)
或者在终端中运行如下命令,也可获得相同结果:
python applications/tools/animeganv2.py --input_image ${PATH_OF_IMAGE}
1.2.2 数据准备
我们下载作者提供的训练数据,训练数据可以从这里下载。 下载后解压到data目录下:
wget https://github.com/TachibanaYoshino/AnimeGAN/releases/download/dataset-1/dataset.zip
cd PaddleGAN
unzip YOUR_DATASET_DIR/dataset.zip -d data/animedataset
解压完成后数据分布如下所示:
animedataset
├── Hayao
│ ├── smooth
│ └── style
├── Paprika
│ ├── smooth
│ └── style
├── Shinkai
│ ├── smooth
│ └── style
├── SummerWar
│ ├── smooth
│ └── style
├── test
│ ├── HR_photo
│ ├── label_map
│ ├── real
│ ├── test_photo
│ └── test_photo256
├── train_photo
└── val
1.2.3 训练
示例以训练Hayao风格的数据为例。
为了保证模型具备生成原图的效果,需要预热模型:
python tools/main.py --config-file configs/animeganv2_pretrain.yaml
预热模型完成后,训练风格迁移模型: 注意: 必须先修改在configs/animeganv2.yaml中的pretrain_ckpt参数,确保指向正确的 预热模型权重路径 设置batch size=4,learning rate=0.0002,在一个 GTX2060S GPU上训练30个epoch即可获得较好的效果,其他超参数请参考configs/animeganv2.yaml。
python tools/main.py --config-file configs/animeganv2.yaml
改变目标图像的风格 修改configs/animeganv2.yaml中的style参数即可改变风格(目前可选择Hayao,Paprika,Shinkai,SummerWar)。如果您想使用自己的数据集,可以在配置文件中修改数据集为您自己的数据集。
注意: 修改目标风格后,必须计算目标风格数据集的像素均值,并修改configs/animeganv2.yaml中的transform_anime->Add->value参数。
如下例子展示了如何获得Hayao风格图像的像素均值:
python tools/animegan_picmean.py --dataset data/animedataset/Hayao/style
image_num: 1792
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1792/1792 [00:04<00:00, 444.95it/s]
RGB mean diff
[-4.4346957 -8.665916 13.100612 ]
1.2.3 测试
测试模型:
python tools/main.py --config-file configs/animeganv2.yaml --evaluate-only --load ${PATH_OF_WEIGHT}
1.3 结果展示
c#封装
以上例子都是python的我们就用c#来封装一下,使用他已经转好的onnx模型。下面是模型所在的地址信息
c#效果图
以下是最终的效果展示
c#代码
下面是核心代码拿来就可以直接使用
public void SetPicture()
{
if (pictureBox1.Image == null)
{
MessageBox.Show("请先选择图片");
return;
}
Bitmap bitmap = new Bitmap(pictureBox1.Image);
Mat srcResize = bitmap.ToMat();
Size size;
Tensor<float> inputTensors = ProcessImage(srcResize.ToImage<Bgr, float>(), out size, true);
var inputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("generator_input:0", inputTensors)
};
SessionOptions opdeb = new SessionOptions();
opdeb.GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_EXTENDED;
opdeb.InterOpNumThreads = 1;
opdeb.IntraOpNumThreads = 1;
using (InferenceSession debNets = new InferenceSession($@"{AppDomain.CurrentDomain.BaseDirectory}Shinkai_53.onnx"))
{
var sss = debNets.InputMetadata;
IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results = debNets.Run(inputs);
{
var resultsArray = results.ToArray();
var predData = resultsArray[0].AsTensor<float>();
var image = TensorToImage(predData, size);
pictureBox2.Image = image.ToBitmap();
srcResize.Dispose();
}
}
}
public static Tensor<float> ProcessImage(Image<Bgr, float> img, out Size size, bool x32 = true)
{
int h = img.Height;
int w = img.Width;
if (x32)
{
Func<int, int> to_32s = (int x) =>
{
return 256 > x ? 256 : x - x % 32;
};
img = img.Resize(to_32s(w), to_32s(h), Inter.Linear);
}
size = img.Size;
Image<Bgr, float> processedImg = img / 127.5 - 1.0;
var bitmap = processedImg.ToBitmap();
var tensor = new DenseTensor<float>(new[] { 1, bitmap.Height, bitmap.Width, 3 });
var array = new float[1, bitmap.Height, bitmap.Width, 3];
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
tensor[0, y, x, 0] = processedImg.Data[y, x, 2];
tensor[0, y, x, 1] = processedImg.Data[y, x, 1];
tensor[0, y, x, 2] = processedImg.Data[y, x, 0];
}
}
return tensor;
}
public static Image<Bgr, float> TensorToImage(Tensor<float> tensor, Size size)
{
int height = size.Height;
int width = size.Width;
Image<Bgr, float> img = new Image<Bgr, float>(width, height);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
float b = Math.Min(Math.Max((tensor[0, y, x, 0] + 1f) / 2f * 255f, 0f), 255f);
float g = Math.Min(Math.Max((tensor[0, y, x, 1] + 1f) / 2f * 255f, 0f), 255f);
float r = Math.Min(Math.Max((tensor[0, y, x, 2] + 1f) / 2f * 255f, 0f), 255f);
img.Data[y, x, 2] = b;
img.Data[y, x, 1] = g;
img.Data[y, x, 0] = r;
}
}
return img;
}