上一篇我们实现了用SDK登录摄像头并实现预览(https://www.cnblogs.com/wdw984/p/13564195.html),这次我们实现通过SDK调用摄像头本身自带的人脸抓拍功能。
因为篇幅较短,这里直接上代码。
首先我们在MainWindow代码里定义一个安全队列用来存储抓拍到的人脸数据,一个定时取队列数据的定时器,一个人脸抓拍回调事件
private static ConcurrentQueue<CaptureInfo> _concurrentQueue = new ConcurrentQueue<CaptureInfo>(); private static HuaWeiSdkHelper.PfRealDataCallBack _fedRealPlayCallbackFaceCapture; private Timer _timer;
在窗体加载事件中初始化定时器,用来把抓拍到的数据保存到本地
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) {
//上一篇文章中的代码省略 _timer = new Timer(300) { Enabled = false }; _timer.Elapsed += Timer_Elapsed; }
SDK定义了人脸捕获需要定义的Struct和Enum
namespace HuaWeiCamera.Struct { /// <summary> /// 元数据获取相关参数 /// </summary> [StructLayout(LayoutKind.Sequential)] public struct PU_META_DATA { /// <summary> /// 数据容量 /// </summary> public ushort usCapacity; /// <summary> /// 有效数目 /// </summary> public ushort usValidNumber; /// <summary> /// 参考PU_UserData 定义 /// </summary> public System.IntPtr pstMetaUserData; } }
namespace HuaWeiCamera.Struct { /// <summary> /// 元数据用户数据 /// </summary> [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 1)] public struct PU_UserData { /// <summary> /// 元数据类型 /// </summary> public LAYER_THREE_TYPE eType; /// <summary> /// 用户元数据详情 /// </summary> public PU_UserData_unMetadata Union1; } }
namespace HuaWeiCamera.Struct { /// <summary> /// 用户元数据详情 /// </summary> [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)] public struct PU_UserData_unMetadata { [System.Runtime.InteropServices.FieldOffset(0)] public int bBoolValue; [System.Runtime.InteropServices.FieldOffset(0)] public byte charValue; [System.Runtime.InteropServices.FieldOffset(0)] public byte ucharValue; [System.Runtime.InteropServices.FieldOffset(0)] public short shortValue; [System.Runtime.InteropServices.FieldOffset(0)] public ushort ushortValue; [System.Runtime.InteropServices.FieldOffset(0)] public int IntValue; [System.Runtime.InteropServices.FieldOffset(0)] public uint uIntValue; [System.Runtime.InteropServices.FieldOffset(0)] public long longlongValue; [System.Runtime.InteropServices.FieldOffset(0)] public ulong uLonglongValue; /// <summary> /// 元数据二进制颜色 /// </summary> [System.Runtime.InteropServices.FieldOffset(0)] public ST_BINARY stBinay; /// <summary> /// 元数据矩形 /// </summary> [System.Runtime.InteropServices.FieldOffset(0)] public META_RECT_S stRec; /// <summary> /// 元数据划点 /// </summary> [System.Runtime.InteropServices.FieldOffset(0)] public META_POINT_S stPoint; /// <summary> /// 元数据划线 /// </summary> [System.Runtime.InteropServices.FieldOffset(0)] public META_LINE_S stLine; [System.Runtime.InteropServices.FieldOffset(0)] public IntPtr stPolyGon; [System.Runtime.InteropServices.FieldOffset(0)] public IntPtr stColor; [System.Runtime.InteropServices.FieldOffset(0)] public IntPtr stHumanAttr; /// <summary> /// 人脸信息 /// </summary> [System.Runtime.InteropServices.FieldOffset(0)] public META_FACE_INFO stFaceInfo; /// <summary> /// 人脸属性 /// </summary> [System.Runtime.InteropServices.FieldOffset(0)] public META_FACE_ATTRIBUTES stFaceAttr; [System.Runtime.InteropServices.FieldOffset(0)] public IntPtr szUserData; } }
LAYER_THREE_TYPE的Enum请参考https://support.huawei.com/enterprise/zh/doc/EDOC1100084903
定时器事件中处理捕获到的人脸数据(存为本地图片)
#region 处理人脸数据 private void Timer_Elapsed(object sender, ElapsedEventArgs e) { if (_concurrentQueue.Count == 0) { Console.WriteLine(@"暂无人脸图片"); return; } if (!_concurrentQueue.TryDequeue(out CaptureInfo face)) { Console.WriteLine(@"读取队列错误"); return; } if (face._dataFacePic != null && face._dataFacePic.Length > 0) { Console.WriteLine(@"人脸存储中"); Task.Run(async () => { var saveFaceFile = Path.Combine($"{AppDomain.CurrentDomain.BaseDirectory}", "jpg", $"face_{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.jpg"); await YuvHelper.Byte2Jpg(face._dataFacePic, saveFaceFile).ConfigureAwait(false); }); } if (face._dataFacePanorama != null && face._dataFacePanorama.Length > 0) { Console.WriteLine(@"全景图片存储中"); Task.Run(async () => { var savePanoramaFile = Path.Combine($"{AppDomain.CurrentDomain.BaseDirectory}", "jpg", $"Panorama_{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.jpg"); await YuvHelper.Byte2Jpg(face._dataFacePanorama, savePanoramaFile).ConfigureAwait(false); }); } } #endregion
在人脸捕获按钮事件中启动人脸捕获回调
#region 人脸捕获 private void ButtonFace_OnClick(object sender, RoutedEventArgs e) { if (0 == _ulIdentifyId) { HuaWeiSdkHelper.InitAndLogin("192.168.2.250", 6060, "ApiAdmin", "HuaWei123", out _ulIdentifyId, out string errMsg); if (0 == _ulIdentifyId) { MessageBox.Show(errMsg); return; } } var prpInfos = new PU_REAL_PLAY_INFO_S[1]; var clientInfo = new PU_REAL_PLAY_INFO_S { ulChannelId = 101, hPlayWnd = IntPtr.Zero, enProtocolType = PU_PROTOCOL_TYPE.PU_PROTOCOL_TYPE_TCP, enStreamType = PU_STREAM_TYPE.PU_VIDEO_MAIN_STREAM, enVideoType = PU_VIDEO_TYPE.PU_VIDEO_TYPE_META,//这里需要设置为视频类型为元数据 enMediaCryptoType = PU_MEDIA_CRYPTO_TYPE.PU_MEDIA_CRYPTO_NONE, enMediaCallbackType = PU_MEDIA_CALLBACK_TYPE.PU_MEDIA_CALLBACK_TYPE_META_FRAME,//回调方式为智能元数据 bKeepLive = true, szLocalIp = null, szReserved = new byte[32] }; clientInfo.szReserved[22] = 1;//szReserved[22]表示智能分析数据打包格式 0:XML,1:元数据 prpInfos[0] = clientInfo; var loginUserId = _ulIdentifyId; IntPtr pUsrData = (IntPtr)loginUserId; _fedRealPlayCallbackFaceCapture = FaceCaptureReaplayCallbackWithMetaFrame; var ulRealHandleCapture = HuaWeiSdkHelper.IVS_PU_RealPlay(_ulIdentifyId, prpInfos, _fedRealPlayCallbackFaceCapture, ref pUsrData); if (0 == ulRealHandleCapture) { MessageBox.Show(HuaWeiSdkHelper.GetLastErrorInfo()); return; } _timer.Enabled = true; } #region 人脸捕获数据回调 private static void FaceCaptureReaplayCallbackWithMetaFrame(IntPtr szBuffer, int lSize, IntPtr pUsrData) { var ptrstMetaTargetData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PU_META_DATA))); try { var bRet = HuaWeiSdkHelper.IVS_User_GetMetaData(szBuffer, lSize, LAYER_TWO_TYPE.TARGET, ref ptrstMetaTargetData); if (false == bRet) { return; } if ((IntPtr)0 == ptrstMetaTargetData) { return; } //将数据从非托管内存块封送到新分配的指定类型的托管对象 var pstMetaData = (PU_META_DATA)Marshal.PtrToStructure(ptrstMetaTargetData, typeof(PU_META_DATA)); //数据处理 if (0 == pstMetaData.usValidNumber) { return; } PU_UserData pstMetaUserData = new PU_UserData(); int nSizeofPuUserDataInC = Marshal.SizeOf(pstMetaUserData); byte[] dataFacePic = null;//人脸图片,如果捕获到人脸,会转成byte[]数组填充进来 byte[] dataFacePanorama = null;//检测到人脸的时候的全景图片 var faceFeature = new META_FACE_ATTRIBUTES();//附加的人脸上的数据 bool hasFaceFeature = false; int target = 0; for (int uIndex = 0; uIndex < pstMetaData.usValidNumber; ++uIndex) { IntPtr ptr2 = new IntPtr(pstMetaData.pstMetaUserData.ToInt32() + nSizeofPuUserDataInC * uIndex); pstMetaUserData = (PU_UserData)Marshal.PtrToStructure(ptr2, typeof(PU_UserData));//数据转成元用户数据结构 switch (pstMetaUserData.eType) { case LAYER_THREE_TYPE.TARGET_TYPE: target = pstMetaUserData.Union1.IntValue; break; case LAYER_THREE_TYPE.FACE_PIC://人脸抠图 dataFacePic = new byte[pstMetaUserData.Union1.stBinay.ulBinaryLenth]; //使用地址data来获取需要的内存块中的数据 Marshal.Copy(pstMetaUserData.Union1.stBinay.pBinaryData, dataFacePic, 0, (int)pstMetaUserData.Union1.stBinay.ulBinaryLenth); break; case LAYER_THREE_TYPE.FACE_PANORAMA://人脸全景 dataFacePanorama = new byte[pstMetaUserData.Union1.stBinay.ulBinaryLenth]; //使用地址data来获取需要的内存块中的数据 Marshal.Copy(pstMetaUserData.Union1.stBinay.pBinaryData, dataFacePanorama, 0, (int)pstMetaUserData.Union1.stBinay.ulBinaryLenth); break; case LAYER_THREE_TYPE.FACE_FEATURE://人脸属性 hasFaceFeature = true; faceFeature = pstMetaUserData.Union1.stFaceAttr; break; default: break; } } if ((int)Target.FaceCapture == target) { CaptureInfo info = new CaptureInfo { _dataFacePanorama = dataFacePanorama, _dataFacePic = dataFacePic, _faceFeature = faceFeature, _hasFaceFeature = hasFaceFeature }; _concurrentQueue.Enqueue(info);//加入到待处理队列中 } HuaWeiSdkHelper.IVS_User_FreeMetaData(out ptrstMetaTargetData);//释放数据占用空间 } catch (Exception e) { Console.WriteLine(e); throw; } finally { Marshal.FreeHGlobal(ptrstMetaTargetData);//释放内存 } } #endregion #endregion
在程序退出时,去释放资源
private void MainWindow_OnClosed(object sender, EventArgs e) { _isExit = true; if (_timer.Enabled) _timer.Enabled = false; if (_ulRealHandleId > 0) { HuaWeiSdkHelper.IVS_PU_StopRealPlay(_ulIdentifyId, _ulRealHandleId); } if (_ulIdentifyId > 0) { HuaWeiSdkHelper.IVS_PU_Logout(_ulIdentifyId); } HuaWeiSdkHelper.IVS_PU_Cleanup(); VideoFileStream.Close(); }
SDK把人脸抓拍注册成功后,摄像头本身带的有人脸识别算法,捕获到人脸后,会把数据回调给注册事件,注册事件中根据回调中给的人脸数据的内存地址取出数据,实例化成C#的数据结构,把图片转换成byte[]写入到队列里,定时处理队列时取出数据写成图片,即完成了摄像头人脸识别抓拍(有的摄像头带人脸比对算法,可直接进行人脸比对)。