diff --git a/Wpf_AiSportsMicrospace/Common/AppSettings.cs b/Wpf_AiSportsMicrospace/Common/AppSettings.cs index 26d57e4..ed5c9df 100644 --- a/Wpf_AiSportsMicrospace/Common/AppSettings.cs +++ b/Wpf_AiSportsMicrospace/Common/AppSettings.cs @@ -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"; } } diff --git a/Wpf_AiSportsMicrospace/Common/HttpManager.cs b/Wpf_AiSportsMicrospace/Common/HttpManager.cs index 11751f8..5cbb496 100644 --- a/Wpf_AiSportsMicrospace/Common/HttpManager.cs +++ b/Wpf_AiSportsMicrospace/Common/HttpManager.cs @@ -5,7 +5,7 @@ namespace Wpf_AiSportsMicrospace.Common { public class HttpManager { - public static async Task HttpPostAsync(string url, string postData = null, string contentType = "application/json", int timeOut = 30, Dictionary headers = null) + public static string HttpPost(string url, string postData = null, string contentType = "application/json", int timeOut = 30, Dictionary 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 HttpGetAsync(string url, Dictionary headers = null) { using (var client = new HttpClient()) diff --git a/Wpf_AiSportsMicrospace/Common/Utils.cs b/Wpf_AiSportsMicrospace/Common/Utils.cs index 998cf66..7a25598 100644 --- a/Wpf_AiSportsMicrospace/Common/Utils.cs +++ b/Wpf_AiSportsMicrospace/Common/Utils.cs @@ -214,10 +214,10 @@ namespace Wpf_AiSportsMicrospace.Common return _cache.GetValueOrDefault(relativePath); } - public static async Task> FacialRecognition(byte[] buffer, double[] xNorms) + public static List 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(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(); 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(); - 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; } + + /// /// 调用人脸识别接口,返回学生信息 /// - public static async Task 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(response, new JsonSerializerOptions + // 假设 response 是 JSON 字符串 + var apiResponse = JsonSerializer.Deserialize>(response, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - if (faceInfo == null) + // 取 data + var studentInfo = apiResponse.Data; + + if (studentInfo == null) return null; - return faceInfo; + return studentInfo; } catch { diff --git a/Wpf_AiSportsMicrospace/Dto/ApiResponse.cs b/Wpf_AiSportsMicrospace/Dto/ApiResponse.cs new file mode 100644 index 0000000..4c037f7 --- /dev/null +++ b/Wpf_AiSportsMicrospace/Dto/ApiResponse.cs @@ -0,0 +1,12 @@ + + +namespace Dto +{ + public class ApiResponse + { + public bool Status { get; set; } + public int Code { get; set; } + public string Message { get; set; } + public T Data { get; set; } + } +} diff --git a/Wpf_AiSportsMicrospace/Dto/StudentInfoDto.cs b/Wpf_AiSportsMicrospace/Dto/StudentInfoDto.cs index 882aa8a..3a682fe 100644 --- a/Wpf_AiSportsMicrospace/Dto/StudentInfoDto.cs +++ b/Wpf_AiSportsMicrospace/Dto/StudentInfoDto.cs @@ -9,13 +9,14 @@ namespace Dto /// 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; } } } diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index bc47647..80d9032 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -70,12 +70,25 @@ namespace Wpf_AiSportsMicrospace.Views } private async void UserControl_Loaded(object sender, RoutedEventArgs e) { - // 绑定人脸识别事件 - _mainWin.FacialRecognitionEvent += async (sender, buffer) => + // 使用局部变量保存事件处理方法,以便解绑 + EventHandler handler = null; + + handler = async (sender, buffer) => { - var studentList = await Utils.FacialRecognition(buffer, new double[] { 0.50, 0.78 }); + var studentList = Utils.FacialRecognition(buffer, new double[] { 0.21, 0.36, 0.50, 0.64, 0.78, 0.92 }); + + if (studentList != null && studentList.Count > 0) + { + // 解绑事件 + _mainWin.FacialRecognitionEvent -= handler; + } + + // 这里可以继续处理 studentList }; + // 绑定事件 + _mainWin.FacialRecognitionEvent += handler; + DrawCirclesWithText(); // 播放音乐