jeongong 发表于 2022-3-21 09:13

C#虹软 人脸识别1:N 10万级人脸库对比实现

本帖最后由 jeongong 于 2022-3-21 16:55 编辑

       最近在做一个医疗自助报到项目,本想用指纹识别方式去做,后来了解虹软的人脸识别还是比较人性化的,最重要的是免费。于是就用上了。按官方Demo写好后,发现还是挺快的,当人脸库图片加到10万级别时,算了下加载时间一张人脸得160毫秒左右,10万张单纯加载就得大半天了。(人脸图片直接存数据库,循环加载再提取人脸特征到内存)。后来看到可以预先保存人脸库到数据库。于是又倒腾了下,最终测试效果还是比较满意的,10万张人脸特征库,加载时间2~3秒。最最关键的是,可以不保存人脸图片了{:1_918:}。对比时,在最坏的情况要对比到第10万张,得花7秒左右的时间对比。这个对于实际业务运用中还是不够的。这个可以使用多引擎多线程加快对比,但由于本项目特定业务需求,只加载了有业务需求的库,所以大大降低了人脸库的数量。主要实现代码:


人脸库采集、人脸特征保存:
         /// <summary>
      /// 注册一张人脸照片到特征库中
      /// 返回1成功,如果返回其他就是注册失败,有可能是提取人脸特征失败(没有人脸,人脸太小,太模糊等原因)
      /// </summary>
      /// <param name="image">待注册的张人脸照片</param>
      /// <returns>"1"成功,其他就是失败的提示信息</returns>
      public static string RegisterImageEx(Image image)
      {
            cutImage = null;
            ifeature = new FaceFeature();
            if (image == null)
            {
                return "图片为空";
            }
            if (image.Width > 1536 || image.Height > 1536)
            {
                image = ImageUtil.ScaleImage(image, 1536, 1536);
            }
            if (image == null)
            {
                return "图片缩放后为空";
            }
            if (image.Width % 4 != 0)
            {
                image = ImageUtil.ScaleImage(image, image.Width - (image.Width % 4), image.Height);
            }
            //人脸检测
            ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image);
            //判断检测结果
            if (multiFaceInfo.faceNum > 0)
            {
                // 存在人脸
                MRECT rect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects);
                image = ImageUtil.CutImage(image, rect.left, rect.top, rect.right, rect.bottom);
                if (image.Width % 4 != 0)
                {
                  image = ImageUtil.ScaleImage(image, image.Width - (image.Width % 4), image.Height);
                }
                cutImage = image;
            }
            else
            {
                //图片中没有人脸
                return "图片中没有人脸";
            }

            ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo();
            FaceFeature feature = FaceUtil.ExtractFeatureEx(pImageEngine, image, out singleFaceInfo);
            if (singleFaceInfo.faceRect.left == 0 && singleFaceInfo.faceRect.right == 0)
            {
                return "特征值提取失败:未检测到人脸";
            }
            else
            {
                // 先清空,模拟人证比对,一次只能注册一张照片
                imagesFeatureListEx.Clear();
                // 添加特征值到人脸库
                imagesFeatureListEx.Add(feature);
                ifeature = feature;
                return "1";
            }
      }


/// <summary>
      /// 提取人脸特征
      /// </summary>
      /// <param name="pEngine">引擎Handle</param>
      /// <param name="image">图像</param>
      /// <returns>保存人脸特征结构体指针</returns>
      public static FaceFeature ExtractFeatureEx(IntPtr pEngine, Image image, out ASF_SingleFaceInfo singleFaceInfo)
      {
            FaceFeature localFeature = new FaceFeature();
            if (image.Width > 1536 || image.Height > 1536)
            {
                image = ImageUtil.ScaleImage(image, 1536, 1536);
            }
            else
            {
                image = ImageUtil.ScaleImage(image, image.Width, image.Height);
            }
            if (image == null)
            {
                singleFaceInfo = new ASF_SingleFaceInfo();

                return localFeature;
            }
            ImageInfo imageInfo = ImageUtil.ReadBMP(image);
            if (imageInfo == null)
            {
                singleFaceInfo = new ASF_SingleFaceInfo();
                return localFeature;
            }
            ASF_MultiFaceInfo multiFaceInfo = DetectFace(pEngine, imageInfo);
            singleFaceInfo = new ASF_SingleFaceInfo();
            localFeature = ExtractFeatureEx(pEngine, imageInfo, multiFaceInfo, out singleFaceInfo);
            MemoryUtil.Free(imageInfo.imgData);
            return localFeature;
      }

保存人脸特征库时,转成base64 字符串保存。


/// <summary>
      /// 传入一张摄像头获取的人脸图片,与特征库中的图片进行比较,返回比较的结果
      /// 含RBG活体检测
      /// </summary>
      /// <param name="bitmap">摄像头拍摄的图片</param>
      /// <param name="rgbVideoSourceWidth">摄像头镜头的宽度(程序中视频的宽度)</param>
      /// <param name="rgbVideoSourceHeight">摄像头镜头的高度(程序中视频的高度)</param>
      /// <returns></returns>
      public static FaceCompareResult DoRgbCompareEx(Bitmap bitmap)
      {
            faceIndex = -1;
            if (bitmap == null)
            {
                faceCompareResult.faceIndex = -1;
                faceCompareResult.msg = "待识别图片为空";
                return faceCompareResult;
            }
            if (null == patientFaceList || patientFaceList.Count <= 0)
            {
                faceCompareResult.faceIndex = -1;
                faceCompareResult.msg = "人脸库中没有图片";
                return faceCompareResult;
            }

            //检测得到图片中的全部人脸,得到Rect框
            ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
            //得到最大人脸
            ASF_SingleFaceInfo maxFace = FaceUtil.GetMaxFace(multiFaceInfo);
            //得到Rect
            MRECT rect = maxFace.faceRect;
            float offsetX = VideoSource.Width * 1f / bitmap.Width;
            float offsetY = VideoSource.Height * 1f / bitmap.Height;

            //保证只检测一帧,防止页面卡顿以及出现其他内存被占用情况
            if (isRGBLock == false)
            {
                isRGBLock = true;
                //异步处理提取特征值和比对,不然页面会比较卡
                ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
                {
                  if (rect.left != 0 && rect.right != 0 && rect.top != 0 && rect.bottom != 0)
                  {
                        try
                        {
                            if (isUninitEngine)
                            {
                              return;
                            }

                            lock (rectLock)
                            {
                              allRect.left = (int)(rect.left * offsetX);
                              allRect.top = (int)(rect.top * offsetY);
                              allRect.right = (int)(rect.right * offsetX);
                              allRect.bottom = (int)(rect.bottom * offsetY);
                            }
                            //调整图片数据,非常重要
                            ImageInfo imageInfo = ImageUtil.ReadBMP(bitmap);
                            if (imageInfo == null)
                            {
                              faceCompareResult.faceIndex = -1;
                              faceCompareResult.msg = "bitmap转ImageInfo失败";
                              return;
                            }
                            int retCode_Liveness = -1;
                            bool isLiveness = false;
                            if (isUninitEngine)
                            {
                              return;
                            }
                            //RGB活体检测
                            ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_RGB(pVideoRGBImageEngine, 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 (imageInfo != null)
                            {
                              MemoryUtil.Free(imageInfo.imgData);
                            }
                            if (isLiveness)
                            {
                              //提取人脸特征
                              FaceFeature feature = FaceUtil.ExtractFeatureEx(pVideoRGBImageEngine, bitmap, maxFace);
                              float similarity = 0f;
                              //得到比对结果

                              int result = CompareFeatureEx(feature, 0, patientFaceList.Count, out similarity);
                              if (result > -1)
                              {
                                    //将比对结果放到显示消息中,用于最新显示
                                    faceCompareResult.faceIndex = result;
                                    faceCompareResult.similarity = similarity;
                                    faceCompareResult.isLiveness = isLiveness;
                                    faceCompareResult.image = (Bitmap)bitmap.Clone(); //传回图像
                                    //faceCompareResult.msg = $"RGB活体,{result}号,相似度:{similarity}";
                                    faceIndex = result;
                              }
                              else
                              {
                                    //显示消息
                                    faceCompareResult.faceIndex = result;
                                    faceCompareResult.similarity = similarity;
                                    faceCompareResult.isLiveness = isLiveness;
                                    //faceCompareResult.msg = $"RGB活体,{result}号,相似度:{similarity}";
                                    faceIndex = result;
                              }
                            }
                            else
                            {
                              //显示消息
                              faceCompareResult.faceIndex = -1;
                              faceCompareResult.similarity = -1;
                              faceCompareResult.isLiveness = isLiveness;
                              faceCompareResult.msg = $"RGB假体";
                            }
                        }
                        catch (Exception ex)
                        {
                            //显示消息
                            faceCompareResult.faceIndex = -1;
                            faceCompareResult.similarity = -1;
                            faceCompareResult.isLiveness = false;
                            faceCompareResult.msg = ex.ToString();
                            Console.WriteLine(ex.Message);
                        }
                        finally
                        {
                            if (bitmap != null)
                            {
                              bitmap.Dispose();
                            }
                            isRGBLock = false;
                        }
                  }
                  else
                  {
                        //显示消息
                        faceCompareResult.faceIndex = -1;
                        faceCompareResult.similarity = -1;
                        faceCompareResult.isLiveness = false;
                        faceCompareResult.msg = "没有人脸";
                        lock (rectLock)
                        {
                            allRect.left = 0;
                            allRect.top = 0;
                            allRect.right = 0;
                            allRect.bottom = 0;
                        }
                  }
                  isRGBLock = false;
                }));
            }

            return faceCompareResult;
      }

/// <summary>
      /// 得到feature比较结果
      /// </summary>
      /// <param name="feature"></param>
      /// <returns></returns>
      private static int CompareFeatureEx(FaceFeature feature,int start,int end ,out float similarity)
      {
            int result = -1;
            similarity = 0f;

            //如果人脸库不为空,则进行人脸匹配

                for(int i=start;i<end;i++)
                {
                  //调用人脸匹配方法,进行匹配
                   int refCode= FaceUtil.ASFFaceFeatureCompare(pVideoRGBImageEngine, feature, new FaceFeature() { feature = Convert.FromBase64String(patientFaceList.Feature), featureSize = patientFaceList.FeatureSize }, out similarity);
                  if (refCode==0&&similarity >= threshold)
                  {
                        result = patientFaceList.PatientID;
                        break;
                  }
                }
            Console.WriteLine("Get Result:" + result+ " Get similarity:"+ similarity);
            return result;
      }

jeongong 发表于 2022-3-30 16:45

RemMai 发表于 2022-3-29 19:34
做过对接,其实挺简单的。
就是工厂任务终端。

是的对接是比较简单,就处理人脸库对比麻烦点,业务需求要求高,对比的响应基本得在3秒内,用户才能接受

jeongong 发表于 2022-3-21 16:58

tb612443 发表于 2022-3-21 15:09
感谢分享,为什么我看到的虹软都是收费的接口

有免费版,增值版。免费个人版可以注册100台设备。只不过每年都要更新本地的SDK

mengkaikai 发表于 2022-3-21 11:10

先收藏,谢谢楼主

hackgsl 发表于 2022-3-21 11:14

感谢分享。

zzyang115 发表于 2022-3-21 11:33

不懂,但看着就很厉害··

neptune88 发表于 2022-3-21 11:43

感谢楼主分享,方便的话,能否直接给个完整的demo,谢谢!

疯情都市 发表于 2022-3-21 12:59

这也会,确实牛B的

zhao2021 发表于 2022-3-21 13:44

感谢分享。

caxzan 发表于 2022-3-21 13:48

楼主,你这个有没有案例啊,就是登入界面,实现人脸

13149158 发表于 2022-3-21 14:05

这个会不会有点太凌乱了,我这复制粘贴之后有很多连接不上的句子似的。

symbolshen 发表于 2022-3-21 14:16

收藏一下 应该会很有用
页: [1] 2 3 4 5
查看完整版本: C#虹软 人脸识别1:N 10万级人脸库对比实现