欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#實現(xiàn)基于ffmpeg加虹軟的人臉識別的示例

 更新時間:2017年10月10日 08:57:24   作者:sweetwxh  
本篇文章主要介紹了C#實現(xiàn)基于ffmpeg加虹軟的人臉識別的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

關于人臉識別

目前的人臉識別已經(jīng)相對成熟,有各種收費免費的商業(yè)方案和開源方案,其中OpenCV很早就支持了人臉識別,在我選擇人臉識別開發(fā)庫時,也橫向?qū)Ρ攘巳N庫,包括在線識別的百度、開源的OpenCV和商業(yè)庫虹軟(中小型規(guī)模免費)。

百度的人臉識別,才上線不久,文檔不太完善,之前聯(lián)系百度,官方也給了我基于Android的Example,但是不太符合我的需求,一是照片需要上傳至百度服務器(這個是最大的問題),其次,人臉的定位需要自行去實現(xiàn)(捕獲到人臉后上傳進行識別)。

OpenCV很早以前就用過,當時做人臉+車牌識別時,最先考慮的就是OpenCV,但是識別率在當時不算很高,后來是采用了一個電子科大的老師自行開發(fā)的識別庫(相對易用,識別率也還不錯),所以這次準備做時,沒有選擇OpenCV。

虹軟其實在無意間發(fā)現(xiàn)的,當時正在尋找開發(fā)庫,正在測試Python的一個方案,就發(fā)現(xiàn)有新聞說虹軟的識別庫全面開放并且可以免費使用,而且是離線識別,所以就下載嘗試了一下,發(fā)現(xiàn)識別率還不錯,所以就暫定了采用虹軟的識別方案。這里主要就給大家分享一下開發(fā)過程當中的一些坑和使用心得,順便開源識別庫的C# Wrapper。

SDK的C# Wrapper

由于虹軟的庫是采用C++開發(fā)的,而我的應用程序采用的是C#,所以,需要對庫進行包裝,便于C#的調(diào)用,包裝的主要需求是可以在C#中快速方便的調(diào)用,無需考慮內(nèi)存、指針等問題,并且具備一定的容錯性。Wrapper庫目前已經(jīng)開源,大家可以到Github上進行下載,地址點擊這里。Wrapper庫基本上沒有什么可以說的,無非是對PInvoke的包裝,只是里面做了比較多的細節(jié)處理,屏蔽了調(diào)用細節(jié),提供了相對高層的函數(shù)。有興趣的可以看看源代碼。

Wrapper庫的使用例子

基本使用

人臉檢測(靜態(tài)圖片):

using (var detection = LocatorFactory.GetDetectionLocator("appId", "sdkKey"))
{
  var image = Image.FromFile("test.jpg");
  var bitmap = new Bitmap(image);

  var result = detection.Detect(bitmap, out var locateResult);
  //檢測到位置信息在使用完畢后,需要釋放資源,避免內(nèi)存泄露
  using (locateResult)
  {
    if (result == ErrorCode.Ok && locateResult.FaceCount > 0)
    {
      using (var g = Graphics.FromImage(bitmap))
      {
        var face = locateResult.Faces[0].ToRectangle();
        g.DrawRectangle(new Pen(Color.Chartreuse), face.X, face.Y, face.Width, face.Height);
      }

      bitmap.Save("output.jpg", ImageFormat.Jpeg);
    }
  }
}

人臉跟蹤(人臉跟蹤一般用于視頻的連續(xù)幀識別,相較于檢測,又更高的執(zhí)行效率,這里用靜態(tài)圖片做例子,實際使用和檢測沒啥區(qū)別):

using (var detection = LocatorFactory.GetTrackingLocator("appId", "sdkKey"))
{
  var image = Image.FromFile("test.jpg");
  var bitmap = new Bitmap(image);

  var result = detection.Detect(bitmap, out var locateResult);
  using (locateResult)
  {
    if (result == ErrorCode.Ok && locateResult.FaceCount > 0)
    {
      using (var g = Graphics.FromImage(bitmap))
      {
        var face = locateResult.Faces[0].ToRectangle();
        g.DrawRectangle(new Pen(Color.Chartreuse), face.X, face.Y, face.Width, face.Height);
      }

      bitmap.Save("output.jpg", ImageFormat.Jpeg);
    }
  }
}

人臉對比:

using (var proccesor = new FaceProcessor("appid",
        "locatorKey", "recognizeKey", true))
{
  var image1 = Image.FromFile("test2.jpg");
  var image2 = Image.FromFile("test.jpg");

  var result1 = proccesor.LocateExtract(new Bitmap(image1));
  var result2 = proccesor.LocateExtract(new Bitmap(image2));
  
  //FaceProcessor是個整合包裝類,集成了檢測和識別,如果要單獨使用識別,可以使用FaceRecognize類
  //這里做演示,假設圖片都只有一張臉
  //可以將FeatureData持久化保存,這個即是人臉特征數(shù)據(jù),用于后續(xù)的人臉匹配
  //File.WriteAllBytes("XXX.data", feature.FeatureData);FeatureData會自動轉(zhuǎn)型為byte數(shù)組

  if ((result1 != null) & (result2 != null))
    Console.WriteLine(proccesor.Match(result1[0].FeatureData, result2[0].FeatureData, true));
}

使用注意事項

LocateResult(檢測結(jié)果)和Feature(人臉特征)都包含需要釋放的內(nèi)存資源,在使用完畢后,記得需要釋放,否則會引起內(nèi)存泄露。FaceProcessor和FaceRecognize的Match函數(shù),在完成比較后,可以自動釋放,只需要最后兩個參數(shù)指定為true即可,如果是用于人臉匹配(1:N),則可以采用默認參數(shù),這種情況下,第一個參數(shù)指定的特征數(shù)據(jù)不會自動釋放,用于循環(huán)和特征庫的特征進行比對。

整合的完整例子

在Github上,有完整的FaceDemo例子,里面主要實現(xiàn)了通過ffmpeg采集RTSP協(xié)議的圖像(使用??档臄z像機),然后進行人臉匹配。在開發(fā)過程中遇到不少的坑。

人臉識別的首要工作就是捕獲攝像機視頻幀,這一塊上是坑的最久的,因為最開始采用的是OpenCV的包裝庫,Emgu.CV,在開發(fā)過程中,捕獲USB攝像頭時,倒是問題不大,沒有出現(xiàn)過異常。在捕獲RTSP視頻流時,會不定時的出現(xiàn)AccessviolationException異常,短則幾十分鐘,長則幾個小時,總之就是不穩(wěn)定。在官方Github地址上,也提了Issue,他們給出的答復是屏蔽的我業(yè)務邏輯,僅捕獲視頻流試試,結(jié)果問題依然,所以,我基本坑定了試Emgu.CV上面的問題。后來經(jīng)過反復的實驗,最終確定了選擇ffmpeg。

ffmepg主要采用ProcessStartInfo進行調(diào)用,我采用的是NReco.VideoConverter(一個ffmpeg調(diào)用的包裝,可以通過nuget搜索安裝),雖然ffmpeg解決了穩(wěn)定性問題,但是實際開發(fā)時,也遇到了不少坑,其中,最主要的是NReco.VideoConverter沒有任何文檔和例子(實際有,需要75刀購買),所以,自己研究了半天,如何捕獲視頻流并轉(zhuǎn)換為Bitmap對象。只要實現(xiàn)這一步,后續(xù)就是調(diào)用Wrapper就行了。

FaceDemo詳解

上面說到了,通過ffmpeg捕獲視頻流并轉(zhuǎn)換Bitmap是重點,所以,這里也主要介紹這一塊。

首先是ffmpeg的調(diào)用參數(shù):

var setting =
new ConvertSettings
{
  CustomOutputArgs = "-an -r 15 -pix_fmt bgr24 -updatefirst 1"
}; //-s 1920x1080 -q:v 2 -b:v 64k

task = ffmpeg.ConvertLiveMedia("rtsp://admin:12qwaszxA@192.168.1.64:554/h264/ch1/main/av_stream", null,
outputStream, Format.raw_video, setting);
task.OutputDataReceived += DataReceived;
task.Start();

-an表示不捕獲音頻流,-r表示幀率,根據(jù)需求和實際設備調(diào)整此參數(shù),-pix_fmt比較重要,一般情況下,指定為bgr24不會有太大問題(還是看具體設備),之前就是用成了rgb24,結(jié)果捕獲出來的圖像,人都變成阿凡達了,顏色是反的。最后一個參數(shù),坑的我差點放棄這個方案。本身,ffmpeg在調(diào)用時,需要指定一個文件名模板,捕獲到的輸出會按照模板生成文件,如果要將數(shù)據(jù)輸出到控制臺,則最后傳入一個-即可,最開始沒有指定updatefirst,ffmpeg在捕獲了第一幀后就拋出了異常,最后查了半天ffmpeg說明(完整參數(shù)說明非常多,輸出到文本有1319KB),發(fā)現(xiàn)了這個參數(shù),表示持續(xù)更新第一個文件。最后,在調(diào)用視頻捕獲是,需要指定輸出格式,必須指定為Format.raw_video,實際上這個格式名稱有些誤導人,按道理將應該叫做raw_image,因為最終輸出的是每幀原始的位圖數(shù)據(jù)。

到此為止,還并沒有解決視頻流數(shù)據(jù)的捕獲,因為又來一個坑,ProcessStartInfo的控制臺緩沖區(qū)大小只有32768 bytes,即,每一次的輸出,實際上并不是一個完整的位圖數(shù)據(jù)。

//完整代碼參加Github源代碼
//代碼片段1
private Bitmap _image;
private IntPtr _pImage;

{
  _pImage = Marshal.AllocHGlobal(1920 * 1080 * 3);
  _image = new Bitmap(1920, 1080, 1920 * 3, PixelFormat.Format24bppRgb, _pImage);
}

//代碼片段2
private MemoryStream outputStream;

private void DataReceived(object sender, EventArgs e)
{
  if (outputStream.Position == 6220800)
    lock (_imageLock)
    {
      var data = outputStream.ToArray();

      Marshal.Copy(data, 0, _pImage, data.Length);

      outputStream.Seek(0, SeekOrigin.Begin);
    }
}

花了不少時間摸索(不要看只有幾行,人都整崩潰了),得出了上述代碼。首先,我捕獲的圖像數(shù)據(jù)是24位的,并且圖像大小是1080p的,所以,實際上,一個原始位圖數(shù)據(jù)的大小為stride * height,即width * 3 * height,大小為6220800 bytes。所以,在判斷了捕獲數(shù)據(jù)到達這個大小后,就進行Bitmap轉(zhuǎn)換處理,然后將MemoryStream的位置移動到最開始。需要注意的時,由于捕獲到的是原始數(shù)據(jù)(不包含bmp的HeaderInfo),所以注意看Bitmap的構(gòu)造方式,是通過一個指向原始數(shù)據(jù)位置的指針就行構(gòu)造的,更新該圖像時,也僅需要更新指針指向的位置數(shù)據(jù)即可,無需在建立新的Bitmap實例。

位圖數(shù)據(jù)獲取到了,就可以進行識別處理了,高高興興的加上了識別邏輯,但是現(xiàn)實總是充滿了意外和驚喜,沒錯,坑又來了。沒有加入識別邏輯的時候,捕獲到的圖像在PictureBox上顯示非常正常,清晰、流暢,加上識別邏輯后,開始出現(xiàn)花屏(捕獲到的圖像花屏)、拖影、顯示延遲(至少會延遲10-20秒以上)、程序卡頓,總之就是各種問題。最開始,我的識別邏輯寫到DataReceived方法里面的,這個方法是運行于主線程外的另一個線程中的,其實按道理將,捕獲、識別、顯示位于一個線程中,應該是不會出現(xiàn)問題,我估計(不確定,沒有去深入研究,如果誰知道實際原因,可以留言告訴我),是因為ffmpeg的原因,因為ffmpeg是單獨的一個進程在跑,他的數(shù)據(jù)捕獲是持續(xù)在進行的,而識別模塊的處理時間大于每一幀的采集時間,所以,緩沖區(qū)中的數(shù)據(jù)沒有得到及時處理,ffmpeg接收到的部分圖像數(shù)據(jù)(大于32768的數(shù)據(jù))被丟棄了,然后就出現(xiàn)了各種問題。最后,又是一次耗時不短的探索之旅。

private void Render()
{
  while (_renderRunning)
  {
    if (_image == null)
      continue;

    Bitmap image;

    lock (_imageLock)
    {
      image = (Bitmap) _image.Clone();
    }

    if (_shouldShot){
      WriteFeature(image);
      _shouldShot = false;
    }

    Verify(image);

    if (videoImage.InvokeRequired)
      videoImage.Invoke(new Action(() => { videoImage.Image = image; }));
    else
      videoImage.Image = image;
  }
}

如上代碼所述,我單獨開了一個線程,用于圖像的識別處理和顯示,每次都從已捕獲到的圖像中克隆出新的Bitmap實例進行處理。這種方式的缺點在于,有可能會導致丟幀的現(xiàn)象,因為上面說到了,識別時間(如果檢測到新的人臉,那么加上匹配,大約需要130ms左右)大于每幀時間,但是并不影響識別效果和需求的實現(xiàn),基本丟棄的幀可以忽律。最后,運行,穩(wěn)定了、完美了,實際也感覺不到丟幀。

Demo程序,我運行了大約4天左右,中間沒有出現(xiàn)過任何異常和識別錯誤。

寫在最后

雖然虹軟官方表示,免費識別庫適用于1000人臉庫以下的識別,實際上,做一定的工作(工作量其實也不?。?,也是可以實現(xiàn)較大規(guī)模的人臉搜索滴。例如,采用多線程進行匹配,如果人臉庫人臉數(shù)量大于1000,則可以考慮每個線程分別進行處理,人臉特征數(shù)據(jù)做緩存(一個人臉的特征數(shù)據(jù)是22KB,對內(nèi)存要求較高),以提升程序的識別搜索效率。或者人臉庫特別大的情況下,可以采用分布式處理,人臉特征加載到Redis數(shù)據(jù)庫當中,多個進程多個線程讀取處理,每個線程上傳自己的識別結(jié)果,然后主進程做結(jié)果合并判斷工作,主要的挑戰(zhàn)就在于多線程的工作分配一致性和對單點故障的容錯性。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • C# 獲取硬盤號,CPU信息,加密解密技術的步驟

    C# 獲取硬盤號,CPU信息,加密解密技術的步驟

    這篇文章主要介紹了C# 獲取硬盤號,CPU信息,加密解密技術的步驟,幫助大家更好的理解和學習c#,感興趣的朋友可以了解下
    2021-01-01
  • 基于WPF實現(xiàn)ListBox拖動子項

    基于WPF實現(xiàn)ListBox拖動子項

    這篇文章主要為大家詳細介紹了如何基于WPF實現(xiàn)ListBox拖動子項效果,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考下
    2024-04-04
  • C#圖像灰度級拉伸的方法

    C#圖像灰度級拉伸的方法

    這篇文章主要介紹了C#圖像灰度級拉伸的方法,涉及C#灰度操作的相關技巧,需要的朋友可以參考下
    2015-04-04
  • C#二維數(shù)組基本用法實例

    C#二維數(shù)組基本用法實例

    這篇文章主要介紹了C#二維數(shù)組基本用法,以實例形式分析了C#中二維數(shù)組的定義、初始化、遍歷及打印等用法,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-10-10
  • C#實現(xiàn)PDF簽名時添加時間戳的2種方法(附VB.NET代碼)

    C#實現(xiàn)PDF簽名時添加時間戳的2種方法(附VB.NET代碼)

    在PDF添加簽名時,支持添加可信時間戳來保證文檔的法律效應。本文,將通過C#程序代碼介紹如何添加可信時間戳,可通過2種方法來實現(xiàn)。感興趣的可以了解一下
    2021-05-05
  • C#數(shù)據(jù)結(jié)構(gòu)與算法揭秘二 線性結(jié)構(gòu)

    C#數(shù)據(jù)結(jié)構(gòu)與算法揭秘二 線性結(jié)構(gòu)

    本文中,我們討論了什么是線性結(jié)構(gòu),線性結(jié)構(gòu)有哪些特點,并且詳細介紹了一個最簡單線性結(jié)構(gòu)順序表,并且通過源代碼對她進行一些列的分析,最后還舉了兩個例子,讓我們更好的理解順序表
    2012-11-11
  • .net 隨機生成漢字

    .net 隨機生成漢字

    在c#中可以使用system.text來處理所有語言編碼,其中encodiong類是比較重要的漢字編碼類,接下來我們就圍繞c#隨機生成漢字代碼講起,有需要的朋友可以參考下
    2015-08-08
  • C#判斷字符是否為漢字的三種方法分享

    C#判斷字符是否為漢字的三種方法分享

    判斷一個字符是不是漢字通常有三種方法,第一種用 ASCII 碼判斷,第二種用漢字的 UNICODE 編碼范圍判 斷,第三種用正則表達式判斷,以下是具體方法
    2014-01-01
  • 運用示例簡單講解C#取消令牌CancellationTokenSource

    運用示例簡單講解C#取消令牌CancellationTokenSource

    這篇文章運用示例簡單講解C#取消令牌CancellationTokenSource,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • C# WinForm導出Excel方法介紹

    C# WinForm導出Excel方法介紹

    在.NET應用中,導出Excel是很常見的需求,導出Excel報表大致有以下三種方式:Office PIA,文件流和NPOI開源庫,本文只介紹前兩種方式
    2013-12-12

最新評論