人脸
This commit is contained in:
parent
9f56229c59
commit
c1c7570e8e
@ -43,7 +43,8 @@ public partial class App : Application
|
|||||||
services.AddDbContext<AppDbContext>(options =>
|
services.AddDbContext<AppDbContext>(options =>
|
||||||
options.UseSqlite(connectionString));
|
options.UseSqlite(connectionString));
|
||||||
|
|
||||||
services.AddSingleton<MainWindow>();
|
//services.AddSingleton<MainWindow>();
|
||||||
|
services.AddSingleton<Main>();
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
@ -57,8 +58,16 @@ public partial class App : Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 用 DI 创建窗口
|
// 用 DI 创建窗口
|
||||||
var mainWindow = AppHost.Services.GetRequiredService<MainWindow>();
|
|
||||||
mainWindow.Show();
|
//var mainWindow = AppHost.Services.GetRequiredService<MainWindow>();
|
||||||
|
//mainWindow.Show();
|
||||||
|
|
||||||
|
var mainWin = AppHost.Services.GetRequiredService<Main>();
|
||||||
|
Current.MainWindow = mainWin;
|
||||||
|
mainWin.Show();
|
||||||
|
|
||||||
|
var newPage = new Home();
|
||||||
|
mainWin.SwitchPage(newPage, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
Wpf_AiSportsMicrospace/Common/AppSettings.cs
Normal file
12
Wpf_AiSportsMicrospace/Common/AppSettings.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common
|
||||||
|
{
|
||||||
|
public static class AppSettings
|
||||||
|
{
|
||||||
|
public static string SchoolCode = "202501060001";
|
||||||
|
public static string BackendUrl = "http://localhost:9992/api/StudentFace";
|
||||||
|
}
|
||||||
|
}
|
||||||
67
Wpf_AiSportsMicrospace/Common/HttpManager.cs
Normal file
67
Wpf_AiSportsMicrospace/Common/HttpManager.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Wpf_AiSportsMicrospace.Common
|
||||||
|
{
|
||||||
|
public class HttpManager
|
||||||
|
{
|
||||||
|
public static async Task<string> HttpPostAsync(string url, string postData = null, string contentType = "application/json", int timeOut = 30, Dictionary<string, string> headers = null)
|
||||||
|
{
|
||||||
|
using (var client = new HttpClient())
|
||||||
|
{
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(timeOut);
|
||||||
|
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpContent content = null;
|
||||||
|
if (!string.IsNullOrEmpty(postData))
|
||||||
|
{
|
||||||
|
content = new StringContent(postData, Encoding.UTF8, contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await client.PostAsync(url, content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
return await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static async Task<string> HttpGetAsync(string url, Dictionary<string, string> headers = null)
|
||||||
|
{
|
||||||
|
using (var client = new HttpClient())
|
||||||
|
{
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
return await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -56,7 +56,7 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_webcamClient = WebcamClient.CreateRTSP("172.17.30.65", "admin", "yd708090", 554u);
|
_webcamClient = WebcamClient.CreateRTSP("172.17.30.64", "admin", "yd708090", 554u);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,15 +1,22 @@
|
|||||||
using System;
|
using Common;
|
||||||
using System.Collections.Generic;
|
using Dto;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using WpfAnimatedGif;
|
using WpfAnimatedGif;
|
||||||
|
using Color = System.Windows.Media.Color;
|
||||||
|
using SharpImage = SixLabors.ImageSharp;
|
||||||
|
|
||||||
namespace Wpf_AiSportsMicrospace.Common
|
namespace Wpf_AiSportsMicrospace.Common
|
||||||
{
|
{
|
||||||
@ -96,7 +103,6 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//public static Uri countDownGif = new Uri("/Resources/Img/Album/1.gif", UriKind.Relative);
|
//public static Uri countDownGif = new Uri("/Resources/Img/Album/1.gif", UriKind.Relative);
|
||||||
|
|
||||||
//倒计时动画
|
//倒计时动画
|
||||||
@ -151,7 +157,6 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//预加载资源
|
//预加载资源
|
||||||
private static readonly Dictionary<string, BitmapImage> _cache = new Dictionary<string, BitmapImage>();
|
private static readonly Dictionary<string, BitmapImage> _cache = new Dictionary<string, BitmapImage>();
|
||||||
private static bool _isPreloaded = false;
|
private static bool _isPreloaded = false;
|
||||||
@ -208,5 +213,87 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
LoadGifToCache(relativePath);
|
LoadGifToCache(relativePath);
|
||||||
return _cache.GetValueOrDefault(relativePath);
|
return _cache.GetValueOrDefault(relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<List<StudentInfoDto>> FacialRecognition(byte[] buffer, double[] xNorms)
|
||||||
|
{
|
||||||
|
if (xNorms == null || xNorms.Length < 2)
|
||||||
|
throw new ArgumentException("xNorms 至少需要两个元素");
|
||||||
|
|
||||||
|
using var image = SharpImage.Image.Load<Rgba32>(buffer);
|
||||||
|
|
||||||
|
int imgWidth = image.Width;
|
||||||
|
int imgHeight = image.Height;
|
||||||
|
int rectHeight = imgHeight / 2;
|
||||||
|
|
||||||
|
// 计算分割边界
|
||||||
|
var sortedX = xNorms.OrderBy(x => x).ToList();
|
||||||
|
var splitXs = new List<double>();
|
||||||
|
for (int i = 0; i < sortedX.Count - 1; i++)
|
||||||
|
splitXs.Add((sortedX[i] + sortedX[i + 1]) / 2.0);
|
||||||
|
splitXs.Insert(0, 0.0);
|
||||||
|
splitXs.Add(1.0);
|
||||||
|
|
||||||
|
var studentInfos = new List<StudentInfoDto>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 6 && i < splitXs.Count - 1; i++)
|
||||||
|
{
|
||||||
|
int x1 = (int)(splitXs[i] * imgWidth);
|
||||||
|
int x2 = (int)(splitXs[i + 1] * imgWidth);
|
||||||
|
int w = x2 - x1;
|
||||||
|
|
||||||
|
var cropRect = new Rectangle(x1, 0, w, rectHeight);
|
||||||
|
|
||||||
|
using var cropped = image.Clone(ctx => ctx.Crop(cropRect));
|
||||||
|
|
||||||
|
// 转为 Base64
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
cropped.Save(ms, new JpegEncoder());
|
||||||
|
string base64 = Convert.ToBase64String(ms.ToArray());
|
||||||
|
|
||||||
|
// 调用人脸识别方法(假设返回姓名+头像Base64)
|
||||||
|
StudentInfoDto studentInfo = await FaceRecognitionAsync(base64);
|
||||||
|
studentInfos.Add(studentInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return studentInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调用人脸识别接口,返回学生信息
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<StudentInfoDto> FaceRecognitionAsync(string base64Image)
|
||||||
|
{
|
||||||
|
var param = new
|
||||||
|
{
|
||||||
|
AppSettings.SchoolCode,
|
||||||
|
Base64 = base64Image
|
||||||
|
};
|
||||||
|
|
||||||
|
string json = JsonSerializer.Serialize(param);
|
||||||
|
|
||||||
|
// 调用 HttpPostAsync
|
||||||
|
string response = await HttpManager.HttpPostAsync(AppSettings.BackendUrl, json);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(response))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 反序列化为接口返回对象
|
||||||
|
var faceInfo = JsonSerializer.Deserialize<StudentInfoDto>(response, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (faceInfo == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return faceInfo;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
Wpf_AiSportsMicrospace/Dto/StudentInfoDto.cs
Normal file
21
Wpf_AiSportsMicrospace/Dto/StudentInfoDto.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Dto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 学生信息
|
||||||
|
/// </summary>
|
||||||
|
public class StudentInfoDto
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Photo { get; set; }
|
||||||
|
public string StudentNo { get; set; }
|
||||||
|
public string SchoolCode { get; set; }
|
||||||
|
public string GradeName { get; set; }
|
||||||
|
public string ClassName { get; set; }
|
||||||
|
public int Age { get; set; }
|
||||||
|
public string Sex { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -70,6 +70,12 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
}
|
}
|
||||||
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
// 绑定人脸识别事件
|
||||||
|
_mainWin.FacialRecognitionEvent += async (sender, buffer) =>
|
||||||
|
{
|
||||||
|
var studentList = await Utils.FacialRecognition(buffer, new double[] { 0.50, 0.78 });
|
||||||
|
};
|
||||||
|
|
||||||
DrawCirclesWithText();
|
DrawCirclesWithText();
|
||||||
|
|
||||||
// 播放音乐
|
// 播放音乐
|
||||||
|
|||||||
@ -1,24 +1,20 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Data;
|
|
||||||
using System.Windows.Documents;
|
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Media.Media3D;
|
|
||||||
using Wpf_AiSportsMicrospace.Common;
|
using Wpf_AiSportsMicrospace.Common;
|
||||||
using WpfAnimatedGif;
|
using WpfAnimatedGif;
|
||||||
using Yztob.AiSports.Inferences.Abstractions;
|
using Yztob.AiSports.Inferences.Abstractions;
|
||||||
using Yztob.AiSports.Inferences.Things;
|
using Yztob.AiSports.Inferences.Things;
|
||||||
using Yztob.AiSports.Sensors.Abstractions;
|
using Yztob.AiSports.Sensors.Abstractions;
|
||||||
using Yztob.AiSports.Sensors.Things;
|
using Yztob.AiSports.Sensors.Things;
|
||||||
|
using Brushes = System.Windows.Media.Brushes;
|
||||||
|
|
||||||
namespace Wpf_AiSportsMicrospace.Views
|
namespace Wpf_AiSportsMicrospace.Views
|
||||||
{
|
{
|
||||||
@ -41,6 +37,9 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
private bool _isReconnecting = false;
|
private bool _isReconnecting = false;
|
||||||
private readonly object _reconnectLock = new();
|
private readonly object _reconnectLock = new();
|
||||||
|
|
||||||
|
public event EventHandler<List<Human>> HumanFrameUpdated;
|
||||||
|
public event EventHandler<Byte[]> FacialRecognitionEvent;
|
||||||
|
|
||||||
public Main()
|
public Main()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -108,6 +107,13 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
// return;
|
// return;
|
||||||
|
|
||||||
var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
|
var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
|
||||||
|
|
||||||
|
// 触发全局事件
|
||||||
|
Application.Current.Dispatcher.BeginInvoke(() =>
|
||||||
|
{
|
||||||
|
FacialRecognitionEvent?.Invoke(this, buffer);
|
||||||
|
});
|
||||||
|
|
||||||
var humanResult = _humanPredictor.Predicting(buffer, frame.Number);
|
var humanResult = _humanPredictor.Predicting(buffer, frame.Number);
|
||||||
|
|
||||||
var humans = humanResult?.Humans?.ToList();
|
var humans = humanResult?.Humans?.ToList();
|
||||||
@ -126,8 +132,6 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<List<Human>> HumanFrameUpdated;
|
|
||||||
|
|
||||||
private void StartHeartbeatMonitor()
|
private void StartHeartbeatMonitor()
|
||||||
{
|
{
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
@ -237,7 +241,7 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 强制WPF加载所有资源
|
// 强制WPF加载所有资源
|
||||||
page.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
page.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||||
page.Arrange(new Rect(0, 0, page.DesiredSize.Width, page.DesiredSize.Height));
|
page.Arrange(new Rect(0, 0, page.DesiredSize.Width, page.DesiredSize.Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +254,7 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
transformGroup.Children.Add(scale);
|
transformGroup.Children.Add(scale);
|
||||||
transformGroup.Children.Add(skew);
|
transformGroup.Children.Add(skew);
|
||||||
transformGroup.Children.Add(translate);
|
transformGroup.Children.Add(translate);
|
||||||
newPage.RenderTransformOrigin = new Point(fromRight ? 0 : 1, 0.5);
|
newPage.RenderTransformOrigin = new System.Windows.Point(fromRight ? 0 : 1, 0.5);
|
||||||
newPage.RenderTransform = transformGroup;
|
newPage.RenderTransform = transformGroup;
|
||||||
|
|
||||||
// 动画
|
// 动画
|
||||||
@ -274,7 +278,7 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
PreloadPageResources(newPage);
|
PreloadPageResources(newPage);
|
||||||
|
|
||||||
// 每次都创建新的 Image 实例
|
// 每次都创建新的 Image 实例
|
||||||
var maskTop = new Image
|
var maskTop = new System.Windows.Controls.Image
|
||||||
{
|
{
|
||||||
Source = new BitmapImage(new Uri("/Resources/Img/gif/top_animation_image.png", UriKind.Relative)),
|
Source = new BitmapImage(new Uri("/Resources/Img/gif/top_animation_image.png", UriKind.Relative)),
|
||||||
Stretch = Stretch.Fill,
|
Stretch = Stretch.Fill,
|
||||||
@ -283,7 +287,7 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
Width = 1920,
|
Width = 1920,
|
||||||
Height = 1000
|
Height = 1000
|
||||||
};
|
};
|
||||||
var maskBottom = new Image
|
var maskBottom = new System.Windows.Controls.Image
|
||||||
{
|
{
|
||||||
Source = new BitmapImage(new Uri("/Resources/Img/gif/bottom_animation_image.png", UriKind.Relative)),
|
Source = new BitmapImage(new Uri("/Resources/Img/gif/bottom_animation_image.png", UriKind.Relative)),
|
||||||
Stretch = Stretch.Fill,
|
Stretch = Stretch.Fill,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user