C++ AnimeGAN实现照片一键动漫化
前言
AnimeGAN是来自武汉大学和湖北工业大学的AI项目,是由神经网络风格迁移加生成对抗网络(GAN)而成,它是基于CartoonGAN的改进,并提出了一个更加轻量级的生成器架构。原理和训练代码转Github,Pytorch版本。官方的有放出三个试玩的模型,有两个模型是针对人脸的,有一个是卡通效果,可以用来试试别的图像的,熟悉python和pytorch的直接git下来就可以本地部署了,就可以试玩了。
先看看人像的效果
我的demo里面的图像第一张是原图,第二第三张是人像模型,第四张是卡通效果。
风景的效果:
我这里用的是C++,环境是Win10,VS2019,OpenCV4.5,用的推理库是ncnn-20210720, 要用到GPU,所以要下VulkanSDK,GPU 是GTX1650。
环境安装
1.安装Vulkan各它的依赖库。
版本:VulkanSDK-1.2.141.2
直接点击安装,把之后验证是否安装成功,运行xxx\VulkanSDK\1.2.141.2\Bin\vkcube.exe,出现下面图像代表安装成功。
glfw
https://www.glfw.org/
把glfw-3.3.2.bin.WIN64复制到VulkanSDK\1.2.141.2\Third-Party
GLM
https://github.com/g-truc/glm/
把GLM复制到VulkanSDK\1.2.141.2\Third-Party
添加系统环境变量
3.下载NCNN,可以直接下载对应自己IDE的relese版本的,免得编译麻烦,下载地址。
4.下载OpenCV, 4.0以上就可以。
C++推理
1.先从 GitHub 下载权重文件。然后按官方给的文档和脚本转成onnx模型。转成onnx之后也可以有用onnxruntime进行推理,我这里又把onnx转ncnn的推理模型,ncnn带有模型转换脚本,直接转就行。
2.把ncnn库,OpenCV,VulkanSDK的lib都加vs的库依赖里,然后加上OpenCV和VulkanSDK的动态库环境变量,就可以开始撸代码了。
3.测试代码:
#include <opencv2/opencv.hpp> #include <ncnn/gpu.h> #include <ncnn/net.h> //模型路径 std::string celeba_model = "models/Celeba.bin"; std::string celeba_param = "models/Celeba.param"; std::string face_v1_model = "models/FacePointV1.bin"; std::string face_v1_param = "models/FacePointV1.param"; std::string face_v2_model = "models/FacePointV2.bin"; std::string face_v2_param = "models/FacePointV2.param"; //读取模型 int readModels(ncnn::Net& ncnn_net, std::string param_path, std::string model_path, bool use_gpu = true); //推理,target_w,target_h为推理尺寸,越往大的改,细节保留就越多,也越吃GPU算力 int animeInference(const ncnn::Net& ncnn_net, const cv::Mat& cv_src, cv::Mat& cv_dst,int target_w = 512, int target_h = 512) { cv::Mat cv_backup = cv_src.clone(); const int w = cv_src.cols; const int h = cv_src.rows; const float mean_vals[3] = { 127.5f, 127.5f, 127.5f }; const float norm_vals[3] = { 1 / 127.5f, 1 / 127.5f, 1 / 127.5f }; ncnn::Mat in = ncnn::Mat::from_pixels_resize(cv_backup.data, ncnn::Mat::PIXEL_BGR2RGB, w, h, target_w, target_h); in.substract_mean_normalize(mean_vals, norm_vals); ncnn::Mat out; ncnn::Extractor ex = ncnn_net.create_extractor(); ex.input("input", in); ex.extract("out", out); cv::Mat result(out.h, out.w, CV_32FC3); for (int i = 0; i < out.c; i++) { float* out_data = out.channel(i); for (int h = 0; h < out.h; h++) { for (int w = 0; w < out.w; w++) { result.at<cv::Vec3f>(h, w)[2 - i] = out_data[h * out.h + w]; } } } cv::Mat result8U(out.h, out.w, CV_8UC3); result.convertTo(result8U, CV_8UC3, 255.0/2, 255.0/2); result8U.copyTo(cv_dst); cv::resize(cv_dst, cv_dst, cv_src.size()); return 0; } //调用,三种风格 int styletransferResult(const cv::Mat& cv_src, std::vector<cv::Mat>& cv_dsts, ncnn::Net& face_v1_net, ncnn::Net& face_v2_net, ncnn::Net& celeba_net) { animeInference(face_v1_net, cv_src, cv_dsts.at(0)); animeInference(face_v2_net, cv_src, cv_dsts.at(1)); animeInference(celeba_net, cv_src, cv_dsts.at(2)); } int main(int argc, char** argv) { //图像路径 std::string path = "images"; std::vector<std::string> filenames; cv::glob(path, filenames, false); ncnn::Net face_v1_net, face_v2_net, celeba_net; readModels(face_v1_net, face_v1_param, face_v1_model); readModels(face_v2_net, face_v1_param, face_v1_model); readModels(celeba_net, celeba_param, celeba_model); for(auto v : filenames) { cv::Mat cv_src = cv::imread(v, 1); std::vector<cv::Mat> cv_dsts(3); double start = static_cast<double>(cv::getTickCount()); styletransferResult(cv_src, cv_dsts, face_v1_net, face_v2_net, celeba_net); double time = ((double)cv::getTickCount() - start) / cv::getTickFrequency(); std::cout << "time:" << time << "(s)" << std::endl; cv_src.push_back(cv_dsts.at(0)); cv_dsts.at(1).push_back(cv_dsts.at(2)); cv::Mat des; des.create(cv_src.rows, cv_src.cols*2, cv_src.type()); cv::Mat r1 = des(cv::Rect(0, 0, cv_src.cols, cv_src.rows)); cv_src.copyTo(r1); cv::Mat r2 = des(cv::Rect(cv_src.cols, 0, cv_src.cols,cv_src.rows)); cv_dsts.at(1).copyTo(r2); imshow("style", des); cv::imwrite(std::to_string(j)+".jpg", des); // cv::waitKey(); } return 0; } int readModels(ncnn::Net& ncnn_net,std::string param_path,std::string model_path, bool use_gpu) { bool has_gpu = false; #if NCNN_VULKAN ncnn::create_gpu_instance(); has_gpu = ncnn::get_gpu_count() > 0; #endif bool to_use_gpu = has_gpu && use_gpu; ncnn_net.opt.use_vulkan_compute = to_use_gpu; int rp = ncnn_net.load_param(param_path.c_str()); int rb = ncnn_net.load_model(model_path.c_str()); if (rp < 0 || rb < 0) { return 1; } return 0; }
运行结果
5.跑起来很费GPU,如果有好一些的GPU,推理尺寸可以开大一点,细节的保留会更完整些,如果是前两种风格,图像尽量大于512*512,人像人脸特征明显,因为算法是基本于人脸关键点做的。
6.源码和可执行文件以上传到csdn,下载下来,把自己想试的图像放在images目录下,运行.exe文件,就可以在当前的得到最终合并的效果图像。源码下载地址,可执行文件地址
以上就是C++ AnimeGAN实现照片一键动漫化的详细内容,更多关于C++ AnimeGAN照片动漫化的资料请关注脚本之家其它相关文章!
最新评论