using IOTContainer.Model; using Newtonsoft.Json; using OpenCvSharp; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using static IOTContainer.Common.BaiduFace; namespace IOTContainer.Common { public class BaiduFace { private BaiduFace() { } /// /// 初始化 /// /// true-成功 false-失败 public bool Init() { try { //更新sdk配置文件 UpdateConfig(); //初始化 int n = sdk_init(Path.Combine(FaceParameters.Parameters._baseDir, "BaiduSDK")); if (n != 0) { FaceParameters.Parameters.isInit = false; Log.MyLog.WriteLogFile("百度人脸Sdk初始化失败:"+n, "BaiduFace"); return false; } if (is_auth()) { FaceParameters.Parameters.isInit = true; return true; } else { FaceParameters.Parameters.isInit = false; Log.MyLog.WriteLogFile("设备未授权!", "BaiduFace"); return false; } } catch (Exception ex) { FaceParameters.Parameters.isInit = false; Log.MyLog.WriteLogFile("百度人脸Sdk初始化异常:" + ex.Message, "BaiduFace"); return false; } } /// /// 使用完毕,销毁sdk,释放内存 /// public void Destory() { try { sdk_destroy(); } catch (Exception ex) { Log.MyLog.WriteLogFile("百度人脸Sdk销毁异常:" + ex.Message, "BaiduFace"); } } /// /// usb摄像头实时人脸检测示例 /// public void USBVideoTract() { using (var window = new Window("face")) using (VideoCapture cap = VideoCapture.FromCamera(FaceParameters.Parameters._cameraIndex)) { if (!cap.IsOpened()) { Log.MyLog.WriteLogFile("打开摄像头失败!","BaiduFace"); //var faceData = new FaceSendModel //{ // age = 28, // genderMale = "男", // vipId = "", // faceID = "", // carNo = "", // isOK = "1", // faceImage = "" //}; //ComParameters.Parameters.EdgeWindow.pushFaceBase(JsonConvert.SerializeObject(faceData)); return; } Mat image = new Mat(); while (true) { try { //读取相机帧 cap.Read(image); // same as cvQueryFrame if (!image.Empty()) { FaceDectData? currentFace = null; #region 推送流文件 if (FaceParameters.Parameters._isOpenLive && null != image) { using (var newFrame = new Mat(image, new Rect(new Point(FaceParameters.Parameters._liveX, FaceParameters.Parameters._liveY), new Size(FaceParameters.Parameters._liveWidth, FaceParameters.Parameters._liveHeight)))) { try { var encodeBuff = newFrame.ImEncode(".jpg"); var base64Str = Convert.ToBase64String(encodeBuff); var faceData = new FaceSendModel { age = 28, genderMale = "男", vipId = "", faceID = "", carNo = "", isOK = "0", faceImage = base64Str }; //获取特征属性值 try { var speInfos = GetFaceInfoAttr(image, 1); // var attrInfo = GetFaceAttr(image, 1); bool isSuccess = false; if (speInfos != null && speInfos.Any()) { faceData.age = speInfos[0].age; faceData.genderMale = speInfos[0].gender == BDFaceGender.BDFACE_GENDER_MALE ? "男" : "女"; faceData.isOK = "1"; Log.MyLog.WriteLogFile($"发送视频流:{JsonConvert.SerializeObject(faceData)}", "EdgeFunction"); } //推送视频流 //Log.MyLog.WriteLogFile($"发送视频流:{JsonConvert.SerializeObject(faceData)}", "EdgeFunction"); ComParameters.Parameters.EdgeWindow.pushFaceBase(JsonConvert.SerializeObject(faceData)); } catch (Exception e) { Log.MyLog.WriteLogFile("获取特征属性值异常:" + e.Message, "BaiduFace"); } } catch (Exception e) { Log.MyLog.WriteLogFile("推送流文件异常:" + e.Message,"BaiduFace"); } } } #endregion try { //展示 window.ShowImage(image); Cv2.WaitKey(2); WinApi.Windows_Minimize("face"); } catch (Exception suex) { Log.MyLog.WriteLogFile("图像展示异常:" + suex.ToString(), "BaiduFace"); } } else { Log.MyLog.WriteLogFile("mat is empty", "BaiduFace"); } } catch (Exception ex) { Log.MyLog.WriteLogFile("usb摄像头实时人脸检测示例异常:" + ex.ToString(), "BaiduFace"); } } } } /// /// 获取人脸检测属性值 /// /// 图像帧 /// 最大检测人数 /// 传出的检测值 /// private FaceDectData[] GetFaceInfoAttr(Mat mat, int maxDectNum) { try { var Info = DetectFace(mat, maxDectNum); if (Info.Any()) { var attrInfo = GetFaceAttr(mat, maxDectNum); var resu = Info.Select(p => new FaceDectData { index = p.index, center_x = p.center_x, center_y = p.center_y, width = p.width, height = p.height, angle = p.angle, score = p.score, }).ToArray(); for (int i = 0; i < Info.Length; i++) { resu[i].age = i < attrInfo.Length ? attrInfo[i].age : int.MaxValue; resu[i].gender = i < attrInfo.Length ? attrInfo[i].gender : BDFaceGender.BDFACE_GENDER_MALE; } return resu; } return _emptyInfo; } catch (Exception) { return _emptyInfo; } } /// /// 人脸检测 /// /// /// /// private BDFaceBBox[] DetectFace(Mat mat, int maxDectNum) { try { int sizeTrack = Marshal.SizeOf(typeof(BDFaceBBox)); IntPtr ptT = Marshal.AllocHGlobal(sizeTrack * maxDectNum); int faceNum = detect(ptT, mat.CvPtr, _trackType); if (faceNum > 0) { // 因为需预分配内存,所以返回的人脸数若大于预先分配的内存数,则仅仅显示预分配的人脸数 if (faceNum > maxDectNum) { faceNum = maxDectNum; } BDFaceBBox[] info = new BDFaceBBox[faceNum]; for (int index = 0; index < faceNum; index++) { IntPtr ptr = new IntPtr(); if (8 == IntPtr.Size) { ptr = (IntPtr)(ptT.ToInt64() + sizeTrack * index); } else if (4 == IntPtr.Size) { ptr = (IntPtr)(ptT.ToInt32() + sizeTrack * index); } info[index] = (BDFaceBBox)Marshal.PtrToStructure(ptr, typeof(BDFaceBBox)); } Marshal.FreeHGlobal(ptT); info = info.Where(p => p.score > FaceParameters.Parameters._miniThreshold).ToArray(); return info; } else { return _emptyBox; } } catch (Exception ex) { Log.MyLog.WriteLogFile("人脸检测失败" + ex.Message, "BaiduFace"); return _emptyBox; } } /// /// 获取人脸属性值 /// /// /// /// private BDFaceAttribute[] GetFaceAttr(Mat mat, int maxDectNum) { try { int size = Marshal.SizeOf(typeof(BDFaceAttribute)); IntPtr ptT = Marshal.AllocHGlobal(size * maxDectNum); int faceNum = face_attr(ptT, mat.CvPtr); if (faceNum > 0) { if (faceNum > maxDectNum) { faceNum = maxDectNum; } BDFaceAttribute[] attr_info = new BDFaceAttribute[faceNum]; for (int index = 0; index < faceNum; index++) { IntPtr ptr = new IntPtr(); if (8 == IntPtr.Size) { ptr = (IntPtr)(ptT.ToInt64() + size * index); } else if (4 == IntPtr.Size) { ptr = (IntPtr)(ptT.ToInt32() + size * index); } attr_info[index] = (BDFaceAttribute)Marshal.PtrToStructure(ptr, typeof(BDFaceAttribute)); } Marshal.FreeHGlobal(ptT); return attr_info; } else { return _emptyAttr; } } catch (Exception ex) { Log.MyLog.WriteLogFile("获取人脸属性值失败" + ex.Message); return _emptyAttr; } } public void FaceContact(Mat mat, FaceDectData faceBox) { if (_faceDic.ContainsKey(faceBox.index)) { int x = Convert.ToInt32(faceBox.center_x - faceBox.width / 2.0); int y = Convert.ToInt32(faceBox.center_y - faceBox.height / 2.0); int w = Convert.ToInt32(faceBox.width); int h = Convert.ToInt32(faceBox.height); try { using (var image = new Mat(mat, new Rect(new Point(x, y), new Size(w, h)))) { var encodeBuff = image.ImEncode(".jpg"); var base64Str = Convert.ToBase64String(encodeBuff); var gender = faceBox.gender == BDFaceGender.BDFACE_GENDER_MALE ? 1 : 0; var param = $"Face={base64Str}&ip={SystemManage.GetLocalIp()}&Age={faceBox.age}&GenderMale={gender}"; if (!string.IsNullOrEmpty(FaceParameters.Parameters._method)) { param += $"&method={FaceParameters.Parameters._method}"; FaceParameters.Parameters._method = null; } //HttpComm.Http.UploadFace(param); } } catch (Exception ex) { Log.MyLog.WriteLogFile("上传人脸数据失败:" + ex.Message,"BaiduFace"); } } } /// /// 更新Sdk配置文件 /// public static void UpdateConfig() { try { var filePath = Path.Combine(FaceParameters.Parameters._baseDir, "BaiduSDK","config", "detect.json"); if (!File.Exists(filePath)) { Log.MyLog.WriteLogFile("Sdk配置文件路径异常:" + filePath); return; } var content = FileManage.ReadJsonFile(filePath); if (!string.IsNullOrEmpty(content)) { var model = JsonConvert.DeserializeObject(content); model.max_detect_num = FaceParameters.Parameters._maxDectNum; FileManage.WriteJsonFile(filePath, JsonConvert.SerializeObject(model)); } } catch (Exception ex) { Log.MyLog.WriteLogFile("更新百度Sdk配置文件异常:" + ex.Message); } } public static BaiduFace SDK { get { if (_sdk == null) _sdk = new BaiduFace(); return _sdk; } } private static BaiduFace _sdk; private const string DLLPath = @"BaiduSDK\BaiduFaceApi.dll"; #region 百度DLL导入 // sdk初始化 [DllImport(DLLPath, EntryPoint = "sdk_init", CharSet = CharSet.Ansi , CallingConvention = CallingConvention.Cdecl)] private static extern int sdk_init(string model_path); // 是否授权 [DllImport(DLLPath, EntryPoint = "is_auth", CharSet = CharSet.Ansi , CallingConvention = CallingConvention.Cdecl)] private static extern bool is_auth(); // sdk销毁 [DllImport(DLLPath, EntryPoint = "sdk_destroy", CharSet = CharSet.Ansi , CallingConvention = CallingConvention.Cdecl)] private static extern void sdk_destroy(); // type 为0时候执行RGB人脸跟踪,1时候执行NIR人脸跟踪 [DllImport(DLLPath, EntryPoint = "track", CharSet = CharSet.Ansi , CallingConvention = CallingConvention.Cdecl)] public static extern int track(IntPtr ptr, IntPtr mat, int type); // type 为0时候执行RGB人脸跟踪,1时候执行NIR人脸跟踪 [DllImport(DLLPath, EntryPoint = "clear_track_history", CharSet = CharSet.Ansi , CallingConvention = CallingConvention.Cdecl)] public static extern void clear_track_history(int type); // 获取人脸属性 [DllImport(DLLPath, EntryPoint = "face_attr", CharSet = CharSet.Ansi , CallingConvention = CallingConvention.Cdecl)] public static extern int face_attr(IntPtr ptr, IntPtr mat); //人脸检测 type 0: 表示rgb 人脸检测 1:表示nir人脸检测 [DllImport(DLLPath, EntryPoint = "detect", CharSet = CharSet.Ansi , CallingConvention = CallingConvention.Cdecl)] public static extern int detect(IntPtr ptr, IntPtr mat, int type); #endregion #region 百度结构体 [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BDFaceBBox { public int index; // 人脸索引值 public float center_x; // 人脸中心点x坐标 public float center_y; // 人脸中心点y坐标 public float width; // 人脸宽度 public float height; // 人脸高度 public float angle; // 人脸角度 public float score; // 人脸置信度 } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BDFaceLandmark { public int index; // 人脸关键点索引值 public int size; // 人脸关键点数量 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 144)] public float[] data;// = new float[144]; public float score; // 人脸关键点置信度 } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BDFaceTrackInfo { public int face_id; [MarshalAs(UnmanagedType.Struct)] public BDFaceBBox box; [MarshalAs(UnmanagedType.Struct)] public BDFaceLandmark landmark; } /// /// 人脸表情属性枚举 /// enum BDFaceAttributeEmotionType { BDFACE_ATTRIBUTE_EMOTION_FROWN = 0, // 皱眉 BDFACE_ATTRIBUTE_EMOTION_SMILE = 1, // 笑 BDFACE_ATTRIBUTE_EMOTION_CALM = 2, // 平静 }; /// /// 人脸种族属性枚举 /// enum BDFaceRace { BDFACE_RACE_YELLOW = 0, // 黄种人 BDFACE_RACE_WHITE = 1, // 白种人 BDFACE_RACE_BLACK = 2, // 黑种人 BDFACE_RACE_INDIAN = 3, // 印第安人 }; /// /// 眼镜状态属性枚举 /// enum BDFaceGlasses { BDFACE_NO_GLASSES = 0, // 无眼镜 BDFACE_GLASSES = 1, // 有眼镜 BDFACE_SUN_GLASSES = 2, // 墨镜 }; /// /// 性别属性枚举 /// public enum BDFaceGender { BDFACE_GENDER_FEMAILE = 0, // 女性 BDFACE_GENDER_MALE = 1, // 男性 }; /// /// 人脸属性结构体 /// [StructLayout(LayoutKind.Sequential, Pack = 1)] struct BDFaceAttribute { public int age; // 年龄 public BDFaceRace race; // 种族 public BDFaceAttributeEmotionType emotion; // 表情 public BDFaceGlasses glasses; // 戴眼镜状态 public BDFaceGender gender; // 性别 }; #endregion #region 业务逻辑类 /// /// 综合识别类 /// public struct FaceDectData { public int index; // 人脸索引值 public float center_x; // 人脸中心点x坐标 public float center_y; // 人脸中心点y坐标 public float width; // 人脸宽度 public float height; // 人脸高度 public float angle; // 人脸角度 public float score; // 人脸置信度 public int age;// 年龄 public BDFaceGender gender;// 性别 } #endregion #region 业务逻辑变量 /// /// type 为0时候执行RGB人脸跟踪,1时候执行NIR人脸跟踪 /// private int _trackType = 0; /// /// 人脸属性值空默认值 /// private BDFaceAttribute[] _emptyAttr = new BDFaceAttribute[0]; /// /// 人脸检测空默认值 /// private BDFaceBBox[] _emptyBox = new BDFaceBBox[0]; /// /// 人脸特征空默认值 /// private BDFaceTrackInfo[] _emptyTrack = new BDFaceTrackInfo[0]; /// /// 人脸综合空默认值 /// private FaceDectData[] _emptyInfo = new FaceDectData[0]; /// /// 停留两秒后被判定的当前检测到的主 人脸 /// public FaceDectData? _masterFace = null; /// /// 近期人脸特征字典(从未检测到人脸到检测到人脸期间的人脸,当未检测到人脸时会清空) /// private Dictionary _faceDic = new Dictionary(); /// /// 首次进入时的人脸时间字典 /// private Dictionary _faceEnterDic = new Dictionary(); /// /// 首次未被识别到的人脸时间字典(检测到不同人脸时会记录上一个人脸id和(第一次未检测到这个人的)时间) /// private Dictionary _faceLeaveDic = new Dictionary(); private bool _isSendLive; #endregion } // 绘制类,画人脸框,画关键点等 class FaceDraw { // 画人脸框 public static int draw_rects(ref Mat img, int face_num, BDFaceBBox[] info) { if (face_num <= 0) { return 0; } Scalar color = new Scalar(0, 255, 0); for (int i = 0; i < face_num; i++) { int x = Convert.ToInt32(info[i].center_x - info[i].width / 2.0); int y = Convert.ToInt32(info[i].center_y - info[i].height / 2.0); int w = Convert.ToInt32(info[i].width); int h = Convert.ToInt32(info[i].height); Rect rect = new Rect(x, y, w, h); Cv2.Rectangle(img, rect, color); } return 0; } // 画人脸框 public static int draw_rects(ref Mat img, int face_num, FaceDectData[] info) { if (face_num <= 0) { return 0; } Scalar color = new Scalar(0, 255, 0); for (int i = 0; i < face_num; i++) { int x = Convert.ToInt32(info[i].center_x - info[i].width / 2.0); int y = Convert.ToInt32(info[i].center_y - info[i].height / 2.0); int w = Convert.ToInt32(info[i].width); int h = Convert.ToInt32(info[i].height); Rect rect = new Rect(x, y, w, h); Cv2.Rectangle(img, rect, color); } return 0; } // 画人脸框 public static int draw_rects(ref Mat img, int face_num, BDFaceTrackInfo[] track_info) { if (face_num <= 0) { return 0; } Scalar color = new Scalar(0, 255, 0); for (int i = 0; i < face_num; i++) { int x = Convert.ToInt32(track_info[i].box.center_x - track_info[i].box.width / 2.0); int y = Convert.ToInt32(track_info[i].box.center_y - track_info[i].box.height / 2.0); int w = Convert.ToInt32(track_info[i].box.width); int h = Convert.ToInt32(track_info[i].box.height); Rect rect = new Rect(x, y, w, h); Cv2.Rectangle(img, rect, color); } return 0; } // 画人脸关键点 public static int draw_shape(ref Mat img, int face_num, BDFaceTrackInfo[] track_info) { if (face_num <= 0) { return 0; } int face_id = 0; Scalar color = new Scalar(0, 255, 255); Scalar color2 = new Scalar(0, 0, 255); for (int i = 0; i < face_num; ++i) { int point_size = track_info[i].landmark.size / 2; int radius = 2; face_id = track_info[i].face_id; for (int j = 0; j < point_size; ++j) { Cv2.Circle(img, (int)track_info[i].landmark.data[j * 2], (int)track_info[i].landmark.data[j * 2 + 1], radius, color); } if (point_size == 72) { const int components = 9; int[] comp1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; int[] comp2 = { 13, 14, 15, 16, 17, 18, 19, 20, 13, 21 }; int[] comp3 = { 22, 23, 24, 25, 26, 27, 28, 29, 22 }; int[] comp4 = { 30, 31, 32, 33, 34, 35, 36, 37, 30, 38 }; int[] comp5 = { 39, 40, 41, 42, 43, 44, 45, 46, 39 }; int[] comp6 = { 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 47 }; int[] comp7 = { 51, 57, 52 }; int[] comp8 = { 58, 59, 60, 61, 62, 63, 64, 65, 58 }; int[] comp9 = { 58, 66, 67, 68, 62, 69, 70, 71, 58 }; int[][] idx = { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, comp9 }; int[] npoints = { 13, 10, 9, 10, 9, 11, 3, 9, 9 }; for (int m = 0; m < components; ++m) { for (int n = 0; n < npoints[m] - 1; ++n) { Point p1 = new Point(track_info[i].landmark.data[idx[m][n] * 2], track_info[i].landmark.data[idx[m][n] * 2 + 1]); Point p2 = new Point(track_info[i].landmark.data[idx[m][n + 1] * 2], track_info[i].landmark.data[idx[m][n + 1] * 2 + 1]); Cv2.Line(img, p1, p2, color2); } } } string s_face_id = face_id.ToString(); double font_scale = 2; Point pos = new Point(track_info[i].box.center_x, track_info[i].box.center_y); Cv2.PutText(img, s_face_id, pos, HersheyFonts.HersheyComplex, font_scale, new Scalar(0, 255, 255)); } return 0; } // 画人脸关键点 public static int draw_landmark(ref Mat img, int face_num, BDFaceLandmark[] landmark) { if (face_num <= 0) { return 0; } Scalar color = new Scalar(0, 255, 255); Scalar color2 = new Scalar(0, 0, 255); for (int i = 0; i < face_num; ++i) { int point_size = landmark[i].size / 2; int radius = 2; for (int j = 0; j < point_size; ++j) { Cv2.Circle(img, (int)landmark[i].data[j * 2], (int)landmark[i].data[j * 2 + 1], radius, color); } if (point_size == 72) { const int components = 9; int[] comp1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; int[] comp2 = { 13, 14, 15, 16, 17, 18, 19, 20, 13, 21 }; int[] comp3 = { 22, 23, 24, 25, 26, 27, 28, 29, 22 }; int[] comp4 = { 30, 31, 32, 33, 34, 35, 36, 37, 30, 38 }; int[] comp5 = { 39, 40, 41, 42, 43, 44, 45, 46, 39 }; int[] comp6 = { 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 47 }; int[] comp7 = { 51, 57, 52 }; int[] comp8 = { 58, 59, 60, 61, 62, 63, 64, 65, 58 }; int[] comp9 = { 58, 66, 67, 68, 62, 69, 70, 71, 58 }; int[][] idx = { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, comp9 }; int[] npoints = { 13, 10, 9, 10, 9, 11, 3, 9, 9 }; for (int m = 0; m < components; ++m) { for (int n = 0; n < npoints[m] - 1; ++n) { Point p1 = new Point(landmark[i].data[idx[m][n] * 2], landmark[i].data[idx[m][n] * 2 + 1]); Point p2 = new Point(landmark[i].data[idx[m][n + 1] * 2], landmark[i].data[idx[m][n + 1] * 2 + 1]); Cv2.Line(img, p1, p2, color2); } } } } return 0; } } }