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

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

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

關(guān)于人臉識(shí)別

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

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

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

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

SDK的C# Wrapper

由于虹軟的庫是采用C++開發(fā)的,而我的應(yīng)用程序采用的是C#,所以,需要對(duì)庫進(jìn)行包裝,便于C#的調(diào)用,包裝的主要需求是可以在C#中快速方便的調(diào)用,無需考慮內(nèi)存、指針等問題,并且具備一定的容錯(cuò)性。Wrapper庫目前已經(jīng)開源,大家可以到Github上進(jìn)行下載,地址點(diǎn)擊這里。Wrapper庫基本上沒有什么可以說的,無非是對(duì)PInvoke的包裝,只是里面做了比較多的細(xì)節(jié)處理,屏蔽了調(diào)用細(xì)節(jié),提供了相對(duì)高層的函數(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ù)幀識(shí)別,相較于檢測,又更高的執(zhí)行效率,這里用靜態(tài)圖片做例子,實(shí)際使用和檢測沒啥區(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);
    }
  }
}

人臉對(duì)比:

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是個(gè)整合包裝類,集成了檢測和識(shí)別,如果要單獨(dú)使用識(shí)別,可以使用FaceRecognize類
  //這里做演示,假設(shè)圖片都只有一張臉
  //可以將FeatureData持久化保存,這個(gè)即是人臉特征數(shù)據(jù),用于后續(xù)的人臉匹配
  //File.WriteAllBytes("XXX.data", feature.FeatureData);FeatureData會(huì)自動(dòng)轉(zhuǎn)型為byte數(shù)組

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

使用注意事項(xiàng)

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

整合的完整例子

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

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

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

FaceDemo詳解

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

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

到此為止,還并沒有解決視頻流數(shù)據(jù)的捕獲,因?yàn)橛謥硪粋€(gè)坑,ProcessStartInfo的控制臺(tái)緩沖區(qū)大小只有32768 bytes,即,每一次的輸出,實(shí)際上并不是一個(gè)完整的位圖數(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í)間摸索(不要看只有幾行,人都整崩潰了),得出了上述代碼。首先,我捕獲的圖像數(shù)據(jù)是24位的,并且圖像大小是1080p的,所以,實(shí)際上,一個(gè)原始位圖數(shù)據(jù)的大小為stride * height,即width * 3 * height,大小為6220800 bytes。所以,在判斷了捕獲數(shù)據(jù)到達(dá)這個(gè)大小后,就進(jìn)行Bitmap轉(zhuǎn)換處理,然后將MemoryStream的位置移動(dòng)到最開始。需要注意的時(shí),由于捕獲到的是原始數(shù)據(jù)(不包含bmp的HeaderInfo),所以注意看Bitmap的構(gòu)造方式,是通過一個(gè)指向原始數(shù)據(jù)位置的指針就行構(gòu)造的,更新該圖像時(shí),也僅需要更新指針指向的位置數(shù)據(jù)即可,無需在建立新的Bitmap實(shí)例。

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

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;
  }
}

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

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

寫在最后

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

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

相關(guān)文章

  • C# 獲取硬盤號(hào),CPU信息,加密解密技術(shù)的步驟

    C# 獲取硬盤號(hào),CPU信息,加密解密技術(shù)的步驟

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

    基于WPF實(shí)現(xiàn)ListBox拖動(dòng)子項(xiàng)

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

    C#圖像灰度級(jí)拉伸的方法

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

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

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

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

    在PDF添加簽名時(shí),支持添加可信時(shí)間戳來保證文檔的法律效應(yīng)。本文,將通過C#程序代碼介紹如何添加可信時(shí)間戳,可通過2種方法來實(shí)現(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)有哪些特點(diǎn),并且詳細(xì)介紹了一個(gè)最簡單線性結(jié)構(gòu)順序表,并且通過源代碼對(duì)她進(jìn)行一些列的分析,最后還舉了兩個(gè)例子,讓我們更好的理解順序表
    2012-11-11
  • .net 隨機(jī)生成漢字

    .net 隨機(jī)生成漢字

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

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

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

    運(yùn)用示例簡單講解C#取消令牌CancellationTokenSource

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

    C# WinForm導(dǎo)出Excel方法介紹

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

最新評(píng)論