關於人臉識別的視頻圖片處理

這篇隨筆記錄一下最近作人臉識別遇到過的一些問題的解決辦法,方便增強印象。以雙目攝像頭爲例。前端

首先是封裝:算法

視頻處理在主窗體下操做比較簡單,可是若是是將視頻處理過程封裝起來,就稍微有些麻煩了,會引發線程安全的問題。安全

首先將視頻控件的對象或者是句柄傳到接口裏,而後對該對象進行操做。異步

 1         private AForge.Controls.VideoSourcePlayer VideoPlayer { get; set; }
 2         private AForge.Controls.VideoSourcePlayer VideoPlayer2 { get; set; }
 3       public void SetAFVideo(bool isDefault, AForge.Controls.VideoSourcePlayer video)
 6         {
 7             if (isDefault)
 8             {
 9                 this.VideoPlayer = video;
10                 this.VideoPlayer.Paint += VideoPlayer_Paint;
11             }
12             else
13             {
14                 this.VideoPlayer2 = video;
15             }
16         }

有了視頻控件的對象後,接着是打開攝像頭ide

 1  /// <summary>
 2         /// 打開攝像頭
 3         /// </summary>
 4         /// <param name="isDefault"></param>
 5         /// <param name="videoName">攝像頭名稱</param>
 6         /// <returns></returns>
 7         public bool AFOpenVideo(bool isDefault, string videoName)
 8         {
 9             FaceResult.Reset();          
10             _isStopDetect = false;
11             if (isDefault)
12             {
13                 if (this.VideoPlayer == null) return false;
14                 this.VideoPlayer.Show();
15                 if (_deviceVideoClr == null)
16                 {
17                     _deviceVideoClr = Video.GetVideoSource(videoName);
18                 }
19                 this.VideoPlayer.VideoSource = _deviceVideoClr;
20                 this.VideoPlayer.Start();
21                 return true;
22             }
23             else
24             {
25                 if (this.VideoPlayer2 == null) return false;
26                 this.VideoPlayer2.Show();
27                 if (_deviceVideoGry == null)
28                 {
29                     _deviceVideoGry = Video.GetVideoSource(videoName);
30                 }
31                 this.VideoPlayer2.VideoSource = _deviceVideoGry;
32                 this.VideoPlayer2.Start();
33                 return true;
34             }
35         }

關閉攝像頭。關閉攝像頭,有兩種方式,一種是在主線程中直接關閉,另一種是在子線程裏關閉this

/// <summary>
        /// 關閉攝像頭
        /// </summary>
        public void CloseVideo()
        {
            _isStopDetect = true;
            Thread.Sleep(150);
        }
        /// <summary>
        /// 關閉攝像頭
        /// </summary>
        public void CloseVideoDirect()
        {
            CancelFaceDetect();
            if (this.VideoPlayer != null)
            {
                if (this.VideoPlayer.IsRunning)
                {
                    VideoPlayer.SignalToStop();
                    VideoPlayer.Hide();
                }
            }
            if (this.VideoPlayer2 != null)
            {
                if (this.VideoPlayer2.IsRunning)
                {
                    VideoPlayer2.SignalToStop();
                    VideoPlayer2.Hide();
                }
            }
        }

取消人臉比對spa

 private void CancelFaceDetect()
        {
            _mEvent.Set();
        }

人臉識別算法初始化線程

 /// <summary>
        /// 人臉算法初始化
        /// </summary>
        /// <returns></returns>
        public void InitFaceEngine(Action<bool, string> action)
        {
               //初始化過程
        }

人臉比對過程,在子線程裏處理,防止頁面卡頓,同時每次只處理一幀。經過ManualResetEvent信號變量來終止人臉比對過程,視頻比對過程在code

FaceDetect方法裏。比對完成,必須釋放img對象佔用的內存
 
 1 public void StartFaceDetect()
 2         {
 3             if (!_isFaceCompare) return;
 4             _mEvent.Reset();
 5             Task.Factory.StartNew(() =>
 6             {
 7                 Task.Delay(2000).Wait();
 8                 while (true)
 9                 {
10                     try
11                     {
12                         if (_mEvent.WaitOne(100))
13                         {
14                             FaceResult.Reset();
15                             break;
16                         }
17                         var imgClr = this.VideoPlayer.GetCurrentVideoFrame();
18                         var imgGry = this.VideoPlayer2.GetCurrentVideoFrame();
19                         float offsetX = VideoPlayer.Width * 1f / imgClr.Width;
20                         float offsetY = VideoPlayer.Height * 1f / imgClr.Height;
21                         FaceResult.OffsetX = offsetX;
22                         FaceResult.OffsetY = offsetY;
23                         FaceDetect(imgClr, imgGry);
24                         imgClr.Dispose();
25                         imgGry.Dispose();
26                     }
27                     catch (System.Exception ex)
28                     {
29                         throw new Exception(ex.Message);
30                     }
31                 }
32             });
33         }

給人臉框選。經過_isStopDetect變量來標識,在人臉識別經過後是否關閉攝像頭orm

 1  private void VideoPlayer_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
 2         {
 3             for (int i = 0; i < FaceResult.FaceNumber; i++)
 4             {
 5                 //根據Rect進行畫框
 6                 e.Graphics.DrawRectangle(_rectPen, FaceResult.X, FaceResult.Y, FaceResult.Width, FaceResult.Height);
 7                 if (_trackUnit.message != "" && FaceResult.X > 0 && FaceResult.Y > 0)
 8                 {
 9                     //將上一幀檢測結果顯示到頁面上
10                     e.Graphics.DrawString(_trackUnit.message, _trackFont, _trackBrush, FaceResult.X, FaceResult.Y + 5);
11                 }
12             }
13             if (_isStopDetect)
14             {
15                 CloseVideoDirect();
16             }
17         }

將比對結果實時告訴前端。比對的信息經過事件FaceDetectShowHandler 來通知到前端,人臉比對成功經過事件FaceDectctResultHandler通知前端,前端收到FaceDectctResultHandler後執CloseVideo(),以關閉攝像頭

 1  public Action<string> FaceDetectShowHandler { get; set; }
 2  public Action FaceDectctResultHandler { get; set; }
 3  /// <summary>
 4  /// 人臉比對
 5  /// </summary>
 6  /// <param name="bitClr"></param>
 7  /// <param name="bitGry"></param>
 8   public void FaceDetect(Bitmap bitClr, Bitmap bitGry)
 9   {
10 //...
11       this.__errString = "沒有檢測到人臉,請正視相機!!";
12      FaceDetectShowHandler?.Invoke(_errString);
13     this.__errString = "類似度高";
14        FaceDectctResultHandler?.Invoke();
15 
16  }
經過前面的示例代碼知道,視頻比對是在一個task子線程進行,前端要收到比對信息的消息通知,也是在子線程進行,若是將消息顯示在前端界面上進行刷新,就必須經過委託來刷新界面,不然引發線程安全的問題。同理,前端收到FaceDectctResultHandler事件通知後,要關閉攝像頭,就不能直接調用close方法來關閉,而必須在主線程關閉,這裏經過一個變量_isStopDetect在paint事件裏關閉攝像頭,由於Paint事件是主線程,因此主線程裏關閉攝像頭徹底沒問題。另外關於視頻圖像的處理,有兩種方式,一種是用一個while循環,一種是經過線程池,每捕捉到一幀圖像就丟到線程池裏異步處理,可是用線程池可能會引起一個問題,就是引用類型變量在一個循環沒有處理完時,變量就被修改了,此時容易引發程序崩潰。用while循環,須要開啓一個子線程,防止頁面卡頓,是一個同步處理的過程。在關閉攝像頭時,有一個休眠150毫秒的動做,目的是讓ManualResetEvent先收到終止的信號,終止while循環,而後執行關閉攝像頭的動做,不然時間太短,致使while一直執行。
相關文章
相關標籤/搜索