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

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

上一篇:多目标进化优化算法在PU学习中的应用


下一篇:Python画奥林匹克环