【OpenVINO+paddle】一切皆可二次元-CPU部署飞桨AnimeGAN实现所有照片二次元化

【OpenVINO+paddle】一切皆可二次元-CPU部署飞桨GAN实现所有照片二次元化

在这篇文章中我将小喵咪、小姐姐、钢铁侠图片二次元化,事实上你可以尝试任何一张你感兴趣的图像。在这里你将会使用到PP-AnimeGAN模型,将这个模型导出为onnx模型,然后转换为OpenVINO的IR格式,并在CPU上实现任意的图片的二次元化。

请添加图片描述

免责声明:上面所有图片都是采用百度公开的图片,如果有侵权图片请联系我删除。

想要了解更过飞桨的这个算法,可以点击这个链接 PaddleGAN’s AnimeGAN
我已经在aistudio挂载了我所有的源代码和代码结果,大家可以直接点开下面的链接进行尝试。

https://aistudio.baidu.com/aistudio/projectdetail/3591452?contributionType=1

引用会使用到的库

这里我们的环境是使用jupyter notebook,如果小伙伴还没有安装到openvino的库可以看下我这篇文稿,直接一键配置环境。
https://aistudio.baidu.com/aistudio/projectdetail/3563713?contributionType=1

import sys
import time
import os
from pathlib import Path

import cv2
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import HTML, display
from openvino.inference_engine import IECore

# PaddlePaddle需要c++编译器。下面的代码主要针对window,因为linux大多数都有C++
#安装c++
try:
    import paddle
    from paddle.static import InputSpec
    from ppgan.apps import AnimeGANPredictor
except NameError:
    if sys.platform == "win32":
        install_message = (
            "To use this notebook, please install the free Microsoft "
            "Visual C++ redistributable from <a href='https://aka.ms/vs/16/release/vc_redist.x64.exe'>"
            "https://aka.ms/vs/16/release/vc_redist.x64.exe</a>"
        )
    else:
        install_message = (
            "To use this notebook, please install a C++ compiler. On macOS, "
            "`xcode-select --install` installs many developer tools, including C++. On Linux, "
            "install gcc with your distribution's package manager."
        )
    display(
        HTML(
            f"""<div class="alert alert-danger" ><i>
    <b>Error: </b>PaddlePaddle requires installation of C++. {install_message}"""
        )
    )
    raise

定义模型、文件、文件夹的名称

MODEL_DIR = "model"
MODEL_NAME = "paddlegan_anime"

os.makedirs(MODEL_DIR, exist_ok=True)

# 创建好转换的模型的文件名。
model_path = Path(f"{MODEL_DIR}/{MODEL_NAME}")
ir_path = model_path.with_suffix(".xml")
onnx_path = model_path.with_suffix(".onnx")

定义一个处理图片大小的函数

def resize_to_max_width(image, max_width):
    """
    调整' image '为' max_width ',保持图像的长宽比。
    """
    if image.shape[1] > max_width:
        hw_ratio = image.shape[0] / image.shape[1]
        new_height = int(max_width * hw_ratio)
        image = cv2.resize(image, (max_width, new_height))
    return image

关于PaddleGAN

PaddleGAN这个文档解释了飞桨模型的基本细节,如果是官方提供的静态模型,您可以使用’ .run() ‘运行模型。如果您想要更加深入地了解可以直接在Jupyter notebook 上代码后面打’ ??'快捷方式显示文档字符串和函数的源代码。

下面我们将定义好AnimeGANPredictor()并从PaddlePaddle下载权重。下载可能需要一段时间,下载一次就行反复下载也没啥。

predictor = AnimeGANPredictor()
predictor.run??

这里介绍一下AnimeGANPredictor.run()的使用方法:

  1. 使用OpenCV加载图像并将其转换为RGB
  2. 变换图像
    3.通过生成器模型传播转换后的图像,并对结果进行后处理,会返回一个范围为[0,255]的数组
  3. 将(C,H,W)的结果转置为(H,W,C)形状
  4. 将结果图像的大小调整为原始图像大小
  5. 可选地调整结果图像的亮度
  6. 保存图像

我们可以手动执行这些步骤并确认结果。如果要加快推理时间,需要在通过网络传播大型图像之前调整它们的大小。下一个单元格中的推理步骤仍然需要一些时间来执行。

PADDLEGAN_INFERENCE = True
OUTPUT_DIR = "output"

os.makedirs(OUTPUT_DIR, exist_ok=True)
#第1步。加载图像并转换为RGB
image_path = Path("data/gangtiexia.jpeg")

image = cv2.cvtColor(cv2.imread(str(image_path), flags=cv2.IMREAD_COLOR), cv2.COLOR_BGR2RGB)

##对大的图像进行推理需要很长时间。将大小调整为最大宽度600
image = resize_to_max_width(image, 600)

#第2步。变换图像
transformed_image = predictor.transform(image)
input_tensor = paddle.to_tensor(transformed_image[None, ::])

if PADDLEGAN_INFERENCE:
    #第三步。做推理。
    predictor.generator.eval()
    with paddle.no_grad():
        result = predictor.generator(input_tensor)

    #第四步。按照与下面相同的步骤将推理结果转换为图像
    # padlegan的predictor.run()函数
    result_image_pg = (result * 0.5 + 0.5)[0].numpy() * 255
    result_image_pg = result_image_pg.transpose((1, 2, 0))

    #第五步。调整结果图像的大小
    result_image_pg = cv2.resize(result_image_pg, image.shape[:2][::-1])

    #步骤6。调整brightness
    result_image_pg = predictor.adjust_brightness(result_image_pg, image)

    #步骤7。保存结果图像
    anime_image_path_pg = Path(f"{OUTPUT_DIR}/{image_path.stem}_anime_pg").with_suffix(".jpg")
    if cv2.imwrite(str(anime_image_path_pg), result_image_pg[:, :, (2, 1, 0)]):
        print(f"The anime image was saved to {anime_image_path_pg}")

显示GAN模型的推理结果

if PADDLEGAN_INFERENCE:
    fig, ax = plt.subplots(1, 2, figsize=(25, 15))
    ax[0].imshow(image)
    ax[1].imshow(result_image_pg)
else:
    print("PADDLEGAN_INFERENCE is not enabled. Set PADDLEGAN_INFERENCE = True in the previous cell and run that cell to show inference results.")

你可以看下结果
请添加图片描述

将模型转换到ONNX和IR

我们将PaddleGAN模型转换为OpenVINO IR,首先使用“paddle2onnx”将PaddleGAN转换为ONNX,然后使用OpenVINO的模型优化器将ONNX模型转换为IR。

转换为ONNX

导出到ONNX需要指定输入形状与PaddlePaddle的’ InputSpec ‘和调用’ PaddlePaddle . ONNX .export ‘。我们检查转换后的图像的输入形状,并使用它作为ONNX模型的输入形状。输出到ONNX应该不会花很长时间。如果导出成功,结果输出会显示’ ONNX模型保存在paddlegan_animation . ONNX '。

target_height, target_width = transformed_image.shape[1:]
target_height, target_width
predictor.generator.eval()
x_spec = InputSpec([None, 3, target_height, target_width], "float32", "x")
paddle.onnx.export(predictor.generator, str(model_path), input_spec=[x_spec], opset_version=11)

转换为IR模型

OpenVINO IR格式允许在模型文件中存储预处理规范化。这样就不再需要手动对输入图像进行归一化。我们我们可以??来检查一下’ .run() '方法使用的转换:

predictor.__init__??
t = predictor.transform.transforms[0]
t.params

这里一共有三种转换:resize、transpose和normalize,其中normalize使用平均值和刻度[127.5,127.5,127.5]。

使用(256,256)作为大小参数调用ResizeToScale类。进一步的分析表明这是事实
要调整到的最小大小。对象中指定的图像大小
ResizeToScale参数,宽度和高度为32倍。

现在我们知道了均值和标准差,以及模型输入的形状,我们可以调用model Optimizer并使用这些值将模型转换为IR。我们使用FP16精度,并将日志级别设置为CRITICAL,以忽略与本演示无关的警告。有关模型优化器参数的信息,请参阅模型优化器文档

转换模型到IR与Model Optimizer

onnx_path = model_path.with_suffix(".onnx")
print("Exporting ONNX model to IR... This may take a few minutes.")
!mo --input_model $onnx_path --output_dir $MODEL_DIR --input_shape [1,3,$target_height,$target_width] --model_name $MODEL_NAME --data_type "FP16" --mean_values="[127.5,127.5,127.5]" --scale_values="[127.5,127.5,127.5]" --log_level "CRITICAL"

请添加图片描述

显示IR和PaddleGAN模型的推理结果

创建Postprocessing函数

predictor.adjust_brightness??
predictor.calc_avg_brightness??

平均亮度是通过标准公式计算出来的,请参阅https://www.w3.org/TR/AERT/#color-contrast 通过计算源图像与目标图像的亮度差来调整目标图像的亮度。然后将图像转换为8位图像。

我们将这些函数复制到下一个单元格,并使用它们对IR模型进行推理

def calc_avg_brightness(img):
    R = img[..., 0].mean()
    G = img[..., 1].mean()
    B = img[..., 2].mean()

    brightness = 0.299 * R + 0.587 * G + 0.114 * B
    return brightness, B, G, R


def adjust_brightness(dst, src):
    brightness1, B1, G1, R1 = AnimeGANPredictor.calc_avg_brightness(src)
    brightness2, B2, G2, R2 = AnimeGANPredictor.calc_avg_brightness(dst)
    brightness_difference = brightness1 / brightness2
    dstf = dst * brightness_difference
    dstf = np.clip(dstf, 0, 255)
    dstf = np.uint8(dstf)
    return dstf

对IR模型进行推理

​加载IR模型并进行推理,执行与PaddleGAN模型相同的步骤。请参阅OpenVINO推理引擎API了解更多关于IR模型推理的信息。
​IR模型是用基于输入图像计算的输入形状生成的。如果对具有不同输入形状的图像进行推理,结果可能与PaddleGAN结果不同。

# 加载和准备IR模型
ie = IECore()
net = ie.read_network(ir_path)
exec_net = ie.load_network(net, "CPU")
input_key = next(iter(net.input_info.keys()))
output_key = next(iter(net.outputs.keys()))

# Step 1.加载图片如何转化成RGB
image_path = Path("data/cat.jpeg")
image = cv2.cvtColor(cv2.imread(str(image_path), flags=cv2.IMREAD_COLOR), cv2.COLOR_BGR2RGB)

# Step 2. 重新定义图片尺寸
resized_image = cv2.resize(image, (target_width, target_height))
input_image = resized_image.transpose(2, 0, 1)[None, :, :, :]

# Step 3.进行推理
result_ir = exec_net.infer({input_key: input_image})

# Step 4. 按照与下面相同的步骤将推理结果转换为图像
# PaddleGAN's predictor.run() function
result_image_ir = (result_ir[output_key] * 0.5 + 0.5)[0] * 255
result_image_ir = result_image_ir.transpose((1, 2, 0))

# Step 5. 重新定义图像
result_image_ir = cv2.resize(result_image_ir, image.shape[:2][::-1])

# Step 6. 调整brightness
result_image_ir = adjust_brightness(result_image_ir, image)

# Step 7. 保存图片
anime_fn_ir = Path(f"{OUTPUT_DIR}/{image_path.stem}_anime_ir").with_suffix(".jpg")
if cv2.imwrite(str(anime_fn_ir), result_image_ir[:, :, (2, 1, 0)]):
    print(f"The anime image was saved to {anime_fn_ir}")

显示推理结果

fig, ax = plt.subplots(1, 2, figsize=(25, 15))
ax[0].imshow(image)
ax[1].imshow(result_image_ir)
ax[0].set_title("Image")
ax[1].set_title("OpenVINO IR result");

请添加图片描述

性能比较

测量对图像进行推理所需的时间。这给出了性能指标。这不是一个完美的衡量标准。由于PaddleGAN模型需要相当多的内存来进行推理,所以我们只测量一个图像上的推理。要进行更精确的基准测试

NUM_IMAGES = 1
start = time.perf_counter()
for _ in range(NUM_IMAGES):
    exec_net.infer(inputs={input_key: input_image})
end = time.perf_counter()
time_ir = end - start
print(
    f"IR model in Inference Engine/CPU: {time_ir/NUM_IMAGES:.3f} "
    f"seconds per image, FPS: {NUM_IMAGES/time_ir:.2f}"
)


if PADDLEGAN_INFERENCE:
    with paddle.no_grad():
        start = time.perf_counter()
        for _ in range(NUM_IMAGES):
            predictor.generator(input_tensor)
        end = time.perf_counter()
        time_paddle = end - start
    print(
        f"PaddleGAN model on CPU: {time_paddle/NUM_IMAGES:.3f} seconds per image, "
        f"FPS: {NUM_IMAGES/time_paddle:.2f}"
    )

猜你喜欢

转载自blog.csdn.net/weixin_47567401/article/details/123375614