update:本地化&人脸
This commit is contained in:
commit
c7979ae8f9
@ -4,6 +4,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace AiSportsMicrospaceDB.DBContext
|
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<BasicConfig> BasicConfig { get; set; }
|
||||||
|
|
||||||
public DbSet<SixGroupPoints> SixGroupPoints { get; set; }
|
public DbSet<SixGroupPoints> SixGroupPoints { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,10 +15,4 @@ namespace AiSportsMicrospaceDB.Entities
|
|||||||
public string UserName { get; set; }
|
public string UserName { get; set; }
|
||||||
public string Password { 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 class AppSettings
|
||||||
{
|
{
|
||||||
public static string SchoolCode = "202501060001";
|
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 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())
|
using (var client = new HttpClient())
|
||||||
{
|
{
|
||||||
@ -27,9 +27,10 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await client.PostAsync(url, content);
|
// 注意:要调用 PostAsync 而不是 po()
|
||||||
|
var response = client.PostAsync(url, content).Result; // 同步等待
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
return await response.Content.ReadAsStringAsync();
|
return response.Content.ReadAsStringAsync().Result;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -37,6 +38,7 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string> HttpGetAsync(string url, Dictionary<string, string> headers = null)
|
public static async Task<string> HttpGetAsync(string url, Dictionary<string, string> headers = null)
|
||||||
{
|
{
|
||||||
using (var client = new HttpClient())
|
using (var client = new HttpClient())
|
||||||
|
|||||||
@ -214,10 +214,10 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
return _cache.GetValueOrDefault(relativePath);
|
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)
|
if (xNorms == null || xNorms.Length == 0)
|
||||||
throw new ArgumentException("xNorms 至少需要两个元素");
|
throw new ArgumentException("xNorms 不能为空");
|
||||||
|
|
||||||
using var image = SharpImage.Image.Load<Rgba32>(buffer);
|
using var image = SharpImage.Image.Load<Rgba32>(buffer);
|
||||||
|
|
||||||
@ -225,43 +225,66 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
int imgHeight = image.Height;
|
int imgHeight = image.Height;
|
||||||
int rectHeight = imgHeight / 2;
|
int rectHeight = imgHeight / 2;
|
||||||
|
|
||||||
// 计算分割边界
|
// 1. 排序中心点
|
||||||
var sortedX = xNorms.OrderBy(x => x).ToList();
|
var sortedX = xNorms.OrderBy(x => x).ToList();
|
||||||
|
int faceCount = sortedX.Count;
|
||||||
|
|
||||||
|
// 2. 计算分割边界
|
||||||
var splitXs = new List<double>();
|
var splitXs = new List<double>();
|
||||||
for (int i = 0; i < sortedX.Count - 1; i++)
|
for (int i = 0; i < sortedX.Count - 1; i++)
|
||||||
splitXs.Add((sortedX[i] + sortedX[i + 1]) / 2.0);
|
splitXs.Add((sortedX[i] + sortedX[i + 1]) / 2.0);
|
||||||
|
|
||||||
|
// 3. 起止边界
|
||||||
splitXs.Insert(0, 0.0);
|
splitXs.Insert(0, 0.0);
|
||||||
splitXs.Add(1.0);
|
splitXs.Add(1.0);
|
||||||
|
|
||||||
|
// 4. 输出目录
|
||||||
|
string imgDir = Path.Combine(AppContext.BaseDirectory, "img");
|
||||||
|
Directory.CreateDirectory(imgDir);
|
||||||
|
|
||||||
var studentInfos = new List<StudentInfoDto>();
|
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);
|
double xStart = splitXs[i];
|
||||||
int x2 = (int)(splitXs[i + 1] * imgWidth);
|
double xEnd = splitXs[i + 1];
|
||||||
int w = x2 - x1;
|
|
||||||
|
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);
|
var cropRect = new Rectangle(x1, 0, w, rectHeight);
|
||||||
|
|
||||||
using var cropped = image.Clone(ctx => ctx.Crop(cropRect));
|
using var cropped = image.Clone(ctx => ctx.Crop(cropRect));
|
||||||
|
|
||||||
// 转为 Base64
|
|
||||||
using var ms = new MemoryStream();
|
using var ms = new MemoryStream();
|
||||||
cropped.Save(ms, new JpegEncoder());
|
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());
|
string base64 = Convert.ToBase64String(ms.ToArray());
|
||||||
|
|
||||||
// 调用人脸识别方法(假设返回姓名+头像Base64)
|
Console.WriteLine($"[{i + 1}] 范围: {xStart:0.00}-{xEnd:0.00}, 宽: {w}");
|
||||||
StudentInfoDto studentInfo = await FaceRecognitionAsync(base64);
|
|
||||||
studentInfos.Add(studentInfo);
|
// === 同步调用人脸识别接口 ===
|
||||||
|
var result = FaceRecognitionAsync(base64);
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
studentInfos.Add(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return studentInfos;
|
return studentInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 调用人脸识别接口,返回学生信息
|
/// 调用人脸识别接口,返回学生信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<StudentInfoDto> FaceRecognitionAsync(string base64Image)
|
public static StudentInfoDto FaceRecognitionAsync(string base64Image)
|
||||||
{
|
{
|
||||||
var param = new
|
var param = new
|
||||||
{
|
{
|
||||||
@ -272,23 +295,26 @@ namespace Wpf_AiSportsMicrospace.Common
|
|||||||
string json = JsonSerializer.Serialize(param);
|
string json = JsonSerializer.Serialize(param);
|
||||||
|
|
||||||
// 调用 HttpPostAsync
|
// 调用 HttpPostAsync
|
||||||
string response = await HttpManager.HttpPostAsync(AppSettings.BackendUrl, json);
|
string response = HttpManager.HttpPost(AppSettings.BackendUrl, json);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(response))
|
if (string.IsNullOrEmpty(response))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 反序列化为接口返回对象
|
// 假设 response 是 JSON 字符串
|
||||||
var faceInfo = JsonSerializer.Deserialize<StudentInfoDto>(response, new JsonSerializerOptions
|
var apiResponse = JsonSerializer.Deserialize<ApiResponse<StudentInfoDto>>(response, new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
PropertyNameCaseInsensitive = true
|
PropertyNameCaseInsensitive = true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (faceInfo == null)
|
// 取 data
|
||||||
|
var studentInfo = apiResponse.Data;
|
||||||
|
|
||||||
|
if (studentInfo == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return faceInfo;
|
return studentInfo;
|
||||||
}
|
}
|
||||||
catch
|
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>();
|
Sports = new List<SportBase>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新点位方法
|
||||||
|
public void UpdateCirclePositions(List<(double XNorm, double YNorm)> newPositions)
|
||||||
|
{
|
||||||
|
CirclePositions = newPositions;
|
||||||
|
}
|
||||||
|
|
||||||
// 更新排行榜方法
|
// 更新排行榜方法
|
||||||
public List<RankItem> UpdateRankList()
|
public List<RankItem> UpdateRankList()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -9,13 +9,14 @@ namespace Dto
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class StudentInfoDto
|
public class StudentInfoDto
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string StudentName { get; set; }
|
||||||
public string Photo { get; set; }
|
public string Photo { get; set; }
|
||||||
public string StudentNo { get; set; }
|
public string StudentCode { get; set; }
|
||||||
public string SchoolCode { get; set; }
|
public int GradeId { get; set; }
|
||||||
|
public int ClassId { get; set; }
|
||||||
public string GradeName { get; set; }
|
public string GradeName { get; set; }
|
||||||
public string ClassName { get; set; }
|
public string ClassName { get; set; }
|
||||||
public int Age { 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 Emgu.CV.Flann;
|
||||||
using Enum;
|
using Enum;
|
||||||
using HandyControl.Controls;
|
using HandyControl.Controls;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using SharpDX.Direct3D9;
|
using SharpDX.Direct3D9;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@ -68,28 +70,23 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
Unloaded += UserControl_Unloaded;
|
Unloaded += UserControl_Unloaded;
|
||||||
_groupJumpRopeContext = new GroupJumpRopeContext();
|
_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)
|
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 绑定人脸识别事件
|
var app = (App)Application.Current;
|
||||||
_mainWin.FacialRecognitionEvent += async (sender, buffer) =>
|
using var scope = App.AppHost.Services.CreateScope();
|
||||||
{
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
var studentList = await Utils.FacialRecognition(buffer, new double[] { 0.50, 0.78 });
|
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();
|
DrawCirclesWithText();
|
||||||
|
|
||||||
@ -252,6 +249,37 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
//_lastUpdateTime = DateTime.Now;
|
//_lastUpdateTime = DateTime.Now;
|
||||||
|
|
||||||
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
|
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()
|
private void UpdateCountdown()
|
||||||
|
|||||||
@ -38,9 +38,62 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!--进度条-->
|
<!--进度条-->
|
||||||
<Grid VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,155,0,0">
|
<Grid VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,95,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" />
|
<Grid HorizontalAlignment="Left" Margin="-220,15">
|
||||||
<TextBlock x:Name="score2" Text="0" FontSize="70" FontWeight="UltraBold" FontStyle="Italic" Foreground="#fff" Margin="-220,15" TextAlignment="Center" HorizontalAlignment="Right" Width="200" />
|
<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"/>
|
<local:WxProgressBar x:Name="PkBar"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
using Dto;
|
using AiSportsMicrospaceDB.DBContext;
|
||||||
|
using Dto;
|
||||||
using Enum;
|
using Enum;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows;
|
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 readonly Dictionary<int, (string lastNumber, DateTime lastChangeTime, string currentState)> _jumpStatus = new Dictionary<int, (string, DateTime, string)>();
|
||||||
private MusicJumpRopeContext _musicJumpRopeContext;
|
private MusicJumpRopeContext _musicJumpRopeContext;
|
||||||
private GameState _currentGameState = GameState.NotStarted;
|
private GameState _currentGameState = GameState.NotStarted;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
// 容忍时间(节拍误差)
|
// 容忍时间(节拍误差)
|
||||||
public double _beatTolerance = 0.24; // ±150ms
|
public double _beatTolerance = 0.24; // ±150ms
|
||||||
@ -77,6 +81,15 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
|
|
||||||
// await Task.Delay(1000); // 200ms 间隔,不卡UI线程
|
// 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)
|
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
|
||||||
@ -229,6 +242,66 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
|||||||
_lastUpdateTime = DateTime.Now;
|
_lastUpdateTime = DateTime.Now;
|
||||||
_mainWin.ShowCountDownAnimation();
|
_mainWin.ShowCountDownAnimation();
|
||||||
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
|
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()
|
private void UpdateCountdown()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user