renlian
This commit is contained in:
parent
c1c7570e8e
commit
bb2c3bd008
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,12 +70,25 @@ 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) =>
|
EventHandler<byte[]> 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();
|
DrawCirclesWithText();
|
||||||
|
|
||||||
// 播放音乐
|
// 播放音乐
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user