C# 人像卡通化

news2024/12/22 16:14:16

效果

项目

代码

using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace 人像卡通化
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
        string image_path = "";
        string startupPath;
        DateTime dt1 = DateTime.Now;
        DateTime dt2 = DateTime.Now;
        string model_path;
        Mat image;
        Mat result_image;
        int modelSize = 256;

        SessionOptions options;
        InferenceSession onnx_session;
        Tensor<float> input_tensor;
        List<NamedOnnxValue> input_ontainer;
        IDisposableReadOnlyCollection<DisposableNamedOnnxValue> result_infer;
        DisposableNamedOnnxValue[] results_onnxvalue;

        Tensor<float> result_tensors;
        float[] result_array;

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;
            pictureBox1.Image = null;
            image_path = ofd.FileName;
            pictureBox1.Image = new Bitmap(image_path);
            textBox1.Text = "";
            image = new Mat(image_path);
            pictureBox2.Image = null;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (image_path == "")
            {
                return;
            }

            textBox1.Text = "";
            pictureBox2.Image = null;

            int oldwidth = image.Cols;
            int oldheight = image.Rows;

            //缩放图片大小
            int maxEdge = Math.Max(image.Rows, image.Cols);
            float ratio = 1.0f * modelSize / maxEdge;
            int newHeight = (int)(image.Rows * ratio);
            int newWidth = (int)(image.Cols * ratio);
            Mat resize_image = image.Resize(new OpenCvSharp.Size(newWidth, newHeight));
            int width = resize_image.Cols;
            int height = resize_image.Rows;
            if (width != modelSize || height != modelSize)
            {
                resize_image = resize_image.CopyMakeBorder(0, modelSize - newHeight, 0, modelSize - newWidth, BorderTypes.Constant, new Scalar(255, 255, 255));
            }

            Cv2.CvtColor(resize_image, resize_image, ColorConversionCodes.BGR2RGB);

            // 输入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<Vec3b>(y, x)[0] / 255f - 0.5f) / 0.5f;
                    input_tensor[0, 1, y, x] = (resize_image.At<Vec3b>(y, x)[1] / 255f - 0.5f) / 0.5f;
                    input_tensor[0, 2, y, x] = (resize_image.At<Vec3b>(y, x)[2] / 255f - 0.5f) / 0.5f;
                }
            }

            //将 input_tensor 放入一个输入参数的容器,并指定名称
            input_ontainer.Add(NamedOnnxValue.CreateFromTensor("input", input_tensor));

            dt1 = DateTime.Now;
            //运行 Inference 并获取结果
            result_infer = onnx_session.Run(input_ontainer);
            dt2 = DateTime.Now;

            //将输出结果转为DisposableNamedOnnxValue数组
            results_onnxvalue = result_infer.ToArray();

            //读取第一个节点输出并转为Tensor数据
            result_tensors = results_onnxvalue[0].AsTensor<float>();

            result_array = result_tensors.ToArray();

            float[] temp_r = new float[256 * 256];
            float[] temp_g = new float[256 * 256];
            float[] temp_b = new float[256 * 256];

            Array.Copy(result_array, temp_r, 256 * 256);
            Array.Copy(result_array, 256 * 256, temp_g, 0, 256 * 256);
            Array.Copy(result_array, 256 * 256 * 2, temp_b, 0, 256 * 256);

            Mat rmat = new Mat(256, 256, MatType.CV_32F, temp_r);
            Mat gmat = new Mat(256, 256, MatType.CV_32F, temp_g);
            Mat bmat = new Mat(256, 256, MatType.CV_32F, temp_b);

            rmat = (rmat + 1f) * 127.5f;
            gmat = (gmat + 1f) * 127.5f;
            bmat = (bmat + 1f) * 127.5f;

            result_image = new Mat();
            Cv2.Merge(new Mat[] { rmat, gmat, bmat }, result_image);

            if (!result_image.Empty())
            {
                //还原图像大小
                if (width != modelSize || height != modelSize)
                {
                    Rect rect = new Rect(0, 0, width, height);
                    result_image = result_image.Clone(rect);
                }
                result_image = result_image.Resize(new OpenCvSharp.Size(oldwidth, oldheight));

                pictureBox2.Image = new Bitmap(result_image.ToMemoryStream());
                textBox1.Text = "推理耗时:" + (dt2 - dt1).TotalMilliseconds + "ms";
            }
            else
            {
                textBox1.Text = "无信息";
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            startupPath = System.Windows.Forms.Application.StartupPath;
            model_path = startupPath + "\\photo2cartoon_weights.onnx";

            // 创建输出会话,用于输出模型读取信息
            options = new SessionOptions();
            options.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;
            // 设置为CPU上运行
            options.AppendExecutionProvider_CPU(0);

            // 创建推理模型类,读取本地模型文件
            onnx_session = new InferenceSession(model_path, options);

            // 输入Tensor
            input_tensor = new DenseTensor<float>(new[] { 1, 3, 256, 256 });

            // 创建输入容器
            input_ontainer = new List<NamedOnnxValue>();
        }
    }
}

Demo下载

说明

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

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

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

人像分割参考:

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

人脸检测参考:

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

3、参考

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1073947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像分割-Segment Anything实践

一、模型介绍 Segment Anything 模型是一种新的图像分割模型&#xff0c;它可以在不需要大量标注数据的情况下&#xff0c;对图像中的任何物体进行分割。这种方法可以帮助计算机视觉领域的研究人员和开发人员更轻松地训练模型&#xff0c;从而提高计算机视觉应用程序的性能。该…

超前预告 | 云原生?大模型?这届乌镇双态IT大会亮点有点多

石道旁的水面&#xff0c;轻轻泛着微光&#xff0c;几片墨绿缓缓飘下&#xff0c;荡起柔和的波纹&#xff0c;向对岸游去。这儿不似北方秋阳如火的躁动&#xff0c;这儿的秋色是安静的&#xff0c;里便是江南水乡乌镇…… 2023年&#xff0c;第六届双态IT乌镇用户大会将于10月…

不再为文件名大小写烦恼:批量转换,一招搞定

在电脑使用过程中&#xff0c;我们经常需要处理各种文件&#xff0c;有时需要对文件名进行大小写转换以符合特定要求或便于管理。手动修改不仅费时还容易出错&#xff0c;那么有没有一种方法可以批量转换文件名大小写呢&#xff1f;答案是肯定的&#xff0c;下面就为大家介绍如…

DC电源模块在电容滤波器上的设计

BOSHIDA DC电源模块在电容滤波器上的设计 DC电源模块在电容滤波器上的设计是电源管理系统中非常重要的一部分&#xff0c;其目的是为了确保电源输出电压的稳定性和纹波尽可能小。在设计中&#xff0c;需要考虑到电源负载的变化和变压器等电源配件的电磁干扰等因素。下面我们详细…

基于Java的民宿管理系统设计与实现(源码+lw+部署文档+讲解等)(民宿预约、民宿预订、民宿管理、酒店预约通用)

文章目录 前言具体实现截图论文参考详细视频演示代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技…

【数据结构】二叉树--堆排序

目录 一 降序(建小堆) 二 升序 (建大堆) ​三 优化(以升序为例) 四 TOP-K问题 一 降序(建小堆) void Swap(int* x, int* y) {int tmp *x;*x *y;*y tmp; }//降序 建小堆 void AdjustUp(int* a, int child) {int parent (child - 1) / 2;while (child > 0){if (a[chil…

(三)Apache log4net™ 手册 -演示

0、引言 在开始本文之前&#xff0c;推荐您首先阅读 Apache log4net™ 手册中有关 介绍 与 配置 的相关内容。本文将通过实践分别为您演示如何使用 Visual Studio 2022 在 .NET Framework 项目和 .NET 项目下配置并使用 Log4Net。 1、为 .NET Framework 项目配置 Log4Net 1.1…

day26--AJAX(axios使用,http协议(部分),接口文档,form-serialize使用)

目录 AJAX介绍&#xff1a; 什么是AJAX 怎么用AJAX&#xff1f; axios的使用&#xff1a; axios的核心配置&#xff1a; url统一资源定位符&#xff1a; 组成&#xff1a; http协议&#xff1a; 域名&#xff1a; 资源路径&#xff1a; 查询参数&#xff1a; 常用的…

Text embedding 模型总结

文章目录 MTEB榜单8个嵌入任务三种数据集类别 C_METB榜单文本向量表示模型 目前&#xff0c;随着 Langchain LLM模型 的火热&#xff0c;除了层出不穷的大模型外&#xff0c;因为检索的能力会很大程度影响最终的问答效果&#xff0c;文本的嵌入模型也是大家比较关注的。本文主…

Redis之主从复制,哨兵模式,集群

Redis之主从复制&#xff0c;哨兵模式&#xff0c;集群 1、主从复制1.1主从复制概述1.2Redis主从复制作用1.3Redis主从复制流程1.4部署Redis 主从复制 2、哨兵模式2.1哨兵模式原理2.2哨兵模式的作用2.3哨兵模式的结构2.4故障转移机制2.5搭建Redis 哨兵模式 3、Redis集群模式3.1…

在开发APP过程中外包我们经常会遇到哪些问题?我们该如何避免?

虽然选择一个外包公司有很多坑&#xff0c;但是我们有的时候不得不选择一个外包公司&#xff0c;所以选择外包的时候我们要注意一些细节可以有效的区分开外包app产品的可靠性还是至关重要&#xff01;希望我整理的这些对各位有需要的同学有所帮助&#xff01; 首先我们先从公司…

看好你家电视盒的后门!数千个Android电视盒感染了与欺诈相关的危险恶意软件

如果你从Android电视盒获得流媒体修复程序&#xff0c;则你的设备可能会被恶意软件所感染&#xff0c;这些恶意软件能够进行广告欺诈、创建假帐户&#xff0c;并通过悄悄地将你的数据转移到中国的服务器来销售对家庭网络的访问。 根据本周的一份新报告&#xff0c;网络安全公司…

深入理解强化学习——强化学习的目标和数据

分类目录&#xff1a;《深入理解强化学习》总目录 强化学习的目标 在动态环境下&#xff0c;智能体和环境每次进行交互时&#xff0c;环境会产生相应的奖励信号&#xff0c;其往往由实数标量来表示。这个奖励信号一般是诠释当前状态或动作的好坏的及时反馈信号&#xff0c;好比…

射频识别技术课程实验--模拟串口间的通信--基础实验

射频识别技术课程实验–模拟串口间的通信 前期准备 串口调试小助手&#xff1a; 模拟串口工具&#xff1a; Visual Studio 2022&#xff1a; 测试代码&#xff08;c&#xff09;&#xff1a; #include<iostream> #include<Windows.h>using namespace std;int ma…

Redis未授权访问漏洞实验

1 Redis简介 Redis是一个开源的内存数据库管理系统&#xff0c;它被广泛用于缓存、消息队列和实时数据分析等应用场景。Redis支持多种数据结构&#xff0c;包括字符串、列表、集合、有序集合和哈希表&#xff0c;可以通过简单的键值对方式存储和检索数据。由于其高性能和低延迟…

C++day03(动态内存、类中特殊成员函数)

今日任务 1> 思维导图 2> 设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数。 代码&#xff1a; …

【刷题】只出现一次的数字(三种解法)

【刷题】只出现一次的数字 文章目录 【刷题】只出现一次的数字解法异或运算解法一 : 异或运算解法二:集合类Set集合Map集合 链接: https://www.nowcoder.com/share/jump/2008263481696810321082 https://leetcode.cn/problems/single-number/description/ 题目描述 给定一个整…

线段树基本原理和操作

线段树的一些基本操作和原理&#xff1a; 由二分的思想而来&#xff0c;一段区间划分&#xff0c;实现大量数据的查询删除O(log(n)) 线段树&#xff08;英语&#xff1a;Segment tree&#xff09;是一种二叉树形数据结构&#xff0c;1977年由Jon Louis Bentley发明&#xff0…

文件操作【详解】

目录 一、什么是文件 二、文件的打开和关闭 1.文件指针 2.文件的打开和关闭 3.文件的打开方式 三、文件的顺序读写 1.关于输入输出&#xff0c;读和写 2.关于流的介绍 3.操作文件的函数 字符输入函数 fgetc的使用 字符输入函数 fputc的使用 文本行输出函数 fputs() …

awvs 中低危漏洞

低危 X-Frame-Options Header未配置 查看请求头中是否存在X-Frame-Options Header字段 会话Cookie中缺少secure属性(未设置安全标志的Cookie) 当cookie设置为Secure标志时&#xff0c;它指示浏览器只能通过安全SSL/TLS通道访问cookie。 未设置HttpOnly标志的Cookie 当cookie设置…