• C# 人像卡通化 Onnx photo2cartoon


    目录

    效果

    模型信息

    项目

    代码

    图片保存代码参考

    下载 

    说明

    其他


    效果

    模型信息

    Inputs
    -------------------------
    name:input
    tensor:Float[1, 3, 256, 256]
    ---------------------------------------------------------------

    Outputs
    -------------------------
    name:output
    tensor:Float[1, 3, 256, 256]
    ---------------------------------------------------------------

    项目

    VS2022

    .net framework 4.8

    OpenCvSharp 4.8

    Microsoft.ML.OnnxRuntime 1.16.2

    代码

    创建Tensor

    for (int y = 0; y < resize_image.Height; y++)
    {
        for (int x = 0; x < resize_image.Width; x++)
        {
            input_tensor[0, 0, y, x] = (resize_image.At(y, x)[0] / 255f - 0.5f) / 0.5f;
            input_tensor[0, 1, y, x] = (resize_image.At(y, x)[1] / 255f - 0.5f) / 0.5f;
            input_tensor[0, 2, y, x] = (resize_image.At(y, x)[2] / 255f - 0.5f) / 0.5f;
        }
    }

    1. using Microsoft.ML.OnnxRuntime;
    2. using Microsoft.ML.OnnxRuntime.Tensors;
    3. using OpenCvSharp;
    4. using System;
    5. using System.Collections.Generic;
    6. using System.Drawing;
    7. using System.Linq;
    8. using System.Threading.Tasks;
    9. using System.Windows.Forms;
    10. namespace 人像卡通化
    11. {
    12. public partial class Form1 : Form
    13. {
    14. public Form1()
    15. {
    16. InitializeComponent();
    17. }
    18. string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
    19. string image_path = "";
    20. string startupPath;
    21. DateTime dt1 = DateTime.Now;
    22. DateTime dt2 = DateTime.Now;
    23. string model_path;
    24. Mat image;
    25. Mat result_image;
    26. int modelSize = 256;
    27. SessionOptions options;
    28. InferenceSession onnx_session;
    29. Tensor<float> input_tensor;
    30. List<NamedOnnxValue> input_ontainer;
    31. IDisposableReadOnlyCollection<DisposableNamedOnnxValue> result_infer;
    32. DisposableNamedOnnxValue[] results_onnxvalue;
    33. Tensor<float> result_tensors;
    34. float[] result_array;
    35. private void button1_Click(object sender, EventArgs e)
    36. {
    37. OpenFileDialog ofd = new OpenFileDialog();
    38. ofd.Filter = fileFilter;
    39. if (ofd.ShowDialog() != DialogResult.OK) return;
    40. pictureBox1.Image = null;
    41. image_path = ofd.FileName;
    42. pictureBox1.Image = new Bitmap(image_path);
    43. textBox1.Text = "";
    44. image = new Mat(image_path);
    45. pictureBox2.Image = null;
    46. }
    47. private void button2_Click(object sender, EventArgs e)
    48. {
    49. if (image_path == "")
    50. {
    51. return;
    52. }
    53. textBox1.Text = "";
    54. pictureBox2.Image = null;
    55. int oldwidth = image.Cols;
    56. int oldheight = image.Rows;
    57. //缩放图片大小
    58. int maxEdge = Math.Max(image.Rows, image.Cols);
    59. float ratio = 1.0f * modelSize / maxEdge;
    60. int newHeight = (int)(image.Rows * ratio);
    61. int newWidth = (int)(image.Cols * ratio);
    62. Mat resize_image = image.Resize(new OpenCvSharp.Size(newWidth, newHeight));
    63. int width = resize_image.Cols;
    64. int height = resize_image.Rows;
    65. if (width != modelSize || height != modelSize)
    66. {
    67. resize_image = resize_image.CopyMakeBorder(0, modelSize - newHeight, 0, modelSize - newWidth, BorderTypes.Constant, new Scalar(255, 255, 255));
    68. }
    69. Cv2.CvtColor(resize_image, resize_image, ColorConversionCodes.BGR2RGB);
    70. // 输入Tensor
    71. for (int y = 0; y < resize_image.Height; y++)
    72. {
    73. for (int x = 0; x < resize_image.Width; x++)
    74. {
    75. input_tensor[0, 0, y, x] = (resize_image.At<Vec3b>(y, x)[0] / 255f - 0.5f) / 0.5f;
    76. input_tensor[0, 1, y, x] = (resize_image.At<Vec3b>(y, x)[1] / 255f - 0.5f) / 0.5f;
    77. input_tensor[0, 2, y, x] = (resize_image.At<Vec3b>(y, x)[2] / 255f - 0.5f) / 0.5f;
    78. }
    79. }
    80. //input_tensor 放入一个输入参数的容器,并指定名称
    81. input_ontainer.Add(NamedOnnxValue.CreateFromTensor("input", input_tensor));
    82. dt1 = DateTime.Now;
    83. //运行 Inference 并获取结果
    84. result_infer = onnx_session.Run(input_ontainer);
    85. dt2 = DateTime.Now;
    86. //将输出结果转为DisposableNamedOnnxValue数组
    87. results_onnxvalue = result_infer.ToArray();
    88. //读取第一个节点输出并转为Tensor数据
    89. result_tensors = results_onnxvalue[0].AsTensor<float>();
    90. result_array = result_tensors.ToArray();
    91. float[] temp_r = new float[256 * 256];
    92. float[] temp_g = new float[256 * 256];
    93. float[] temp_b = new float[256 * 256];
    94. Array.Copy(result_array, temp_r, 256 * 256);
    95. Array.Copy(result_array, 256 * 256, temp_g, 0, 256 * 256);
    96. Array.Copy(result_array, 256 * 256 * 2, temp_b, 0, 256 * 256);
    97. Mat rmat = new Mat(256, 256, MatType.CV_32F, temp_r);
    98. Mat gmat = new Mat(256, 256, MatType.CV_32F, temp_g);
    99. Mat bmat = new Mat(256, 256, MatType.CV_32F, temp_b);
    100. rmat = (rmat + 1f) * 127.5f;
    101. gmat = (gmat + 1f) * 127.5f;
    102. bmat = (bmat + 1f) * 127.5f;
    103. result_image = new Mat();
    104. Cv2.Merge(new Mat[] { rmat, gmat, bmat }, result_image);
    105. if (!result_image.Empty())
    106. {
    107. //还原图像大小
    108. if (width != modelSize || height != modelSize)
    109. {
    110. Rect rect = new Rect(0, 0, width, height);
    111. result_image = result_image.Clone(rect);
    112. }
    113. result_image = result_image.Resize(new OpenCvSharp.Size(oldwidth, oldheight));
    114. pictureBox2.Image = new Bitmap(result_image.ToMemoryStream());
    115. textBox1.Text = "推理耗时:" + (dt2 - dt1).TotalMilliseconds + "ms";
    116. }
    117. else
    118. {
    119. textBox1.Text = "无信息";
    120. }
    121. }
    122. private void Form1_Load(object sender, EventArgs e)
    123. {
    124. startupPath = System.Windows.Forms.Application.StartupPath;
    125. model_path = startupPath + "\\photo2cartoon_weights.onnx";
    126. // 创建输出会话,用于输出模型读取信息
    127. options = new SessionOptions();
    128. options.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;
    129. // 设置为CPU上运行
    130. options.AppendExecutionProvider_CPU(0);
    131. // 创建推理模型类,读取本地模型文件
    132. onnx_session = new InferenceSession(model_path, options);
    133. // 输入Tensor
    134. input_tensor = new DenseTensor<float>(new[] { 1, 3, 256, 256 });
    135. // 创建输入容器
    136. input_ontainer = new List<NamedOnnxValue>();
    137. }
    138. }
    139. }

    图片保存代码参考

    if (pictureBox2.Image == null)
    {
        return;
    }
    Bitmap output = new Bitmap(pictureBox2.Image);
    var sdf = new SaveFileDialog();
    sdf.Title = "保存";
    sdf.Filter = "Images (*.jpg)|*.jpg|Images (*.png)|*.png|Images (*.bmp)|*.bmp|Images (*.emf)|*.emf|Images (*.exif)|*.exif|Images (*.gif)|*.gif|Images (*.ico)|*.ico|Images (*.tiff)|*.tiff|Images (*.wmf)|*.wmf";
    if (sdf.ShowDialog() == DialogResult.OK)
    {
        switch (sdf.FilterIndex)
        {
            case 1:
                {
                    output.Save(sdf.FileName, ImageFormat.Jpeg);
                    break;
                }
            case 2:
                {
                    output.Save(sdf.FileName, ImageFormat.Png);
                    break;
                }
            case 3:
                {
                    output.Save(sdf.FileName, ImageFormat.Bmp);
                    break;
                }
            case 4:
                {
                    output.Save(sdf.FileName, ImageFormat.Emf);
                    break;
                }
            case 5:
                {
                    output.Save(sdf.FileName, ImageFormat.Exif);
                    break;
                }
            case 6:
                {
                    output.Save(sdf.FileName, ImageFormat.Gif);
                    break;
                }
            case 7:
                {
                    output.Save(sdf.FileName, ImageFormat.Icon);
                    break;
                }
            case 8:
                {
                    output.Save(sdf.FileName, ImageFormat.Tiff);
                    break;
                }
            case 9:
                {
                    output.Save(sdf.FileName, ImageFormat.Wmf);
                    break;
                }
        }
        MessageBox.Show("保存成功,位置:" + sdf.FileName);    

    下载 

    Demo下载

    说明

    1、该例子只是人像转卡通像,转之前需要如下前置处理(为了效果更好)

    • 检测人脸及关键点。
    • 根据关键点旋转校正人脸。
    • 将关键点边界框按固定的比例扩张并裁剪出人脸区域。
    • 使用人像分割模型将背景置白。

    2、该模型不能用于分割半身像,因为该模型是专用模型,需先裁剪出人脸区域再输入

    人像分割参考:

    C# PaddleInference.PP-HumanSeg 人像分割 替换背景色-CSDN博客

    人脸检测参考:

    C# DlibDotNet 人脸识别、人脸68特征点识别、人脸5特征点识别、人脸对齐,三角剖分,人脸特征比对-CSDN博客

    3、参考

    GitHub - minivision-ai/photo2cartoon-paddle: 人像卡通化探索项目 (photo-to-cartoon translation project)

    其他

    C# AnimeGANv2 人像动漫化-CSDN博客

  • 相关阅读:
    强化学习:带MonteCarlo的Reinforce求解MountainCar问题
    matlab server-client传输数据
    做一个物联网温湿度传感器(一)SHT30传感器介绍
    JavaWeb实战:基础CRUD+批量删除+分页+条件
    APP备案流程详细解读
    基于C#+SQL server的校园卡消费信息管理系统
    【es6】解决箭头函数所有的问题,箭头函数的 this 指针,使用 new 操作符
    python读取kafka数据
    ubuntu 22.04 截图工具 shutter
    VLAN划分-----计算机网络
  • 原文地址:https://blog.csdn.net/lw112190/article/details/133711704