C#版Facefusion:让你的脸与世界融为一体!-01 人脸检测
目录
说明
效果
模型信息
项目
代码
下载
说明
C#版Facefusion一共有如下5个步骤:
1、使用yoloface_8n.onnx进行人脸检测
2、使用2dfan4.onnx获取人脸关键点
3、使用arcface_w600k_r50.onnx获取人脸特征值
4、使用inswapper_128.onnx进行人脸交换
5、使用gfpgan_1.4.onnx进行人脸增强
本文分享使用yoloface_8n.onnx实现C#版Facefusion第一步:人脸检测。
效果
模型信息
Model Properties
-------------------------
date:2024-01-13T23:32:01.523479
description:Ultralytics YOLOv8n-pose model trained on /ssd2t/derron/ultralytics/ultralytics/datasets/widerface.yaml
author:Ultralytics
kpt_shape:[5, 3]
task:pose
license:AGPL-3.0 https://ultralytics.com/license
version:8.1.0
stride:32
batch:1
imgsz:[640, 640]
names:{0: 'face'}
---------------------------------------------------------------
Inputs
-------------------------
name:images
tensor:Float[1, 3, 640, 640]
---------------------------------------------------------------
Outputs
-------------------------
name:output0
tensor:Float[1, 20, 8400]
---------------------------------------------------------------
项目
代码
Form1.cs
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace FaceFusionSharp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
string source_path = "";
string target_path = "";
Yolov8Face detect_face;
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)
{
return;
}
button1.Enabled = false;
Application.DoEvents();
Mat source_img = Cv2.ImRead(source_path);
Mat target_img = Cv2.ImRead(target_path);
List<Bbox> boxes;
boxes = detect_face.detect(source_img);
if (boxes.Count == 0)
{
MessageBox.Show("Source中未检测到人脸!");
button1.Enabled = true;
return;
}
//绘图
Cv2.Rectangle(source_img, new OpenCvSharp.Point(boxes[0].xmin, boxes[0].ymin), new OpenCvSharp.Point(boxes[0].xmax, boxes[0].ymax), new Scalar(255, 0, 0), 2);
//显示
pictureBox1.Image = source_img.ToBitmap();
if (pictureBox2.Image == null)
{
return;
}
boxes = detect_face.detect(target_img);
if (boxes.Count == 0)
{
MessageBox.Show("target_img中未检测到人脸!");
button1.Enabled = true;
return;
}
//绘图
Cv2.Rectangle(target_img, new OpenCvSharp.Point(boxes[0].xmin, boxes[0].ymin), new OpenCvSharp.Point(boxes[0].xmax, boxes[0].ymax), new Scalar(255, 0, 0), 2);
//显示
pictureBox2.Image = target_img.ToBitmap();
}
private void Form1_Load(object sender, EventArgs e)
{
detect_face = new Yolov8Face("model/yoloface_8n.onnx");
target_path = "images/target.jpg";
source_path = "images/source.jpg";
pictureBox1.Image = new Bitmap(source_path);
pictureBox2.Image = new Bitmap(target_path);
}
}
}
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace FaceFusionSharp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
string source_path = "";
string target_path = "";
Yolov8Face detect_face;
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)
{
return;
}
button1.Enabled = false;
Application.DoEvents();
Mat source_img = Cv2.ImRead(source_path);
Mat target_img = Cv2.ImRead(target_path);
List<Bbox> boxes;
boxes = detect_face.detect(source_img);
if (boxes.Count == 0)
{
MessageBox.Show("Source中未检测到人脸!");
button1.Enabled = true;
return;
}
//绘图
Cv2.Rectangle(source_img, new OpenCvSharp.Point(boxes[0].xmin, boxes[0].ymin), new OpenCvSharp.Point(boxes[0].xmax, boxes[0].ymax), new Scalar(255, 0, 0), 2);
//显示
pictureBox1.Image = source_img.ToBitmap();
if (pictureBox2.Image == null)
{
return;
}
boxes = detect_face.detect(target_img);
if (boxes.Count == 0)
{
MessageBox.Show("target_img中未检测到人脸!");
button1.Enabled = true;
return;
}
//绘图
Cv2.Rectangle(target_img, new OpenCvSharp.Point(boxes[0].xmin, boxes[0].ymin), new OpenCvSharp.Point(boxes[0].xmax, boxes[0].ymax), new Scalar(255, 0, 0), 2);
//显示
pictureBox2.Image = target_img.ToBitmap();
}
private void Form1_Load(object sender, EventArgs e)
{
detect_face = new Yolov8Face("model/yoloface_8n.onnx");
target_path = "images/target.jpg";
source_path = "images/source.jpg";
pictureBox1.Image = new Bitmap(source_path);
pictureBox2.Image = new Bitmap(target_path);
}
}
}
Yolov8Face.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 Yolov8Face
{
float[] input_image;
int input_height;
int input_width;
float ratio_height;
float ratio_width;
float conf_threshold;
float iou_threshold;
SessionOptions options;
InferenceSession onnx_session;
public Yolov8Face(string modelpath, float conf_thres = 0.5f, float iou_thresh = 0.4f)
{
options = new SessionOptions();
options.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;
options.AppendExecutionProvider_CPU(0);
// 创建推理模型类,读取本地模型文件
onnx_session = new InferenceSession(modelpath, options);
this.input_height = 640;
this.input_width = 640;
conf_threshold = conf_thres;
iou_threshold = iou_thresh;
}
void preprocess(Mat srcimg)
{
int height = srcimg.Rows;
int width = srcimg.Cols;
Mat temp_image = srcimg.Clone();
if (height > input_height || width > input_width)
{
float scale = Math.Min((float)input_height / height, (float)input_width / width);
Size new_size = new Size((int)(width * scale), (int)(height * scale));
Cv2.Resize(srcimg, temp_image, new_size);
}
ratio_height = (float)height / temp_image.Rows;
ratio_width = (float)width / temp_image.Cols;
Mat input_img = new Mat();
Cv2.CopyMakeBorder(temp_image, input_img, 0, input_height - temp_image.Rows, 0, input_width - temp_image.Cols, BorderTypes.Constant, 0);
Mat[] bgrChannels = Cv2.Split(input_img);
for (int c = 0; c < 3; c++)
{
bgrChannels[c].ConvertTo(bgrChannels[c], MatType.CV_32FC1, 1 / 128.0, -127.5 / 128.0);
}
Cv2.Merge(bgrChannels, input_img);
foreach (Mat channel in bgrChannels)
{
channel.Dispose();
}
input_image = Common.ExtractMat(input_img);
input_img.Dispose();
}
public List<Bbox> detect(Mat srcimg)
{
preprocess(srcimg);
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("images", input_tensor)
};
var ort_outputs = onnx_session.Run(input_container).ToArray();
// 形状是(1, 20, 8400),不考虑第0维batchsize,每一列的长度20,前4个元素是检测框坐标(cx,cy,w,h),第4个元素是置信度,剩下的15个元素是5个关键点坐标x,y和置信度
float[] pdata = ort_outputs[0].AsTensor<float>().ToArray();
int num_box = 8400;
List<Bbox> bounding_box_raw = new List<Bbox>();
List<float> score_raw = new List<float>();
for (int i = 0; i < num_box; i++)
{
float score = pdata[4 * num_box + i];
if (score > conf_threshold)
{
float xmin = (float)((pdata[i] - 0.5 * pdata[2 * num_box + i]) * ratio_width); //(cx,cy,w,h)转到(x,y,w,h)并还原到原图
float ymin = (float)((pdata[num_box + i] - 0.5 * pdata[3 * num_box + i]) * ratio_height); //(cx,cy,w,h)转到(x,y,w,h)并还原到原图
float xmax = (float)((pdata[i] + 0.5 * pdata[2 * num_box + i]) * ratio_width); //(cx,cy,w,h)转到(x,y,w,h)并还原到原图
float ymax = (float)((pdata[num_box + i] + 0.5 * pdata[3 * num_box + i]) * ratio_height); //(cx,cy,w,h)转到(x,y,w,h)并还原到原图
//坐标的越界检查保护,可以添加一下
bounding_box_raw.Add(new Bbox(xmin, ymin, xmax, ymax));
score_raw.Add(score);
//剩下的5个关键点坐标的计算,暂时不写,因为在下游的模块里没有用到5个关键点坐标信息
}
}
List<int> keep_inds = Common.nms(bounding_box_raw, score_raw, iou_threshold);
int keep_num = keep_inds.Count();
List<Bbox> boxes = new List<Bbox>();
for (int i = 0; i < keep_num; i++)
{
int ind = keep_inds[i];
boxes.Add(bounding_box_raw[ind]);
}
return boxes;
}
}
}
下载
源码下载