使用C#对华为IPC摄像头二次开发(二)

  • A+
所属分类:.NET技术
摘要

上一篇我们实现了用SDK登录摄像头并实现预览(https://www.cnblogs.com/wdw984/p/13564195.html),这次我们实现通过SDK调用摄像头本身自带的人脸抓拍功能。

上一篇我们实现了用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[]写入到队列里,定时处理队列时取出数据写成图片,即完成了摄像头人脸识别抓拍(有的摄像头带人脸比对算法,可直接进行人脸比对)。