1. 程式人生 > >C#_Demo_攝像頭實時_4執行緒人臉識別註冊開發全過程

C#_Demo_攝像頭實時_4執行緒人臉識別註冊開發全過程

效率有點低,大家看看哪裡開可以節省時間?
原始碼:https://github.com/catzhou2002/ArcFaceDemo

說實話,為了提高識別效率,我也是竭盡所能,幹了不少自認為的優化,如有興趣聽我說說。

第一部分 單執行緒時候的各種折騰

一、折騰LPASVLOFFSCREEN
話說這個LPASVLOFFSCREEN的結果文件裡面沒有說明,或者是我沒找到。
我也不知道從哪裡複製來的,主要折騰的是ppu8Plane[0]地址,一般操作是

  1. 鎖定圖片記憶體
  2. ppu8Plane[0]分配製定長度的記憶體
  3. 把圖片記憶體中的位元組複製到一個臨時陣列
  4. 然後用Marshal.Copy複製到指定的地址
  5. 解鎖圖片記憶體

我改成:

  1. 鎖定圖片記憶體
  2. ppu8Plane[0]指向圖片地址
  3. 等不需要LPASVLOFFSCREEN時(人臉檢測、獲取特徵值、性別判斷、年齡估算等結束後)解鎖圖片記憶體

就晚一點解鎖,省了好多事情,耗時由4毫秒沒成2微妙。當時就發了個帖:C# Bitmap轉ASVLOFFSCREEN的最佳方式?

後來覺得這名字實在記不住,也不C#,改成了ImageData,整個轉換過程如下:

var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height
), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); var imageData = new ImageData { PixelArrayFormat = 513,//Rgb24, Width = bitmap.Width, Height = bitmap.Height, Pitch = new int[4] { bmpData.Stride, 0, 0, 0 }, ppu8Plane = new IntPtr[4
] { bmpData.Scan0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero } }; .... bitmap.UnlockBits(bmpData);

其實如果是視訊圖片的話,圖片的寬度和高度都是固定的,想了想,沒折騰。

二、單執行緒時將獲取到的FaceModel直接做人臉比對的引數

ExtractFeature(_FaceMatchEngine, ref imageData, ref faceFeatureInput, out var <font color="#ff8c00">faceModel</font>);
FacePairMatch(_FaceMatchEngine, ref fm, ref <font color="#ff8c00">faceModel</font>, out float score);

一般操作是faceModel裡面的位元組複製到臨時位元組陣列,然後建立新的FaceModel,分配記憶體,在將臨時位元組陣列複製到FaceModel。

三、人臉庫直接用FaceModel

/// <summary>
    /// 人臉庫
    /// </summary>
    public class FaceLib
    {
        public List<Item> Items { get; set; } = new List<Item>();
        public class Item
        {
            /// <summary>
            /// 用於排序
            /// </summary>
            public long OrderId { get; set; }
            /// <summary>
            /// 檔名作為ID
            /// </summary>
            public string ID { get; set; }
            /// <summary>
            /// 人臉模型
            /// </summary>
<font color="#ff8c00">            public FaceModel FaceModel { get; set; }</font>  
        }
    }

四、比對結果>0.5就算成功
五、人臉庫增加OrderId
識別成功後再次比對就很快,應該是首發命中。

六、將人臉比對和結果顯示分開
一開始沒想太多,將人臉比對和結果顯示放在新視訊幀事件裡面,流程是:

  1. 新視訊幀(30幀/秒)
  2. 獲取檢測和識別的結果(人臉框和ID)
  3. 顯示檢測和識別的結果

結果視訊卡頓,獲取人臉特徵的200毫秒成為瓶頸,改成:

  • 人臉比對
 Task.Factory.StartNew(() =>
            {
                Task.Delay(1000).Wait();
                while (!_CancellationTokenSource.IsCancellationRequested)
                {
                    #region 200毫秒左右
                    MatchFrame(); 
                    #endregion
                }
            }, _CancellationTokenSource.Token);
  • 結果顯示
 private void VideoPlayer_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawRectangle(Pens.White, _FaceResult.Rectangle);
            e.Graphics.DrawString(_FaceResult.ID , this.Font, Brushes.White, _FaceResult.Rectangle.Left, _FaceResult.Rectangle.Top - 20);
        }

測試了一下,效果還可以,就在部落格園發表了
C# 虹軟SDK視訊人臉識別和註冊
,還順手弄了個打賞二維碼。
發表完覺得這麼辛苦寫出來的文章,必須到首頁去亮個相,9天后終於學會發表到部落格園首頁了,於是刪除了打賞二維碼,去首頁亮了個相。
話說首頁和非首頁效果著實不一樣,截圖為證:
這裡寫圖片描述

第二部分 多執行緒的折騰

一、確定4執行緒為最佳
各種測試後得出的結論,也不知道對不對,也不知道為什麼,哎。
因網友的要求,同步到了github

二、刪除了單執行緒
有了更快的,就不要慢的了。

三、n張臉如何分配給4個執行緒獲取特徵值?
動了不少腦筋,Interlocked.Increment是關鍵。
最終有改了下面的內容

  1. 如果只有一張臉(竊以為一張臉的概率比較高),也用Task,影響效率,增加了 if (detectResult.FaceCount ==
    1)
  2. Intptr之間複製位元組用CopyMemory比較快
  3. 兩三張臉的時候開4個執行緒不好,改成 new Task[TaskNum < detectResult.FaceCount ?
    TaskNum : detectResult.FaceCount]

四、識別結果(集)的折騰

  1. 弄了個結果集,按最大人臉數設了個List( Items = new List();)
  2. 增加了FaceFeatureInput FFI,省的每次都去建立
  3. 並將人臉方向設成1(Orient = 1)(因為是視訊圖片,其他方向的人臉,呵呵),人臉檢測後都不要去獲取人臉方向的值
  4. 增加並初始化了FaceModel(FaceModel FaceModel = new FaceModel() { Size =
    22020, PFeature = Marshal.AllocCoTaskMem(22020) };),獲取到的特徵位元組直接複製過來便可

五、儲存特徵值到人臉庫的時候同時儲存頭像
因為虹軟說了,sdk升級的時候,特徵值也有可能變化。那咱先把頭像儲存起來,到時候重新生成一下。
主要的操作是把矩形放大一點(Inflate((int)(r.Width * 0.5), (int)(r.Height * 0.5))),咱儲存的頭像怎麼著得是個人頭吧。

(想來條分割線,居然只有華麗的分割線,算了。順便吐槽一下,這個論壇的編輯器實在是讓人無語^_^)

各種折騰後,黔驢技窮了,10,000人臉的庫得出10張不認識的臉的結論,需要10秒鐘。當然,換好一點的電腦可以提高效率,如我的桌上型電腦(i5-7500),輸入圖片只有1張臉的時候,遍歷

  1. 1萬張人臉僅需390毫秒
  2. 5萬張人臉也就1525毫秒
  3. 10萬張人臉說我記憶體不夠,可能是我的程式是32位的緣故,換成64位的sdk估計3秒鐘也能搞定(太麻煩,不折騰了)

結論是:虹軟中型sdk用於考勤、小區門禁、寫字樓門禁等場所完全沒問題。

下一步我打算(其實已經差不多完成,我公司的專案——酒店自助機)改成單臉多執行緒識別,增加以下功能:

  1. 40次檢測人臉數為0,則確認為沒人,識別頻率降低
  2. 是否換人了?
  3. 同一個人3、4次識別不出ID後,確認為陌生人,不在遍歷
  4. 刷身份證獲取照片人臉比對後存入人臉庫

另外想跟企業微信結合開發開發門禁、CRM什麼的,有興趣的朋友一起交流交流?

洗洗睡了,晚安。