zoukankan      html  css  js  c++  java
  • 基于虹软人脸识别,实现身份认证和自助发卡

      去年下半年开始从BS开发转战CS开发了,相继做了一些大大小小的项目。最近在做的一个人脸识别挺有意思,作为一个初学者我也是摸着石头过河。这个项目主要是通过摄像头获取视频帧,然后使用SDK提取视频帧和身份证照片的特征,使用特征进行比对,比对通过的话,可以通过发卡机写入信息至卡片并吐出这张卡片,用户拿着这张卡片进行后续操作。对于发卡机只需要把一些操作方法进行封装,通过串口发送命令就可以了,身份证信息可以通过读卡器进行获取,这里主要聊一聊进行人脸识别的业务集成,希望对你有所帮助。

    一、流程图

    基本流程如下图,用户在自助发卡机前选择发卡操作,这时自助机会打开摄像头,只需要把身份证放到身份证读卡器上即可,然后摄像头捕获到的人脸与身份证上的人脸进行相似度比对,如果比对通过则由发卡机写入信息并进行发卡操作。

    二、申请并配置KEY

       我这里使用的是虹软视觉开发平台的SDK,首先注册开发者,然后新建应用,你会得到全新的APP_ID和SDK_KEY。个人认证用户每年共有100个设备可以免费激活,而且有效期是一年,一年之后需要给程序更换新的SDK。

    然后我们拿到对应的APP_ID以及SDK_KEY之后,就可以下载开发包了,我这里选择V3.0版本的SDK,然后配置到程序中。

    三、界面设计 

    大致的界面是这个样子,很普通,左侧是视频,右边是身份证照片以及一些状态

    四、初始化引擎 

    开发时用到了三个引擎,

     第一个是图片模式下的人脸检测引擎

    #region 图片引擎pImageEngine初始化
    //初始化引擎
    uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;
    //检测脸部的角度优先值
    int detectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT;
    //人脸在图片中所占比例,如果需要调整检测人脸尺寸请修改此值,有效数值为2-32
    int detectFaceScaleVal = 16;
    //最大需要检测的人脸个数
    int detectFaceMaxNum = 5;
    //引擎初始化时需要初始化的检测功能组合
    int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
    //初始化引擎,正常值为0,其他返回值请参考http://ai.arcsoft.com.cn/bbs/forum.php?mod=viewthread&tid=19&_dsign=dbad527e
    retCode = ASFFunctions.ASFInitEngine(detectMode, detectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine);
    if (retCode == 0)
    {
        lbl_msg.Text=("图片引擎初始化成功!
    ");
    }
    else
    {
        lbl_msg.Text = (string.Format("图片引擎初始化失败!错误码为:{0}
    ", retCode));
    }
    #endregion

    第二个是视频模式下的人脸检测引擎

    #region 初始化视频模式下人脸检测引擎
    uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO;
    int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION;
    retCode = ASFFunctions.ASFInitEngine(detectModeVideo, detectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine);
    if (retCode == 0)
    {
        lbl_msg.Text=("视频引擎初始化成功!
    ");
    }
    else
    {
        lbl_msg.Text = (string.Format("视频引擎初始化失败!错误码为:{0}
    ", retCode));
    }
    #endregion

    第三个是视频专用FR引擎,进行活体检测

    #region 视频专用FR引擎
    detectFaceMaxNum = 1;
    combinedMask = FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_FACE3DANGLE | FaceEngineMask.ASF_LIVENESS;
    retCode = ASFFunctions.ASFInitEngine(detectMode, detectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoImageEngine);
    Console.WriteLine("InitVideoEngine Result:" + retCode);
    
    if (retCode == 0)
    {
        lbl_msg.Text = ("视频专用FR引擎初始化成功!
    ");
    }
    else
    {
        lbl_msg.Text = (string.Format("视频专用FR引擎初始化失败!错误码为:{0}
    ", retCode));
    }
    // 摄像头初始化
    filterInfoCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
    lbl_msg.Text = (string.Format("摄像头初始化完成...
    "));
    #endregion

      视频处理这里使用的是AForge.Video 视频处理类库,然后我们在电脑上接上USB摄像头,通过此类库就可以调用摄像头的开关了,那具体的人脸识别我们肯定要放在视频流渲染事件上了。

      我们首先将身份证放在身份证阅读器上,获取到身份信息,并把身份信息中的人脸照片拿出来,这时我们要用到pImageEngine图片引擎去从证件照中提取人脸特征值。然后我在能够读到身份证信息 和 视频信息都OK的情况下再去获取当前摄像头下的图片,

    //得到当前摄像头下的图片
    Bitmap bitmap = videoSource.GetCurrentVideoFrame();
    //传入比对函数中进行比对
    CompareImgWithIDImg(bitmap, e);

    五、人脸比对

    /// <summary>
    /// 比对函数,将每一帧抓拍的照片和身份证照片进行比对
    /// </summary>
    /// <param name="bitmap"></param>
    /// <param name="e"></param>
    /// <returns></returns>
    private bool CompareImgWithIDImg(Bitmap bitmap, PaintEventArgs e)
    {
        recTimes--;
        if (bitmap == null)
        {
            return false;
        }
        Graphics g = e.Graphics;
        float offsetX = videoSource.Width * 1f / bitmap.Width;
        float offsetY = videoSource.Height * 1f / bitmap.Height;
        //检测人脸,得到Rect框
        ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
        //得到最大人脸
        ASF_SingleFaceInfo maxFace = FaceUtil.GetMaxFace(multiFaceInfo);
        //得到Rect
        MRECT rect = maxFace.faceRect;
        float x = rect.left * offsetX;
        float width = rect.right * offsetX - x;
        float y = rect.top * offsetY;
        float height = rect.bottom * offsetY - y;
        //根据Rect进行画框
        g.DrawRectangle(pen, x, y, width, height);
        //将上一帧检测结果显示到页面上
        g.DrawString(trackUnit.message, font, brush, x, y + 5);
        //保证只检测一帧,防止页面卡顿以及出现其他内存被占用情况
        if (isLock == false)
        {
            isLock = true;
            //异步处理提取特征值和比对,不然页面会比较卡
            ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
            {
                if (rect.left != 0 && rect.right != 0 && rect.top != 0 && rect.bottom != 0)
                {
                    try
                    {
                        //提取人脸特征
                        IntPtr feature = FaceUtil.ExtractFeature(pVideoImageEngine, bitmap, maxFace);
                        float similarity = CompareTwoFeatures(feature, idCardHelper.idInfo.imageFeature);
                        this.similarity.Text = ("相似度为: " + similarity.ToString("P")); ; //显示在界面上
                        this.similarity.ForeColor = similarity > threshold ? Color.Green : Color.Red;
                        //得到比对结果
                        int result = (CompareTwoFeatures(feature, idCardHelper.idInfo.imageFeature) >= threshold) ? 1 : -1;
                        if (result > -1)
                        {
                            bool isLiveness = false;
                            ImageInfo imageInfo = ImageUtil.ReadBMP(bitmap); //调整图片数据
                            if (imageInfo == null)
                                return;
                            int retCode_Liveness = -1;
                            //RGB活体检测
                            ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_RGB(pVideoImageEngine, imageInfo, multiFaceInfo, out retCode_Liveness);
                            //判断检测结果
                            if (retCode_Liveness == 0 && liveInfo.num > 0)
                            {
                                int isLive = MemoryUtil.PtrToStructure<int>(liveInfo.isLive);
                                isLiveness = (isLive == 1) ? true : false;
                            }
                            if (isLiveness)//活体检测成功
                            {
                                //存放当前人脸识别的相似度
                                idCardHelper.idInfo.similarity = similarity;
                                //记录下当前的摄像头的人脸抓拍照
                                idCardHelper.idInfo.capImage = bitmap;
                                //验证通过则不再是当前身份证,等待下一次身份证
                                idCardHelper.idInfo.isRight = false;
                                //在子线程中输出信息到messageBox
                                AppendText p = new AppendText(AddTextToMessBox);
                                lbl_msg.Invoke(p, "人脸验证成功,请取卡...
    ");
                                pass = 1;
                                idCardHelper.idInfo.isPass = 1;
                                //将比对结果放到显示消息中,用于最新显示
                                trackUnit.message = string.Format("通过验证,相似度为{0}", similarity);
                                FileHelper.DeleteFile(m_strPath);   //删除验证过的本地文件
                                Thread.Sleep(1000);//延时1秒
                                this.IDPbox.Image = defaultImage;//照片恢复默认照片
                                trackUnit.message = "";//人脸识别框文字置空
                                setFormResultValue(true);
                            }
                            else
                            {
                                pass = 0;//标志未通过
                                trackUnit.message = "未通过,系统识别为照片";
                                AppendText p = new AppendText(AddTextToMessBox);
                                lbl_msg.Invoke(p, "抱歉,您未通过人脸验证...
    ");
                                FileHelper.DeleteFile(m_strPath);//删除验证过的本地文件
                            }
                        }
                        else
                        {
                            pass = 0;//标志未通过
                            trackUnit.message = "未通过人脸验证";
                            AppendText p = new AppendText(AddTextToMessBox);
                            lbl_msg.Invoke(p, "抱歉,您未通过人脸验证...
    ");
                            FileHelper.DeleteFile(m_strPath);//删除验证过的本地文件
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        FileHelper.DeleteFile(m_strPath);//删除验证过的本地文件
                    }
                    finally
                    {
                        isLock = false;
                    }
                }
                isLock = false;
            }));
        }
        return false;
    }
    
    /// <summary>
    /// 比较两个特征值的相似度,返回相似度
    /// </summary>
    /// <param name="feature1"></param>
    /// <param name="feature2"></param>
    /// <returns></returns>
    private float CompareTwoFeatures(IntPtr feature1, IntPtr feature2)
    {
        float similarity = 0.0f;
        //调用人脸匹配方法,进行匹配
        ASFFunctions.ASFFaceFeatureCompare(pVideoImageEngine, feature1, feature2, ref similarity);
        return similarity;
    }

    我们这个时候是获取的视频中的图片,需要用到视频引擎pVideoEngine去从bitmap中检测人脸并获取最大的那张脸,并提取人脸特征值。获取到视频中和照片中两张人脸的特征值了,那接下来就是将两张照片交给虹软人脸比对算法获取相似度,我们可以设置一个阈值,超过90%我们就可以认定是同一个人,这个还是要在实际项目中去做权衡。

       这样我们就算是比对成功,可以进行后续业务了。但是还没完,我刷完身份证后,迅速用身份证对着摄像头,发现竟然也比对成功了,那如果这样的话,即使不是本人,别人从手机里面拿着照片就可以进行认证了,必然造成不安全性,于是我就把活体检测加上了,活体检测,顾名思义,就是看看是不是个大活人而非照片。

    int retCode_Liveness = -1;
    //RGB活体检测
    ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_RGB(pVideoImageEngine, imageInfo, multiFaceInfo, out retCode_Liveness);
    //判断检测结果
    if (retCode_Liveness == 0 && liveInfo.num > 0)
    {
        int isLive = MemoryUtil.PtrToStructure<int>(liveInfo.isLive);
        isLiveness = (isLive == 1) ? true : false;
    }
    if (isLiveness)//活体检测成功

    加上活体检测,即使是照片,就算相似度达到90%以上,我们也不会放过。

    六、遇到的问题

      到这里基本功能都已经结束了,但是在多次的调试中发现,时不时就会来一次闪退,就是内存溢出。因为我的这个页面是单独弹出来的,这是一个子页面,之前关闭窗口的时候没有把引擎释放,所以导致每次初始化一个引擎大概需要50M左右的内存,迟早会出现内存溢出的情况。于是我就在这个子窗口关闭的时候,对这三个引擎进行释放,这个问题就最终解决了。

    /// <summary>
    /// 窗体关闭事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void IdentityVerify_FormClosed(object sender, FormClosedEventArgs e)
    {
        //销毁引擎
        int retCode = ASFFunctions.ASFUninitEngine(pImageEngine);
        Console.WriteLine("UninitEngine pImageEngine Result:" + retCode);
        //销毁引擎
        retCode = ASFFunctions.ASFUninitEngine(pVideoEngine);
        Console.WriteLine("UninitEngine pVideoEngine Result:" + retCode);
        //销毁引擎
        retCode = ASFFunctions.ASFUninitEngine(pVideoImageEngine);
        Console.WriteLine("UninitEngine pVideoImageEngine Result:" + retCode);
        if (videoSource.IsRunning)
        {
            videoSource.SignalToStop(); //关闭摄像头
        }
        idCardHelper.CloseService();
        this.Dispose();
        this.Close();
        MemoryUtil.ClearMemory();
    }

     GitHub已开源此Demo。

      

  • 相关阅读:
    R语言:用简单的文本处理方法优化我们的读书体验
    R语言-用R眼看琅琊榜小说的正确姿势
    R语言-Kindle特价书爬榜示例 & 输出HTML小技巧
    Hadoop里的数据挖掘应用-Mahout——学习笔记<三>
    Hadoop-HBASE案例分析-Hadoop学习笔记<二>
    Hadoop概括——学习笔记<一>
    R语言——七月
    R语言:ggplot2精细化绘图——以实用商业化图表绘图为例
    R语言学习笔记之: 论如何正确把EXCEL文件喂给R处理
    R语言学习笔记-机器学习1-3章
  • 原文地址:https://www.cnblogs.com/yumaster/p/14523591.html
Copyright © 2011-2022 走看看