update:本地化&人脸
This commit is contained in:
commit
c7979ae8f9
@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AiSportsMicrospaceDB.DBContext
|
||||
@ -15,25 +16,9 @@ namespace AiSportsMicrospaceDB.DBContext
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<SixGroupPoints>().HasData(
|
||||
new SixGroupPoints
|
||||
{
|
||||
Id = 1,
|
||||
points = [
|
||||
(0.21, 0.88 ),
|
||||
(0.36, 0.58 ),
|
||||
(0.50, 0.88),
|
||||
(0.64, 0.58 ),
|
||||
(0.78, 0.88),
|
||||
(0.92, 0.58 )
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
public DbSet<BasicConfig> BasicConfig { get; set; }
|
||||
|
||||
public DbSet<SixGroupPoints> SixGroupPoints { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,10 +15,4 @@ namespace AiSportsMicrospaceDB.Entities
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public class SixGroupPoints {
|
||||
// ✅ 主键
|
||||
public int Id { get; set; }
|
||||
public List<(double,double)> points { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
16
AiSportsMicrospaceDB/Entities/Point.cs
Normal file
16
AiSportsMicrospaceDB/Entities/Point.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AiSportsMicrospaceDB.Entities
|
||||
{
|
||||
[Owned]
|
||||
public class Point
|
||||
{
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
}
|
||||
}
|
||||
23
AiSportsMicrospaceDB/Entities/SixGroupPoints.cs
Normal file
23
AiSportsMicrospaceDB/Entities/SixGroupPoints.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AiSportsMicrospaceDB.Entities
|
||||
{
|
||||
|
||||
public class SixGroupPoints {
|
||||
// ✅ 主键
|
||||
public int Id { get; set; }
|
||||
public Point point1 { get; set; }
|
||||
public Point point2 { get; set; }
|
||||
public Point point3 { get; set; }
|
||||
public Point point4 { get; set; }
|
||||
public Point point5 { get; set; }
|
||||
public Point point6 { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -7,6 +7,6 @@ namespace Common
|
||||
public static class AppSettings
|
||||
{
|
||||
public static string SchoolCode = "202501060001";
|
||||
public static string BackendUrl = "http://localhost:9992/api/StudentFace";
|
||||
public static string BackendUrl = "http://localhost:9991/api/StudentFace";
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ 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)
|
||||
public static string HttpPost(string url, string postData = null, string contentType = "application/json", int timeOut = 30, Dictionary<string, string> headers = null)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
@ -27,9 +27,10 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
|
||||
try
|
||||
{
|
||||
var response = await client.PostAsync(url, content);
|
||||
// 注意:要调用 PostAsync 而不是 po()
|
||||
var response = client.PostAsync(url, content).Result; // 同步等待
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
return response.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -37,6 +38,7 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> HttpGetAsync(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
|
||||
@ -214,10 +214,10 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
return _cache.GetValueOrDefault(relativePath);
|
||||
}
|
||||
|
||||
public static async Task<List<StudentInfoDto>> FacialRecognition(byte[] buffer, double[] xNorms)
|
||||
public static List<StudentInfoDto> FacialRecognition(byte[] buffer, double[] xNorms)
|
||||
{
|
||||
if (xNorms == null || xNorms.Length < 2)
|
||||
throw new ArgumentException("xNorms 至少需要两个元素");
|
||||
if (xNorms == null || xNorms.Length == 0)
|
||||
throw new ArgumentException("xNorms 不能为空");
|
||||
|
||||
using var image = SharpImage.Image.Load<Rgba32>(buffer);
|
||||
|
||||
@ -225,43 +225,66 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
int imgHeight = image.Height;
|
||||
int rectHeight = imgHeight / 2;
|
||||
|
||||
// 计算分割边界
|
||||
// 1. 排序中心点
|
||||
var sortedX = xNorms.OrderBy(x => x).ToList();
|
||||
int faceCount = sortedX.Count;
|
||||
|
||||
// 2. 计算分割边界
|
||||
var splitXs = new List<double>();
|
||||
for (int i = 0; i < sortedX.Count - 1; i++)
|
||||
splitXs.Add((sortedX[i] + sortedX[i + 1]) / 2.0);
|
||||
|
||||
// 3. 起止边界
|
||||
splitXs.Insert(0, 0.0);
|
||||
splitXs.Add(1.0);
|
||||
|
||||
// 4. 输出目录
|
||||
string imgDir = Path.Combine(AppContext.BaseDirectory, "img");
|
||||
Directory.CreateDirectory(imgDir);
|
||||
|
||||
var studentInfos = new List<StudentInfoDto>();
|
||||
|
||||
for (int i = 0; i < 6 && i < splitXs.Count - 1; i++)
|
||||
// 5. 循环裁剪 + 同步识别
|
||||
for (int i = 0; i < faceCount; i++)
|
||||
{
|
||||
int x1 = (int)(splitXs[i] * imgWidth);
|
||||
int x2 = (int)(splitXs[i + 1] * imgWidth);
|
||||
int w = x2 - x1;
|
||||
double xStart = splitXs[i];
|
||||
double xEnd = splitXs[i + 1];
|
||||
|
||||
int x1 = (int)(xStart * imgWidth);
|
||||
int x2 = (int)(xEnd * imgWidth);
|
||||
int w = Math.Max(1, 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());
|
||||
ms.Position = 0;
|
||||
|
||||
string imgPath = Path.Combine(imgDir, $"crop_{i + 1}_{DateTime.Now:HHmmssfff}.jpg");
|
||||
File.WriteAllBytes(imgPath, ms.ToArray());
|
||||
|
||||
string base64 = Convert.ToBase64String(ms.ToArray());
|
||||
|
||||
// 调用人脸识别方法(假设返回姓名+头像Base64)
|
||||
StudentInfoDto studentInfo = await FaceRecognitionAsync(base64);
|
||||
studentInfos.Add(studentInfo);
|
||||
Console.WriteLine($"[{i + 1}] 范围: {xStart:0.00}-{xEnd:0.00}, 宽: {w}");
|
||||
|
||||
// === 同步调用人脸识别接口 ===
|
||||
var result = FaceRecognitionAsync(base64);
|
||||
|
||||
if (result != null)
|
||||
studentInfos.Add(result);
|
||||
}
|
||||
|
||||
return studentInfos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 调用人脸识别接口,返回学生信息
|
||||
/// </summary>
|
||||
public static async Task<StudentInfoDto> FaceRecognitionAsync(string base64Image)
|
||||
public static StudentInfoDto FaceRecognitionAsync(string base64Image)
|
||||
{
|
||||
var param = new
|
||||
{
|
||||
@ -272,23 +295,26 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
string json = JsonSerializer.Serialize(param);
|
||||
|
||||
// 调用 HttpPostAsync
|
||||
string response = await HttpManager.HttpPostAsync(AppSettings.BackendUrl, json);
|
||||
string response = HttpManager.HttpPost(AppSettings.BackendUrl, json);
|
||||
|
||||
if (string.IsNullOrEmpty(response))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// 反序列化为接口返回对象
|
||||
var faceInfo = JsonSerializer.Deserialize<StudentInfoDto>(response, new JsonSerializerOptions
|
||||
// 假设 response 是 JSON 字符串
|
||||
var apiResponse = JsonSerializer.Deserialize<ApiResponse<StudentInfoDto>>(response, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
|
||||
if (faceInfo == null)
|
||||
// 取 data
|
||||
var studentInfo = apiResponse.Data;
|
||||
|
||||
if (studentInfo == null)
|
||||
return null;
|
||||
|
||||
return faceInfo;
|
||||
return studentInfo;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
12
Wpf_AiSportsMicrospace/Dto/ApiResponse.cs
Normal file
12
Wpf_AiSportsMicrospace/Dto/ApiResponse.cs
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
namespace Dto
|
||||
{
|
||||
public class ApiResponse<T>
|
||||
{
|
||||
public bool Status { get; set; }
|
||||
public int Code { get; set; }
|
||||
public string Message { get; set; }
|
||||
public T Data { get; set; }
|
||||
}
|
||||
}
|
||||
@ -67,6 +67,12 @@ namespace Dto
|
||||
Sports = new List<SportBase>();
|
||||
}
|
||||
|
||||
// 更新点位方法
|
||||
public void UpdateCirclePositions(List<(double XNorm, double YNorm)> newPositions)
|
||||
{
|
||||
CirclePositions = newPositions;
|
||||
}
|
||||
|
||||
// 更新排行榜方法
|
||||
public List<RankItem> UpdateRankList()
|
||||
{
|
||||
|
||||
@ -9,13 +9,14 @@ namespace Dto
|
||||
/// </summary>
|
||||
public class StudentInfoDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string StudentName { get; set; }
|
||||
public string Photo { get; set; }
|
||||
public string StudentNo { get; set; }
|
||||
public string SchoolCode { get; set; }
|
||||
public string StudentCode { get; set; }
|
||||
public int GradeId { get; set; }
|
||||
public int ClassId { get; set; }
|
||||
public string GradeName { get; set; }
|
||||
public string ClassName { get; set; }
|
||||
public int Age { get; set; }
|
||||
public string Sex { get; set; }
|
||||
public int Sex { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Wpf_AiSportsMicrospace/Resources/Img/gif/avatar.png
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Img/gif/avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
@ -3,6 +3,8 @@ using Dto;
|
||||
using Emgu.CV.Flann;
|
||||
using Enum;
|
||||
using HandyControl.Controls;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SharpDX.Direct3D9;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@ -60,36 +62,31 @@ namespace Wpf_AiSportsMicrospace.Views
|
||||
private readonly object _updateLock = new object();
|
||||
private readonly Dictionary<int, (string lastNumber, DateTime lastChangeTime, string currentState)> _jumpStatus = new Dictionary<int, (string, DateTime, string)>();
|
||||
private GroupJumpRopeContext _groupJumpRopeContext;
|
||||
public GroupJumpRope()
|
||||
public GroupJumpRope()
|
||||
{
|
||||
InitializeComponent();
|
||||
_detectQueue = new SportDetectionQueue();
|
||||
Loaded += UserControl_Loaded;
|
||||
Unloaded += UserControl_Unloaded;
|
||||
_groupJumpRopeContext = new GroupJumpRopeContext();
|
||||
|
||||
// 将原来的赋值方式:
|
||||
//_groupJumpRopeContext.CirclePositions = config != null ? config.sixJumpPoints : _groupJumpRopeContext.CirclePositions;
|
||||
|
||||
// 修改为:先清空再添加元素(因为 CirclePositions 只有 get,没有 set)
|
||||
//_groupJumpRopeContext.CirclePositions.Clear();
|
||||
//if (config != null && config.sixJumpPoints != null)
|
||||
//{
|
||||
// foreach (var pt in config.sixJumpPoints)
|
||||
// {
|
||||
// _groupJumpRopeContext.CirclePositions.Add(pt);
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
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 });
|
||||
};
|
||||
|
||||
var app = (App)Application.Current;
|
||||
using var scope = App.AppHost.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var cfg = await db.SixGroupPoints.FirstOrDefaultAsync();
|
||||
//var pointlist = JsonSerializer.Deserialize<List<(double, double)>>(cfg!.PointsJson);
|
||||
|
||||
List<(double XNorm, double YNorm)> pointlist =
|
||||
[(cfg.point1.X, cfg.point1.Y),(cfg.point2.X, cfg.point2.Y),
|
||||
(cfg.point3.X, cfg.point3.Y), (cfg.point4.X, cfg.point4.Y),
|
||||
(cfg.point5.X, cfg.point5.Y),(cfg.point6.X, cfg.point6.Y)];
|
||||
_groupJumpRopeContext.CirclePositions = pointlist;
|
||||
|
||||
|
||||
DrawCirclesWithText();
|
||||
|
||||
@ -132,7 +129,7 @@ namespace Wpf_AiSportsMicrospace.Views
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Opacity = 0,
|
||||
Margin = new Thickness(0, -100, 0, 0),
|
||||
Margin = new Thickness(0, -100, 0, 0),
|
||||
};
|
||||
|
||||
// 增加图片的大小,调整比例
|
||||
@ -252,6 +249,37 @@ namespace Wpf_AiSportsMicrospace.Views
|
||||
//_lastUpdateTime = DateTime.Now;
|
||||
|
||||
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
|
||||
|
||||
//开始人脸识别=============================================
|
||||
EventHandler<byte[]> handler = null;
|
||||
|
||||
handler = async (sender, buffer) =>
|
||||
{
|
||||
double[] xValues = [.. _groupJumpRopeContext.CirclePositions.Select(p => p.XNorm)];
|
||||
|
||||
var studentList = Utils.FacialRecognition(buffer, xValues);
|
||||
|
||||
if (studentList != null && studentList.Count > 0)
|
||||
{
|
||||
// 解绑事件
|
||||
_mainWin.FacialRecognitionEvent -= handler;
|
||||
|
||||
for (int i = 0; i < studentList.Count; i++)
|
||||
{
|
||||
var student = studentList[i];
|
||||
if (student != null)
|
||||
{
|
||||
_groupJumpRopeContext.UserList[i].DisplayText = student.StudentName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 这里可以继续处理 studentList
|
||||
};
|
||||
|
||||
// 绑定事件
|
||||
_mainWin.FacialRecognitionEvent += handler;
|
||||
}
|
||||
|
||||
private void UpdateCountdown()
|
||||
|
||||
@ -38,9 +38,62 @@
|
||||
</Grid>
|
||||
|
||||
<!--进度条-->
|
||||
<Grid VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,155,0,0">
|
||||
<TextBlock x:Name="score1" Text="0" FontSize="70" FontWeight="UltraBold" FontStyle="Italic" Foreground="#fff" Margin="-220,15" TextAlignment="Center" HorizontalAlignment="Left" Width="200" />
|
||||
<TextBlock x:Name="score2" Text="0" FontSize="70" FontWeight="UltraBold" FontStyle="Italic" Foreground="#fff" Margin="-220,15" TextAlignment="Center" HorizontalAlignment="Right" Width="200" />
|
||||
<Grid VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,95,0,0">
|
||||
<Grid HorizontalAlignment="Left" Margin="-220,15">
|
||||
<TextBlock x:Name="score1" Text="0" FontSize="50" FontWeight="UltraBold" FontStyle="Italic" Foreground="#fff" Width="200" Margin="0,15,0,0" TextAlignment="Center" RenderTransformOrigin="0.5,0.5" >
|
||||
<TextBlock.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform/>
|
||||
<SkewTransform/>
|
||||
<RotateTransform Angle="1.31"/>
|
||||
<TranslateTransform/>
|
||||
</TransformGroup>
|
||||
</TextBlock.RenderTransform>
|
||||
</TextBlock>
|
||||
<Image Source="/Resources/Img/gif/avatar.png" HorizontalAlignment="Center" x:Name="avatar1" Width="100" Margin="0,15,0,0" >
|
||||
<Image.Clip>
|
||||
<EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50"/>
|
||||
</Image.Clip>
|
||||
</Image>
|
||||
<TextBlock x:Name="name1" Text="一号玩家" FontSize="40" FontWeight="UltraBold" FontStyle="Italic" Foreground="#fff" Width="200" Margin="0,180,0,0" TextAlignment="Center" VerticalAlignment="Top" Height="54" RenderTransformOrigin="0.5,0.5" >
|
||||
<TextBlock.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform/>
|
||||
<SkewTransform AngleY="0"/>
|
||||
<RotateTransform/>
|
||||
<TranslateTransform Y="0"/>
|
||||
</TransformGroup>
|
||||
</TextBlock.RenderTransform>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<Grid HorizontalAlignment="Right" Margin="-220,15">
|
||||
<TextBlock x:Name="score2" Text="0" FontSize="50" FontWeight="UltraBold" FontStyle="Italic" Foreground="#fff" Width="200" Margin="0,15,0,0" TextAlignment="Center" RenderTransformOrigin="0.5,0.5" >
|
||||
<TextBlock.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform/>
|
||||
<SkewTransform/>
|
||||
<RotateTransform Angle="1.31"/>
|
||||
<TranslateTransform/>
|
||||
</TransformGroup>
|
||||
</TextBlock.RenderTransform>
|
||||
</TextBlock>
|
||||
<Image Source="/Resources/Img/gif/avatar.png" HorizontalAlignment="Center" x:Name="avatar2" Width="100" Margin="0,15,0,0" >
|
||||
<Image.Clip>
|
||||
<EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50"/>
|
||||
</Image.Clip>
|
||||
</Image>
|
||||
<TextBlock x:Name="name2" Text="二号玩家" FontSize="40" FontWeight="UltraBold" FontStyle="Italic" Foreground="#fff" Width="200" Margin="0,180,0,0" TextAlignment="Center" VerticalAlignment="Top" Height="54" RenderTransformOrigin="0.5,0.5" >
|
||||
<TextBlock.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform/>
|
||||
<SkewTransform AngleY="0"/>
|
||||
<RotateTransform/>
|
||||
<TranslateTransform Y="0"/>
|
||||
</TransformGroup>
|
||||
</TextBlock.RenderTransform>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<!--<TextBlock x:Name="score2" Text="0" FontSize="70" FontWeight="UltraBold" FontStyle="Italic" Foreground="#fff" Margin="-220,15" TextAlignment="Center" HorizontalAlignment="Right" Width="200" />-->
|
||||
<local:WxProgressBar x:Name="PkBar"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
using Dto;
|
||||
using AiSportsMicrospaceDB.DBContext;
|
||||
using Dto;
|
||||
using Enum;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
@ -40,7 +44,7 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
private readonly Dictionary<int, (string lastNumber, DateTime lastChangeTime, string currentState)> _jumpStatus = new Dictionary<int, (string, DateTime, string)>();
|
||||
private MusicJumpRopeContext _musicJumpRopeContext;
|
||||
private GameState _currentGameState = GameState.NotStarted;
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
// 容忍时间(节拍误差)
|
||||
public double _beatTolerance = 0.24; // ±150ms
|
||||
@ -77,6 +81,15 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
|
||||
// await Task.Delay(1000); // 200ms 间隔,不卡UI线程
|
||||
//}
|
||||
|
||||
var app = (App)Application.Current;
|
||||
using var scope = App.AppHost.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var cfg = await db.SixGroupPoints.FirstOrDefaultAsync();
|
||||
//var pointlist = JsonSerializer.Deserialize<List<(double, double)>>(cfg!.PointsJson);
|
||||
|
||||
List<(double XNorm, double YNorm)> pointlist = [(cfg.point3.X, cfg.point3.Y),(cfg.point5.X, cfg.point5.Y)];
|
||||
_musicJumpRopeContext.UpdateCirclePositions(pointlist);
|
||||
}
|
||||
|
||||
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
|
||||
@ -229,6 +242,66 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
_lastUpdateTime = DateTime.Now;
|
||||
_mainWin.ShowCountDownAnimation();
|
||||
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
|
||||
|
||||
//开始人脸识别=============================================
|
||||
EventHandler<byte[]> handler = null;
|
||||
|
||||
handler = async (sender, buffer) =>
|
||||
{
|
||||
double[] xValues = [.. _musicJumpRopeContext.CirclePositions.Select(p => p.XNorm)];
|
||||
|
||||
var studentList = Utils.FacialRecognition(buffer, xValues);
|
||||
|
||||
if (studentList != null && studentList.Count > 0)
|
||||
{
|
||||
// 解绑事件
|
||||
_mainWin.FacialRecognitionEvent -= handler;
|
||||
name1.Text = studentList[0]?.StudentName ?? "一号玩家";
|
||||
name2.Text = studentList[1]?.StudentName ?? "二号玩家";
|
||||
if (studentList.Count > 1 && studentList[0].Photo != "") {
|
||||
await LoadImageFromUrlAsync(studentList[0]?.Photo ?? "", avatar1);
|
||||
}
|
||||
if (studentList.Count > 2 && studentList[1].Photo != "")
|
||||
{
|
||||
await LoadImageFromUrlAsync(studentList[1]?.Photo ?? "", avatar2);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 这里可以继续处理 studentList
|
||||
};
|
||||
|
||||
// 绑定事件
|
||||
_mainWin.FacialRecognitionEvent += handler;
|
||||
}
|
||||
|
||||
private async Task LoadImageFromUrlAsync(string imageUrl , Image image)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 异步下载图片数据
|
||||
byte[] imageData = await _httpClient.GetByteArrayAsync(imageUrl);
|
||||
|
||||
// 创建BitmapImage并设置源
|
||||
var bitmap = new BitmapImage();
|
||||
using (var stream = new MemoryStream(imageData))
|
||||
{
|
||||
bitmap.BeginInit();
|
||||
bitmap.CacheOption = BitmapCacheOption.OnLoad; // 图片加载后缓存到内存
|
||||
bitmap.StreamSource = stream;
|
||||
bitmap.EndInit(); // 确保EndInit,否则可能导致图片加载不完整
|
||||
}
|
||||
bitmap.Freeze(); // 跨线程访问时可选
|
||||
|
||||
// 在UI线程上设置Image控件的Source
|
||||
image.Source = bitmap;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 处理异常(例如,网络错误、URL无效、图片格式不支持等)
|
||||
//MessageBox.Show($"加载图片失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCountdown()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user