From 25a9268c3f83f8187dd2856d1e0dac1e510905d6 Mon Sep 17 00:00:00 2001 From: jm12138 <2286040843@qq.com> Date: Mon, 16 Nov 2020 11:42:40 +0800 Subject: [PATCH] Add the AnimeGAN modules --- .../animegan_v1_hayao_60/README.md | 127 +++++++++++++++++ .../animegan_v1_hayao_60/model.py | 67 +++++++++ .../animegan_v1_hayao_60/module.py | 72 ++++++++++ .../animegan_v1_hayao_60/processor.py | 133 ++++++++++++++++++ .../animegan_v2_hayao_64/README.md | 127 +++++++++++++++++ .../animegan_v2_hayao_64/model.py | 67 +++++++++ .../animegan_v2_hayao_64/module.py | 72 ++++++++++ .../animegan_v2_hayao_64/processor.py | 133 ++++++++++++++++++ .../animegan_v2_hayao_99/README.md | 127 +++++++++++++++++ .../animegan_v2_hayao_99/model.py | 67 +++++++++ .../animegan_v2_hayao_99/module.py | 72 ++++++++++ .../animegan_v2_hayao_99/processor.py | 133 ++++++++++++++++++ .../animegan_v2_paprika_54/README.md | 127 +++++++++++++++++ .../animegan_v2_paprika_54/model.py | 67 +++++++++ .../animegan_v2_paprika_54/module.py | 72 ++++++++++ .../animegan_v2_paprika_54/processor.py | 133 ++++++++++++++++++ .../animegan_v2_paprika_74/README.md | 127 +++++++++++++++++ .../animegan_v2_paprika_74/model.py | 67 +++++++++ .../animegan_v2_paprika_74/module.py | 72 ++++++++++ .../animegan_v2_paprika_74/processor.py | 133 ++++++++++++++++++ .../animegan_v2_paprika_97/README.md | 127 +++++++++++++++++ .../animegan_v2_paprika_97/model.py | 67 +++++++++ .../animegan_v2_paprika_97/module.py | 72 ++++++++++ .../animegan_v2_paprika_97/processor.py | 133 ++++++++++++++++++ .../animegan_v2_paprika_98/README.md | 127 +++++++++++++++++ .../animegan_v2_paprika_98/model.py | 67 +++++++++ .../animegan_v2_paprika_98/module.py | 72 ++++++++++ .../animegan_v2_paprika_98/processor.py | 133 ++++++++++++++++++ .../animegan_v2_shinkai_33/README.md | 127 +++++++++++++++++ .../animegan_v2_shinkai_33/model.py | 67 +++++++++ .../animegan_v2_shinkai_33/module.py | 72 ++++++++++ .../animegan_v2_shinkai_33/processor.py | 133 ++++++++++++++++++ .../animegan_v2_shinkai_53/README.md | 127 +++++++++++++++++ .../animegan_v2_shinkai_53/model.py | 67 +++++++++ .../animegan_v2_shinkai_53/module.py | 72 ++++++++++ .../animegan_v2_shinkai_53/processor.py | 133 ++++++++++++++++++ 36 files changed, 3591 insertions(+) create mode 100644 hub_module/modules/image/style_transfer/animegan_v1_hayao_60/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v1_hayao_60/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v1_hayao_60/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v1_hayao_60/processor.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_hayao_64/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_hayao_64/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_hayao_64/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_hayao_64/processor.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_hayao_99/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_hayao_99/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_hayao_99/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_hayao_99/processor.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_54/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_54/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_54/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_54/processor.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_74/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_74/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_74/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_74/processor.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_97/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_97/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_97/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_97/processor.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_98/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_98/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_98/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_paprika_98/processor.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/processor.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/README.md create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/model.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/module.py create mode 100644 hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/processor.py diff --git a/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/README.md b/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/README.md new file mode 100644 index 00000000..587f4ba9 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V1 图像风格转换模型 + +模型可将输入的图像转换成Hayao风格 + +模型权重转换自AnimeGAN V1官方开源项目 + +模型所使用的权重为Hayao-60.ckpt + +模型详情请参考[AnimeGAN V1 开源项目](https://github.com/TachibanaYoshino/AnimeGAN) + +## 模型安装 + +```shell +$hub install animegan_v1_hayao_60 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/10175bb964e94ce18608a84b0ab6ebfe154b523df42f44a3a851b2d91dd17a63) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v1_hayao_60', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v1_hayao_60 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v1_hayao_60" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGAN + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/model.py b/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/module.py b/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/module.py new file mode 100644 index 00000000..8b77d894 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v1_hayao_60.model import Model +from animegan_v1_hayao_60.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v1_hayao_60", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v1_hayao_60", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V1_Hayao_60(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v1_hayao_60") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/processor.py b/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v1_hayao_60/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/README.md b/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/README.md new file mode 100644 index 00000000..20c8ef9d --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V2 图像风格转换模型 + +模型可将输入的图像转换成Hayao风格 + +模型权重转换自AnimeGAN V2官方开源项目 + +模型所使用的权重为Hayao-64.ckpt + +模型详情请参考[AnimeGAN V2 开源项目](https://github.com/TachibanaYoshino/AnimeGANv2) + +## 模型安装 + +```shell +$hub install animegan_v2_hayao_64 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/49620341f1fe4f00af4d93c22694897a1ae578a235844a1db1bbb4bd37bf750b) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v2_hayao_64', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v2_hayao_64 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v2_hayao_64" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGANv2 + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/model.py b/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/module.py b/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/module.py new file mode 100644 index 00000000..cdb31f67 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v2_hayao_64.model import Model +from animegan_v2_hayao_64.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v2_hayao_64", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v2_hayao_64", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V2_Hayao_64(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v2_hayao_64") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes diff --git a/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/processor.py b/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_hayao_64/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/README.md b/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/README.md new file mode 100644 index 00000000..94b23a6e --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V2 图像风格转换模型 + +模型可将输入的图像转换成Hayao风格 + +模型权重转换自AnimeGAN V2官方开源项目 + +模型所使用的权重为Hayao-99.ckpt + +模型详情请参考[AnimeGAN V2 开源项目](https://github.com/TachibanaYoshino/AnimeGANv2) + +## 模型安装 + +```shell +$hub install animegan_v2_hayao_99 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/16195e03d7e0412d990349587c587a26d9ae9e2ed1ec4fa1b4dc994e948d1f7d) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v2_hayao_99', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v2_hayao_99 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v2_hayao_99" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGANv2 + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/model.py b/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/module.py b/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/module.py new file mode 100644 index 00000000..ebb1640e --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v2_hayao_99.model import Model +from animegan_v2_hayao_99.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v2_hayao_99", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v2_hayao_99", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V2_Hayao_99(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v2_hayao_99") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/processor.py b/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_hayao_99/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/README.md b/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/README.md new file mode 100644 index 00000000..01eabc69 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V2 图像风格转换模型 + +模型可将输入的图像转换成Paprika风格 + +模型权重转换自AnimeGAN V2官方开源项目 + +模型所使用的权重为Paprika-54.ckpt + +模型详情请参考[AnimeGAN V2 开源项目](https://github.com/TachibanaYoshino/AnimeGANv2) + +## 模型安装 + +```shell +$hub install animegan_v2_paprika_54 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/08ee95c94e0b4d4e8b2855a6ed40af5853b40c0047b3421aaa2f7c877fac5130) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v2_paprika_54', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v2_paprika_54 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v2_paprika_54" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGANv2 + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/model.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/module.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/module.py new file mode 100644 index 00000000..4631c281 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v2_paprika_54.model import Model +from animegan_v2_paprika_54.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v2_paprika_54", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v2_paprika_54", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V2_Paprika_54(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v2_paprika_54") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/processor.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_54/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/README.md b/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/README.md new file mode 100644 index 00000000..74f32987 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V2 图像风格转换模型 + +模型可将输入的图像转换成Paprika风格 + +模型权重转换自AnimeGAN V2官方开源项目 + +模型所使用的权重为Paprika-74.ckpt + +模型详情请参考[AnimeGAN V2 开源项目](https://github.com/TachibanaYoshino/AnimeGANv2) + +## 模型安装 + +```shell +$hub install animegan_v2_paprika_74 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/6574669d87b24bab9627c6e33896528b4a0bf5af1cd84ca29655d68719f2d551) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v2_paprika_74', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v2_paprika_74 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v2_paprika_74" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGANv2 + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/model.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/module.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/module.py new file mode 100644 index 00000000..7d54fafa --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v2_paprika_74.model import Model +from animegan_v2_paprika_74.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v2_paprika_74", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v2_paprika_74", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V2_Paprika_74(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v2_paprika_74") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/processor.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_74/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/README.md b/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/README.md new file mode 100644 index 00000000..8baa6762 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V2 图像风格转换模型 + +模型可将输入的图像转换成Paprika风格 + +模型权重转换自AnimeGAN V2官方开源项目 + +模型所使用的权重为Paprika-97.ckpt + +模型详情请参考[AnimeGAN V2 开源项目](https://github.com/TachibanaYoshino/AnimeGANv2) + +## 模型安装 + +```shell +$hub install animegan_v2_paprika_97 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/3b962a18a22e43028cc5530db1c5adb1a42e6aae4bb74b8598ee30ed52b59c8b) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v2_paprika_97', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v2_paprika_97 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v2_paprika_97" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGANv2 + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/model.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/module.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/module.py new file mode 100644 index 00000000..c57d7172 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v2_paprika_97.model import Model +from animegan_v2_paprika_97.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v2_paprika_97", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v2_paprika_97", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V2_Paprika_97(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v2_paprika_97") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/processor.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_97/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/README.md b/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/README.md new file mode 100644 index 00000000..1f650fd6 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V2 图像风格转换模型 + +模型可将输入的图像转换成Paprika风格 + +模型权重转换自AnimeGAN V2官方开源项目 + +模型所使用的权重为Paprika-98.ckpt + +模型详情请参考[AnimeGAN V2 开源项目](https://github.com/TachibanaYoshino/AnimeGANv2) + +## 模型安装 + +```shell +$hub install animegan_v2_paprika_98 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/495436a627ef423ab572536c5f2ba6d0eb99b1ce098947a5ac02af36e7eb85f7) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v2_paprika_98', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v2_paprika_98 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v2_paprika_98" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGANv2 + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/model.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/module.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/module.py new file mode 100644 index 00000000..6daab807 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v2_paprika_98.model import Model +from animegan_v2_paprika_98.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v2_paprika_98", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v2_paprika_98", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V2_Paprika_98(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v2_paprika_98") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes diff --git a/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/processor.py b/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_paprika_98/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/README.md b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/README.md new file mode 100644 index 00000000..19aaff3a --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V2 图像风格转换模型 + +模型可将输入的图像转换成Shinkai风格 + +模型权重转换自AnimeGAN V2官方开源项目 + +模型所使用的权重为Shinkai-33.ckpt + +模型详情请参考[AnimeGAN V2 开源项目](https://github.com/TachibanaYoshino/AnimeGANv2) + +## 模型安装 + +```shell +$hub install animegan_v2_shinkai_33 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/776a84a0d97c452bbbe479592fbb8f5c6fe9c45f3b7e41fd8b7da80bf52ee668) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v2_shinkai_33', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v2_shinkai_33 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v2_shinkai_33" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGANv2 + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/model.py b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/module.py b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/module.py new file mode 100644 index 00000000..cc0612f2 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v2_shinkai_33.model import Model +from animegan_v2_shinkai_33.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v2_shinkai_33", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v2_shinkai_33", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V2_Shinkai_33(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v2_shinkai_33") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes diff --git a/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/processor.py b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_33/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/README.md b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/README.md new file mode 100644 index 00000000..44dfdcf7 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/README.md @@ -0,0 +1,127 @@ +## 模型概述 +AnimeGAN V2 图像风格转换模型 + +模型可将输入的图像转换成Shinkai风格 + +模型权重转换自AnimeGAN V2官方开源项目 + +模型所使用的权重为Shinkai-53.ckpt + +模型详情请参考[AnimeGAN V2 开源项目](https://github.com/TachibanaYoshino/AnimeGANv2) + +## 模型安装 + +```shell +$hub install animegan_v2_shinkai_53 +``` + + +## API 说明 + +```python +def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 +) +``` + +风格转换API,将输入的图片转换为漫画风格。 + +转换效果图如下: + +![输入图像](https://ai-studio-static-online.cdn.bcebos.com/bd002c4bb6a7427daf26988770bb18648b7d8d2bfd6746bfb9a429db4867727f) +![输出图像](https://ai-studio-static-online.cdn.bcebos.com/fa4ba157e73c48658c4c9c6b8b92f5c99231d1d19556472788b1e5dd58d5d6cc) + + +**参数** + +* images (list\[numpy.ndarray\]): 图片数据,ndarray.shape 为 \[H, W, C\],默认为 None; +* paths (list\[str\]): 图片的路径,默认为 None; +* batch\_size (int): batch 的大小,默认设为 1; +* visualization (bool): 是否将识别结果保存为图片文件,默认设为 False; +* output\_dir (str): 图片的保存路径,默认设为 output; +* min\_size (int): 输入图片的短边最小尺寸,默认设为 32; +* max\_size (int): 输入图片的短边最大尺寸,默认设为 1024。 + + +**返回** + +* res (list\[numpy.ndarray\]): 输出图像数据,ndarray.shape 为 \[H, W, C\]。 + + +## 预测代码示例 + +```python +import cv2 +import paddlehub as hub + +# 模型加载 +# use_gpu:是否使用GPU进行预测 +model = hub.Module('animegan_v2_shinkai_53', use_gpu=False) + +# 模型预测 +result = model.style_transfer(images=[cv2.imread('/PATH/TO/IMAGE')]) + +# or +# result = model.style_transfer(paths=['/PATH/TO/IMAGE']) +``` + +## 服务部署 + +PaddleHub Serving可以部署一个在线图像风格转换服务。 + +## 第一步:启动PaddleHub Serving + +运行启动命令: +```shell +$ hub serving start -m animegan_v2_shinkai_53 +``` + +这样就完成了一个图像风格转换的在线服务API的部署,默认端口号为8866。 + +**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。 + +## 第二步:发送预测请求 + +配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果 + +```python +import requests +import json +import cv2 +import base64 + + +def cv2_to_base64(image): + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + + +# 发送HTTP请求 +data = {'images':[cv2_to_base64(cv2.imread("/PATH/TO/IMAGE"))]} +headers = {"Content-type": "application/json"} +url = "http://127.0.0.1:8866/predict/animegan_v2_shinkai_53" +r = requests.post(url=url, headers=headers, data=json.dumps(data)) + +# 打印预测结果 +print(r.json()["results"]) +``` + + +## 模型相关信息 + +### 模型代码 + +https://github.com/TachibanaYoshino/AnimeGANv2 + +### 依赖 + +paddlepaddle >= 1.8.0 + +paddlehub >= 1.8.0 \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/model.py b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/model.py new file mode 100644 index 00000000..a72cf8a3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/model.py @@ -0,0 +1,67 @@ +import os +import numpy as np + +from paddle.fluid.core import AnalysisConfig, create_paddle_predictor + +__all__ = ['Model'] + +class Model(): + # 初始化函数 + def __init__(self, modelpath, use_gpu): + # 加载模型预测器 + self.predictor = self.load_model(modelpath, use_gpu) + + # 获取模型的输入输出 + self.input_names = self.predictor.get_input_names() + self.output_names = self.predictor.get_output_names() + self.input_tensor = self.predictor.get_input_tensor(self.input_names[0]) + self.output_tensor = self.predictor.get_output_tensor(self.output_names[0]) + + # 模型加载函数 + def load_model(self, modelpath, use_gpu): + # 对运行位置进行配置 + if use_gpu: + try: + places = os.environ["CUDA_VISIBLE_DEVICES"] + places = int(places[0]) + except Exception as e: + print('Error: %s. Please set the environment variables "CUDA_VISIBLE_DEVICES".' % e) + use_gpu = False + + # 加载模型参数 + config = AnalysisConfig(modelpath) + + # 设置参数 + if use_gpu: + config.enable_use_gpu(100, places) + else: + config.disable_gpu() + config.enable_mkldnn() + config.disable_glog_info() + config.switch_ir_optim(True) + config.enable_memory_optim() + config.switch_use_feed_fetch_ops(False) + config.switch_specify_input_names(True) + + # 通过参数加载模型预测器 + predictor = create_paddle_predictor(config) + + # 返回预测器 + return predictor + + # 模型预测函数 + def predict(self, input_datas): + outputs = [] + + # 遍历输入数据进行预测 + for input_data in input_datas: + self.input_tensor.copy_from_cpu(input_data) + self.predictor.zero_copy_run() + output = self.output_tensor.copy_to_cpu() + outputs.append(output) + + # 预测结果合并 + outputs = np.concatenate(outputs, 0) + + # 返回预测结果 + return outputs \ No newline at end of file diff --git a/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/module.py b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/module.py new file mode 100644 index 00000000..d733092d --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/module.py @@ -0,0 +1,72 @@ +import os + +from paddlehub import Module +from paddlehub.module.module import moduleinfo, serving + +from animegan_v2_shinkai_53.model import Model +from animegan_v2_shinkai_53.processor import base64_to_cv2, cv2_to_base64, Processor + +@moduleinfo( + name="animegan_v2_shinkai_53", # 模型名称 + type="CV/style_transfer", # 模型类型 + author="jm12138", # 作者名称 + author_email="jm12138@qq.com", # 作者邮箱 + summary="animegan_v2_shinkai_53", # 模型介绍 + version="1.0.0" # 版本号 +) +class Animegan_V2_Shinkai_53(Module): + # 初始化函数 + def _initialize(self, use_gpu=False): + # 设置模型路径 + self.model_path = os.path.join(self.directory, "animegan_v2_shinkai_53") + + # 加载模型 + self.model = Model(self.model_path, use_gpu) + + # 关键点检测函数 + def style_transfer( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + visualization=False, + min_size=32, + max_size=1024 + ): + # 加载数据处理器 + processor = Processor( + images, + paths, + batch_size, + output_dir, + min_size, + max_size + ) + + # 模型预测 + outputs = self.model.predict(processor.input_datas) + + # 结果后处理 + results = processor.postprocess(outputs, visualization) + + # 返回结果 + return results + + # Hub Serving + @serving + def serving_method(self, images, **kwargs): + # 获取输入数据 + images_decode = [base64_to_cv2(image) for image in images] + + # 图片风格转换 + results = self.style_transfer(images_decode, **kwargs) + + # 对输出图片进行编码 + encodes = [] + for result in results: + encode = cv2_to_base64(result) + encodes.append(encode) + + # 返回结果 + return encodes diff --git a/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/processor.py b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/processor.py new file mode 100644 index 00000000..777487d3 --- /dev/null +++ b/hub_module/modules/image/style_transfer/animegan_v2_shinkai_53/processor.py @@ -0,0 +1,133 @@ +import os +import cv2 +import time +import base64 +import numpy as np + +__all__ = ['base64_to_cv2', 'cv2_to_base64', 'Processor'] + +def check_dir(dir_path): + # 目录检查函数 + if not os.path.exists(dir_path): + os.makedirs(dir_path) + elif os.path.isfile(dir_path): + os.remove(dir_path) + os.makedirs(dir_path) + +def base64_to_cv2(b64str): + # base64转cv2函数 + data = base64.b64decode(b64str.encode('utf8')) + data = np.frombuffer(data, np.uint8) + data = cv2.imdecode(data, cv2.IMREAD_COLOR) + return data + +def cv2_to_base64(image): + # cv2转base64函数 + data = cv2.imencode('.jpg', image)[1] + return base64.b64encode(data.tostring()).decode('utf8') + +class Processor(): + # 初始化函数 + def __init__( + self, + images=None, + paths=None, + batch_size=1, + output_dir='output', + min_size=32, + max_size=1024 + ): + # 变量设置 + self.min_size = min_size + self.max_size = max_size + + self.images = images + self.paths = paths + self.batch_size = batch_size + self.output_dir = output_dir + + # 获取原始输入数据 + self.datas = self.load_datas() + + # 对原始输入数据进行预处理 + self.input_datas = self.preprocess() + + # 读取数据函数 + def load_datas(self): + datas = [] + + # 读取数据列表 + if self.paths is not None: + for im_path in self.paths: + assert os.path.isfile(im_path), "The {} isn't a valid file path.".format(im_path) + im = cv2.imread(im_path) + datas.append(im) + + if self.images is not None: + datas = self.images + + # 返回数据列表 + return datas + + # 数据预处理函数 + def preprocess(self): + input_datas = [] + + # 数据预处理 + for i, img in enumerate(self.datas): + # 格式转换 + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # 缩放图片 + h, w = img.shape[:2] + if max(h,w)>self.max_size: + img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if hw else cv2.resize(img, (int(w/h*self.min_size), self.min_size)) + + # 裁剪图片 + h, w = img.shape[:2] + img = img[:h-(h%32), :w-(w%32), :] + + # 归一化 + img = img/127.5 - 1.0 + + # 新建维度 + img = np.expand_dims(img, axis=0).astype('float32') + + # 加入输入数据列表 + input_datas.append(img) + + # 数据按batch_size切分 + input_datas = np.concatenate(input_datas, 0) + split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size + input_datas = np.array_split(input_datas, split_num) + + # 返回预处理完成的数据 + return input_datas + + def postprocess(self, outputs, visualization): + results = [] + + for im_id, output in enumerate(outputs): + # 反归一化 + image = (output.squeeze() + 1.) / 2 * 255 + + # 限幅 + image = np.clip(image, 0, 255).astype(np.uint8) + + # 格式转换 + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 可视化 + if visualization: + # 检查输出目录 + check_dir(self.output_dir) + + # 写入输出图片 + cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image) + + results.append(image) + + # 返回结果 + return results \ No newline at end of file -- GitLab