C#版Facefusion:让你的脸与世界融为一体!-02 获取人脸关键点

news2024/9/24 3:29:21

C#版Facefusion:让你的脸与世界融为一体!-02 获取人脸关键点

目录

说明

效果

模型信息

项目

代码

下载


说明

C#版Facefusion一共有如下5个步骤:

1、使用yoloface_8n.onnx进行人脸检测
2、使用2dfan4.onnx获取人脸关键点
3、使用arcface_w600k_r50.onnx获取人脸特征值
4、使用inswapper_128.onnx进行人脸交换
5、使用gfpgan_1.4.onnx进行人脸增强
本文分享使用2dfan4.onnx实现C#版Facefusion第二步:获取人脸关键点。

效果

模型信息

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

Outputs
-------------------------
name:landmarks_xyscore
tensor:Float[1, 68, 3]
name:heatmaps
tensor:Float[1, 68, 64, 64]
---------------------------------------------------------------

项目

代码

Form2.cs

using Newtonsoft.Json;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace FaceFusionSharp
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";

        string source_path = "";
        string target_path = "";

        Face68Landmarks detect_68landmarks;

        private void button2_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox1.Image = null;

            source_path = ofd.FileName;
            pictureBox1.Image = new Bitmap(source_path);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox2.Image = null;

            target_path = ofd.FileName;
            pictureBox2.Image = new Bitmap(target_path);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (pictureBox1.Image == null || pictureBox2.Image == null)
            {
                return;
            }

            button1.Enabled = false;
            Application.DoEvents();

            Mat source_img = Cv2.ImRead(source_path);

            List<Bbox> boxes= new List<Bbox>(); 
            string boxesStr = "[{\"xmin\":261.8998,\"ymin\":192.045776,\"xmax\":821.1629,\"ymax\":936.720032}]"; 
            boxes = JsonConvert.DeserializeObject<List<Bbox>>(boxesStr);

            int position = 0; //一张图片里可能有多个人脸,这里只考虑1个人脸的情况
            List<Point2f> face68landmarks = detect_68landmarks.detect(source_img, boxes[position]);

            //绘图
            foreach (Point2f item in face68landmarks)
            {
                Cv2.Circle(source_img, (int)item.X, (int)item.Y, 8, new Scalar(0, 255, 0), -1);
            }
            pictureBox1.Image = source_img.ToBitmap();

            Mat target_img = Cv2.ImRead(target_path);
            boxesStr = "[{\"xmin\":413.807,\"ymin\":1.377529,\"xmax\":894.659,\"ymax\":645.6737}]";
            boxes = JsonConvert.DeserializeObject<List<Bbox>>(boxesStr);

            position = 0; //一张图片里可能有多个人脸,这里只考虑1个人脸的情况
            List<Point2f> target_landmark_5;
            target_landmark_5 = detect_68landmarks.detect(target_img, boxes[position]);

            //绘图
            foreach (Point2f item in target_landmark_5)
            {
                Cv2.Circle(target_img, (int)item.X, (int)item.Y, 8, new Scalar(0, 255, 0), -1);
            }
            pictureBox2.Image = target_img.ToBitmap();

            button1.Enabled = true;

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            detect_68landmarks = new Face68Landmarks("model/2dfan4.onnx");

            target_path = "images/target.jpg";
            source_path = "images/source.jpg";

            pictureBox1.Image = new Bitmap(source_path);
            pictureBox2.Image = new Bitmap(target_path);
        }
    }
}

using Newtonsoft.Json;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace FaceFusionSharp
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";

        string source_path = "";
        string target_path = "";

        Face68Landmarks detect_68landmarks;

        private void button2_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox1.Image = null;

            source_path = ofd.FileName;
            pictureBox1.Image = new Bitmap(source_path);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox2.Image = null;

            target_path = ofd.FileName;
            pictureBox2.Image = new Bitmap(target_path);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (pictureBox1.Image == null || pictureBox2.Image == null)
            {
                return;
            }

            button1.Enabled = false;
            Application.DoEvents();

            Mat source_img = Cv2.ImRead(source_path);

            List<Bbox> boxes= new List<Bbox>(); 
            string boxesStr = "[{\"xmin\":261.8998,\"ymin\":192.045776,\"xmax\":821.1629,\"ymax\":936.720032}]"; 
            boxes = JsonConvert.DeserializeObject<List<Bbox>>(boxesStr);

            int position = 0; //一张图片里可能有多个人脸,这里只考虑1个人脸的情况
            List<Point2f> face68landmarks = detect_68landmarks.detect(source_img, boxes[position]);

            //绘图
            foreach (Point2f item in face68landmarks)
            {
                Cv2.Circle(source_img, (int)item.X, (int)item.Y, 8, new Scalar(0, 255, 0), -1);
            }
            pictureBox1.Image = source_img.ToBitmap();

            Mat target_img = Cv2.ImRead(target_path);
            boxesStr = "[{\"xmin\":413.807,\"ymin\":1.377529,\"xmax\":894.659,\"ymax\":645.6737}]";
            boxes = JsonConvert.DeserializeObject<List<Bbox>>(boxesStr);

            position = 0; //一张图片里可能有多个人脸,这里只考虑1个人脸的情况
            List<Point2f> target_landmark_5;
            target_landmark_5 = detect_68landmarks.detect(target_img, boxes[position]);

            //绘图
            foreach (Point2f item in target_landmark_5)
            {
                Cv2.Circle(target_img, (int)item.X, (int)item.Y, 8, new Scalar(0, 255, 0), -1);
            }
            pictureBox2.Image = target_img.ToBitmap();

            button1.Enabled = true;

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            detect_68landmarks = new Face68Landmarks("model/2dfan4.onnx");

            target_path = "images/target.jpg";
            source_path = "images/source.jpg";

            pictureBox1.Image = new Bitmap(source_path);
            pictureBox2.Image = new Bitmap(target_path);
        }
    }
}

Face68Landmarks.cs

using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;

namespace FaceFusionSharp
{
    internal class Face68Landmarks
    {
        float[] input_image;
        int input_height;
        int input_width;
        Mat inv_affine_matrix = new Mat();

        SessionOptions options;
        InferenceSession onnx_session;

        public Face68Landmarks(string modelpath)
        {
            input_height = 256;
            input_width = 256;

            options = new SessionOptions();
            options.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;
            options.AppendExecutionProvider_CPU(0);// 设置为CPU上运行

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

        }

        void preprocess(Mat srcimg, Bbox bounding_box)
        {
            float sub_max = Math.Max(bounding_box.xmax - bounding_box.xmin, bounding_box.ymax - bounding_box.ymin);
            float scale = 195.0f / sub_max;
            float[] translation = new float[] { (256.0f - (bounding_box.xmax + bounding_box.xmin) * scale) * 0.5f, (256.0f - (bounding_box.ymax + bounding_box.ymin) * scale) * 0.5f };
            //python程序里的warp_face_by_translation函数
            Mat affine_matrix = new Mat(2, 3, MatType.CV_32FC1, new float[] { scale, 0.0f, translation[0], 0.0f, scale, translation[1] });
            Mat crop_img = new Mat();
            Cv2.WarpAffine(srcimg, crop_img, affine_matrix, new Size(256, 256));
            //python程序里的warp_face_by_translation函数
            Cv2.InvertAffineTransform(affine_matrix, inv_affine_matrix);

            Mat[] bgrChannels = Cv2.Split(crop_img);
            for (int c = 0; c < 3; c++)
            {
                bgrChannels[c].ConvertTo(bgrChannels[c], MatType.CV_32FC1, 1 / 255.0);
            }

            Cv2.Merge(bgrChannels, crop_img);

            foreach (Mat channel in bgrChannels)
            {
                channel.Dispose();
            }

            input_image = Common.ExtractMat(crop_img);
            crop_img.Dispose();
        }

        internal List<Point2f> detect(Mat srcimg, Bbox bounding_box)
        {
            preprocess(srcimg, bounding_box);

            Tensor<float> input_tensor = new DenseTensor<float>(input_image, new[] { 1, 3, input_height, input_width });
            List<NamedOnnxValue> input_container = new List<NamedOnnxValue>
            {
                NamedOnnxValue.CreateFromTensor("input", input_tensor)
            };
            var ort_outputs = onnx_session.Run(input_container).ToArray();

            float[] pdata = ort_outputs[0].AsTensor<float>().ToArray(); //形状是(1, 68, 3), 每一行的长度是3,表示一个关键点坐标x,y和置信度
            int num_points = 68;
            List<Point2f> face_landmark_68 = new List<Point2f>();
            for (int i = 0; i < num_points; i++)
            {
                face_landmark_68.Add(new Point2f((float)(pdata[i * 3] / 64.0 * 256.0), (float)(pdata[i * 3 + 1] / 64.0 * 256.0)));
            }

            var face_landmark_68_Points = new Mat(face_landmark_68.Count, 1, MatType.CV_32FC2, face_landmark_68.ToArray());

            Mat face68landmarks_Points = new Mat();
            Cv2.Transform(face_landmark_68_Points, face68landmarks_Points, inv_affine_matrix);

            Point2f[] face68landmarks;
            face68landmarks_Points.GetArray<Point2f>(out face68landmarks);

            //python程序里的convert_face_landmark_68_to_5函数
            Point2f[] face_landmark_5of68 = new Point2f[5];
            float x = 0, y = 0;
            for (int i = 36; i < 42; i++) // left_eye
            {
                x += face68landmarks[i].X;
                y += face68landmarks[i].Y;
            }
            x /= 6;
            y /= 6;
            face_landmark_5of68[0] = new Point2f(x, y); // left_eye

            x = 0;
            y = 0;
            for (int i = 42; i < 48; i++) // right_eye
            {
                x += face68landmarks[i].X;
                y += face68landmarks[i].Y;
            }
            x /= 6;
            y /= 6;
            face_landmark_5of68[1] = new Point2f(x, y); // right_eye

            face_landmark_5of68[2] = face68landmarks[30]; // nose
            face_landmark_5of68[3] = face68landmarks[48]; // left_mouth_end
            face_landmark_5of68[4] = face68landmarks[54]; // right_mouth_end
            //python程序里的convert_face_landmark_68_to_5函数
            return face_landmark_5of68.ToList();
        }
    }
}

下载

源码下载

 

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

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

相关文章

【MATLAB源码-第36期】matlab基于BD,SVD,ZF,MMSE,MF,SLNR预编码的MIMO系统误码率分析。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. MIMO (多输入多输出)&#xff1a;这是一个无线通信系统中使用的技术&#xff0c;其中有多个发送和接收天线。通过同时发送和接收多个数据流&#xff0c;MIMO可以增加数据速率和系统容量&#xff0c;同时提高信号的可靠性。…

算法1: 素数个数统计

统计n以内的素数个数 素数&#xff1a;只能被1和自身整除的自然数&#xff0c;0和1除外&#xff1b; 举例&#xff1a; 输入&#xff1a;100 输出&#xff1a;25 import java.util.*; class Test1{public static void main(String[] args){int a 100; //输入数字//…

41、二叉树-二叉树的层序遍历

思路&#xff1a; 层序遍历就是从左到右依次遍历。这个时候就可以使用队列的方式。例如先把头节点入队&#xff0c;然后遍历开始&#xff0c;首先计算队列长度&#xff0c;第一层&#xff0c;长度为了&#xff0c;遍历一次&#xff0c;依次出队&#xff0c;头结点出队&#xff…

Redis的RedisObject和对外可见的5种数据结构

目录 RedisObject Redis的编码方式 对外可见的5种数据结构 1.string string结构的源码 为什么是小于44字节会采用embstr编码&#xff1f; embstr和raw区别 2.list list结构的源码 3.set set结构的源码 4.zset zset结构的源码 5.hash hash结构的源码 Redis中…

淘宝客订单产品设计:连接商家与推广者的智能桥梁

随着电商行业的迅速发展&#xff0c;淘宝客作为一种常见的推广方式&#xff0c;为商家引流、提升销量发挥了重要作用。而淘宝客订单产品的设计&#xff0c;则是连接商家与推广者的智能桥梁&#xff0c;本文将对其进行探讨与分析。 ### 1. 淘宝客订单产品的定义 淘宝客订单产品…

梯度提升树(Gradient Boosting Trees)

通过5个条件判定一件事情是否会发生&#xff0c;5个条件对这件事情是否发生的影响力不同&#xff0c;计算每个条件对这件事情发生的影响力多大&#xff0c;写一个梯度提升树&#xff08;Gradient Boosting Trees&#xff09;模型程序,最后打印5个条件分别的影响力。 示例一 梯…

【目标检测】Focal Loss

Focal Loss用来解决正负样本不平衡问题&#xff0c;并提升训练过程对困难样本的关注。 在一阶段目标检测算法中&#xff0c;以YOLO v3为例&#xff0c;计算置信度损失&#xff08;图中第3、4项&#xff09;时有目标的点少&#xff0c;无目标的点多&#xff0c;两者可能相差百倍…

WSL(Ubuntu)、PC物理机,linux开发板三个设备通讯,镜像模式

文章目录 一、前言二、使用2.1 需要的系统信息2.2 添加 .wslconfig 文件 三、如何从局域网访问WSL中的服务 一、前言 最近在使用Linux开发板的环境下&#xff0c;由于使用的 WSL的子系统&#xff0c;并不是虚拟机&#xff0c;导致 网络传输 这方面不是很方便&#xff0c;由于 W…

AGM AG32 MCU在汽车UWB应用方案

AG32的汽车UWB应用方案 汽车电子产品的日益成熟&#xff0c;包括ADAS和车载信息娱乐&#xff0c;正在推动对CPLD的需求。例如&#xff0c;利用安装在车上的各种传感器&#xff08;如雷达、摄像头和激光雷达等&#xff09;来感知周围环境&#xff0c;实现实时监测和数据处理。这…

docker容器技术篇:数据卷的常用操作

Docker数据卷的使用 在docker中&#xff0c;为了方便查看容器内产生的数据或者将多个容器中的数据实现共享&#xff0c;就涉及到容器数据卷管理&#xff0c;那什么是数据卷呢&#xff0c;往下看&#xff01;&#xff01;&#xff01; 1 数据卷概念 数据卷是一个共给容器使用…

一款挺不错网站维护页面HTML源码

一款挺不错网站维护页面源码&#xff0c;单HTML不需要数据库&#xff0c;上传到你的虚拟机就可以用做维护页面还不错&#xff0c;用处多。。 源码下载 一款挺不错网站维护页面源码

C# - 反射动态添加/删除Attribute特性

API: TypeDescriptor.AddAttributes TypeDescriptor.GetAttributes 注意&#xff1a;TypeDescriptor.AddAttributes添加的特性需要使用 TypeDescriptor.GetAttributes获取 根据api可以看到&#xff0c;该接口不仅可以给指定类&#xff08;Type&#xff09;添加特性&#xf…

设计模式——模版模式21

模板方法模式在超类中定义了一个事务流程的框架&#xff0c; 允许子类在不修改结构的情况下重写其中一个或者多个特定步骤。下面以ggbond的校招礼盒发放为例。 设计模式&#xff0c;一定要敲代码理解 模版抽象 /*** author ggbond* date 2024年04月18日 17:32* 发送奖品*/ p…

华为框式交换机S12700E系列配置CSS集群

搭建集群环境 a.为两台交换机上电&#xff0c;按照数据规划分别对两台框式交换机进行配置 <HUAWEI> system-view [HUAWEI] sysname Switch1 [Switch1] set css id 1 [Switch1] set css priority 150 //框1的集群优先级配置为150 [Switch1] interface css-port 1 [Sw…

后端-MySQL-week11 多表查询

tips: distinct————紧跟“select”之后&#xff0c;用于去重 多表查询 概述 一对多&#xff08;多对一&#xff09; 多对多 一对一 多表查询概述 分类 连接查询 内连接 外连接 自连接 必须起别名&#xff01; 联合查询-union&#xff0c;union all 子查询 概念 分类 …

家庭营销广告Criteo公司首次获得MRC零售媒体测量认证

家庭营销广告Criteo公司首次获得零售媒体测量MRC认证 商业媒体公司Criteo2024年3月28日宣布&#xff0c;它首次获得媒体评级委员会&#xff08;MRC&#xff09;的认证&#xff0c;在其企业零售媒体平台commerce Max和commerce Yield上&#xff0c;在桌面、移动网络和移动应用内…

Goland远程连接Linux进行项目开发

文章目录 1、Linux上安装go的环境&#xff12;、配置远程连接3、其他配置入口 跑新项目&#xff0c;有个confluent-Kafka-go的依赖在Windows上编译不通过&#xff0c;报错信息&#xff1a; undefined reference to __imp__xxx似乎是这个依赖在Windows上不支持&#xff0c;选择让…

阿里云ECS迁移至AWS EC2,九河云详细教程

在客户在求更大的海外市场&#xff0c;综合考虑后决定选择AWS云&#xff0c;但对迁移方面不太了解&#xff0c;甚至比较担心如果到AWS云是否业务要从0开始&#xff1f;本文九河云将为您介绍如何将阿里云ECS平滑迁移至AWS。 工具介绍 AWS Application Migration Service &…

pytorch-手写数字识别之全连接层实现

目录 1. 背景2. nn.Linear线性层2. 实现MLP网络3. train4. 完整代码 1. 背景 上一篇https://blog.csdn.net/wyw0000/article/details/137622977?spm1001.2014.3001.5502中实现手撸代码的方式实现了手写数字识别&#xff0c;本文将使用pytorch的API实现。 2. nn.Linear线性层…

安卓xml存储读取和sharedpreferences文件存储读取

起因今天有人问到我 xml文件存储读取和sharedpreferences读写该咋做&#xff0c;能不能帮忙写个案例&#xff0c;这里我简单写出一个案例&#xff0c;一下是全部的代码 一、首先引入 权限 <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE&q…