圖像編輯器 Monica 之生成漫畫風(fēng)格的圖像、以及使用 GPU 實(shí)現(xiàn)推理

一. 圖像編輯器 Monica

Monica 是一款跨平臺(tái)的桌面圖像編輯軟件(早期是為了驗(yàn)證一些算法而產(chǎn)生的)。

screenshot.png

其技術(shù)棧如下:

  • Kotlin 編寫 UI(Kotlin Compose Desktop 作為 UI 框架)
  • 基于 mvvm 模式,依賴注入使用 koin,編譯使用 JDK 17
  • 部分算法使用 Kotlin 實(shí)現(xiàn)
  • 傳統(tǒng)的 CV 算法使用 OpenCV C++ 來實(shí)現(xiàn),Kotlin 通過 jni 來調(diào)用。
  • Monica 所使用的模型,主要使用 ONNXRuntime 來部署和加速模型,使用 C++ 實(shí)現(xiàn)。

Monica 目前還處于開發(fā)階段,當(dāng)前版本的可以參見 github 地址:
https://github.com/fengzhizi715/Monica

這個(gè)月我增加了生成多種漫畫風(fēng)格圖像的功能,把使用 ONNXRuntime 部署的模型都支持使用 GPU 來加速推理。當(dāng)然前提是需要有 N 卡。

二. 漫畫風(fēng)格

2.1 效果展示

Monica 將圖片轉(zhuǎn)換成動(dòng)漫風(fēng)格的模型使用 AnimeGANv3(https://github.com/TachibanaYoshino/AnimeGANv3)

下面展示該模塊的入口


漫畫風(fēng)格的入口.png

目前 Monica 集成了其5種動(dòng)漫風(fēng)格的模型。下面列舉了幾種風(fēng)格的效果


宮崎駿風(fēng)格.png
黑白線稿.png
可愛風(fēng)格.png

除了人物之外,景色一樣可以動(dòng)漫化。


風(fēng)景圖.png
風(fēng)景圖動(dòng)漫化.png

2.2 模型的加載、推理

下面展示 AnimeGANv3 相關(guān)模型的加載、推理方法,先定義一個(gè) AnimeGAN 類

#include <iostream>
#include <fstream>
#include <string>
#include <math.h>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
//#include <cuda_provider_factory.h>
#include <onnxruntime_cxx_api.h>
#include "../common/OnnxRuntimeBase.h"

using namespace cv;
using namespace std;
using namespace Ort;

class AnimeGAN: public OnnxRuntimeBase
{
public:
    AnimeGAN(std::string modelPath, const char* logId, const char* provider);

    void inferImage(Mat& src, Mat& dst);
private:
    const int inpWidth = 512;
    const int inpHeight = 512;
    const int outWidth = 512;
    const int outHeight = 512;
    vector<float> input_image_;
};

然后實(shí)現(xiàn)該類

#include "../../include/cartoon/AnimeGAN.h"

AnimeGAN::AnimeGAN(string modelPath, const char* logId, const char* provider): OnnxRuntimeBase(modelPath, logId, provider)
{
}

// 工具函數(shù):將 OpenCV 的 Mat 轉(zhuǎn)為 float tensor(NCHW)
std::vector<float> prepare_input_nhwc(const cv::Mat& img) {
    std::vector<float> input_tensor_values;
    for (int y = 0; y < img.rows; ++y) {
        for (int x = 0; x < img.cols; ++x) {
            for (int c = 0; c < 3; ++c) {
                input_tensor_values.push_back(img.at<cv::Vec3f>(y, x)[c]);
            }
        }
    }
    return input_tensor_values;
}


// 工具函數(shù):將輸出 tensor 轉(zhuǎn)為 Mat
cv::Mat tensor_to_mat_nhwc(const float* data, int h, int w) {
    cv::Mat output(h, w, CV_32FC3);
    int idx = 0;
    for (int y = 0; y < h; ++y) {
        for (int x = 0; x < w; ++x) {
            for (int c = 0; c < 3; ++c) {
                output.at<cv::Vec3f>(y, x)[c] = data[idx++];
            }
        }
    }
    return output;
}

void AnimeGAN::inferImage(Mat& src, Mat& dst)
{
    int w = src.cols;
    int h = src.rows;

    Mat temp;
    resize(src, temp, Size(this->inpWidth, this->inpHeight));
    temp.convertTo(temp, CV_32F, 1.0 / 255.0); // 歸一化 [0,1]

    // 創(chuàng)建輸入 tensor 數(shù)據(jù)
    std::vector<float> input_tensor_values = prepare_input_nhwc(temp);
    std::array<int64_t, 4> input_shape = {1, temp.rows, temp.cols, 3};

    Ort::MemoryInfo allocator_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
    Ort::Value input_tensor_ = Ort::Value::CreateTensor<float>(allocator_info, input_tensor_values.data(), input_tensor_values.size(), input_shape.data(), input_shape.size());

    vector<Value> ort_outputs = this -> forward(input_tensor_);

    // 取出輸出數(shù)據(jù)
    float* output_data = ort_outputs.front().GetTensorMutableData<float>();
    dst = tensor_to_mat_nhwc(output_data, outHeight, outWidth);

    // 后處理輸出圖像
    dst = cv::min(cv::max(dst, 0.0f), 1.0f); // clamp 到 [0,1]
    dst.convertTo(dst, CV_8UC3, 255.0);      // 轉(zhuǎn)回 [0,255]

    cv::resize(dst, dst, cv::Size(w, h));
}

通過 AnimeGAN 類就可以使用相關(guān)模型來推理,將圖像轉(zhuǎn)換成漫畫的風(fēng)格。

三. 使用 GPU 推理

在使用 GPU 做推理時(shí),需要有個(gè)前提就是確保安裝了 CUDA、cuDNN 以及 CUDA 對應(yīng)的 ONNXRuntime 版本。

使用nvidia-smi命令獲取 GPU 的相關(guān)信息。

然后查看 CUDA 版本

nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2021 NVIDIA Corporation
Built on Wed_Jun__2_19:15:15_PDT_2021
Cuda compilation tools, release 11.4, V11.4.48
Build cuda_11.4.r11.4/compiler.30033411_0

查看 cuDNN 版本

cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2
#define CUDNN_MAJOR 8
#define CUDNN_MINOR 2
#define CUDNN_PATCHLEVEL 4

確保上述版本都兼容之后(版本不兼容會(huì)有各種問題),在 CMake 中添加判斷當(dāng)前系統(tǒng)是否支持 CUDA

# 定義一個(gè)開關(guān)變量用于控制是否啟用 CUDA
set(ENABLE_CUDA OFF)
# 針對不同平臺(tái)進(jìn)行判斷
if(APPLE)
    message(STATUS "當(dāng)前平臺(tái):macOS(不支持 CUDA,使用 CPU 版本)")
    set(ENABLE_CUDA OFF)
elseif(WIN32 OR UNIX)
    # 在 Windows/Linux 上查找 CUDA 工具包(推薦使用 CUDAToolkit 模塊)
    find_package(CUDAToolkit QUIET)
    if(CUDAToolkit_FOUND)
        message(STATUS "CUDA 工具包已找到,使用 GPU 版本")
        set(ENABLE_CUDA ON)
    else()
        message(STATUS "未找到 CUDA 工具包,使用 CPU 版本")
    endif()
else()
    message(STATUS "未知平臺(tái),默認(rèn)使用 CPU 版本")
    set(ENABLE_CUDA OFF)
endif()

針對不同的環(huán)境,配置不同的 ONNXRuntime 版本

    # 指定ONNX Runtime的路徑
    if(ENABLE_CUDA)
        set(ONNXRUNTIME_ROOT "/home/xxx/onnxruntime/onnxruntime-linux-x64-gpu-1.10.0")
    else()
        set(ONNXRUNTIME_ROOT "/home/xxx/onnxruntime/onnxruntime-linux-x64-1.10.0")
    endif()

然后再設(shè)置統(tǒng)一的編譯選項(xiàng)或宏定義,用于在源代碼中區(qū)分不同分支

    if(ENABLE_CUDA)
        target_compile_definitions(${LINUX_PROJECT_NAME} PRIVATE USE_GPU)
    endif()

最后,在源代碼中加載模型的時(shí)候區(qū)分一下是否支持 GPU

#ifdef USE_GPU
    const string& onnx_provider = OnnxProviders::CUDA;
#else
    const string& onnx_provider = OnnxProviders::CPU;
#endif

詳細(xì)的代碼可以在這里找到:
https://github.com/fengzhizi715/MonicaImageProcessHttpServer

四. 總結(jié)

Monica 后續(xù)的重點(diǎn)是重構(gòu)形狀繪制模塊,增加人臉美化的功能,引入一些有趣的模型,以及優(yōu)化軟件的各種使用體驗(yàn)。

當(dāng)然,還有盡快解決 Mac 打包的問題,給出各個(gè)平臺(tái)的安裝包。

Monica github 地址:https://github.com/fengzhizi715/Monica

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容