亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#實現基于ffmpeg加虹軟的人臉識別的示例

2019-10-29 21:08:18
字體:
來源:轉載
供稿:網友

關于人臉識別

目前的人臉識別已經相對成熟,有各種收費免費的商業方案和開源方案,其中OpenCV很早就支持了人臉識別,在我選擇人臉識別開發庫時,也橫向對比了三種庫,包括在線識別的百度、開源的OpenCV和商業庫虹軟(中小型規模免費)。

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

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

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

SDK的C# Wrapper

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

Wrapper庫的使用例子

基本使用

人臉檢測(靜態圖片):

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);  //檢測到位置信息在使用完畢后,需要釋放資源,避免內存泄露  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 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持久化保存,這個即是人臉特征數據,用于后續的人臉匹配  //File.WriteAllBytes("XXX.data", feature.FeatureData);FeatureData會自動轉型為byte數組  if ((result1 != null) & (result2 != null))    Console.WriteLine(proccesor.Match(result1[0].FeatureData, result2[0].FeatureData, true));}

使用注意事項

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

整合的完整例子

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

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

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

FaceDemo詳解

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

首先是ffmpeg的調用參數:

var setting =new ConvertSettings{  CustomOutputArgs = "-an -r 15 -pix_fmt bgr24 -updatefirst 1"}; //-s 1920x1080 -q:v 2 -b:v 64ktask = 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表示幀率,根據需求和實際設備調整此參數,-pix_fmt比較重要,一般情況下,指定為bgr24不會有太大問題(還是看具體設備),之前就是用成了rgb24,結果捕獲出來的圖像,人都變成阿凡達了,顏色是反的。最后一個參數,坑的我差點放棄這個方案。本身,ffmpeg在調用時,需要指定一個文件名模板,捕獲到的輸出會按照模板生成文件,如果要將數據輸出到控制臺,則最后傳入一個-即可,最開始沒有指定updatefirst,ffmpeg在捕獲了第一幀后就拋出了異常,最后查了半天ffmpeg說明(完整參數說明非常多,輸出到文本有1319KB),發現了這個參數,表示持續更新第一個文件。最后,在調用視頻捕獲是,需要指定輸出格式,必須指定為Format.raw_video,實際上這個格式名稱有些誤導人,按道理將應該叫做raw_image,因為最終輸出的是每幀原始的位圖數據。

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

//完整代碼參加Github源代碼//代碼片段1private Bitmap _image;private IntPtr _pImage;{  _pImage = Marshal.AllocHGlobal(1920 * 1080 * 3);  _image = new Bitmap(1920, 1080, 1920 * 3, PixelFormat.Format24bppRgb, _pImage);}//代碼片段2private 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);    }}

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

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

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實例進行處理。這種方式的缺點在于,有可能會導致丟幀的現象,因為上面說到了,識別時間(如果檢測到新的人臉,那么加上匹配,大約需要130ms左右)大于每幀時間,但是并不影響識別效果和需求的實現,基本丟棄的幀可以忽律。最后,運行,穩定了、完美了,實際也感覺不到丟幀。

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

寫在最后

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到c#教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产成人jvid在线播放| 欧美亚洲视频在线看网址| 成人国产精品色哟哟| 亚洲精品中文字| 欧美在线视频a| 亚洲国产精品人人爽夜夜爽| 国产精品久久久久久av福利软件| 欧美日韩999| 精品国产欧美成人夜夜嗨| 韩国三级电影久久久久久| 国产日韩欧美中文在线播放| 亚洲码在线观看| 欧美精品制服第一页| 日韩电影免费在线观看中文字幕| 97色在线视频观看| 国产精品久在线观看| 欧美日韩国产精品一区二区不卡中文| 国产亚洲精品91在线| 国产精品久久久av久久久| 欧美最猛性xxxxx亚洲精品| 国产精自产拍久久久久久蜜| 伊人久久久久久久久久久久久| 日韩在线中文字| 精品视频一区在线视频| 国产亚洲精品久久久久久牛牛| 色久欧美在线视频观看| 在线精品播放av| 91久久精品国产91性色| 久久精品99无色码中文字幕| 国产精品中文字幕在线| 色久欧美在线视频观看| 久久影院模特热| 欧美xxxx18国产| 91精品久久久久久久久中文字幕| 亚洲人精选亚洲人成在线| 亚洲国产高清高潮精品美女| 午夜精品蜜臀一区二区三区免费| 一本色道久久综合狠狠躁篇怎么玩| 性欧美长视频免费观看不卡| 欧美日韩精品中文字幕| 成人精品一区二区三区电影免费| 亚洲精品自产拍| 精品国产福利在线| 久久久av网站| 久久人人爽人人爽人人片av高请| 久久视频免费观看| 中文字幕精品—区二区| 26uuu国产精品视频| 北条麻妃在线一区二区| 国产亚洲xxx| 精品国产精品三级精品av网址| 国产丝袜一区二区| 日韩电影中文字幕一区| 91免费精品国偷自产在线| 久久五月情影视| 一二美女精品欧洲| 一色桃子一区二区| 亚洲高清久久久久久| 久久久亚洲欧洲日产国码aⅴ| 国产日韩欧美综合| 亚洲一区二区久久久久久| 欧美电影在线观看网站| 亚洲bt欧美bt日本bt| 欧美性猛交xxxx| 69久久夜色精品国产7777| 日韩高清人体午夜| 亚洲免费一在线| 久久免费福利视频| 久久久视频在线| 中文字幕国内精品| 国产精品高清在线观看| 国产精品久久久久久久av大片| 欧美特黄级在线| 久久91超碰青草是什么| 亚洲视频精品在线| 伊人伊成久久人综合网小说| 亚洲一区二区三区久久| 色综合伊人色综合网| 欧美与欧洲交xxxx免费观看| 日韩在线播放一区| 日韩精品在线视频| 亚洲欧美激情视频| 亚洲美女自拍视频| 日韩中文有码在线视频| 亚洲性夜色噜噜噜7777| 成人午夜一级二级三级| 久久久久久高潮国产精品视| 91精品成人久久| 91久久精品日日躁夜夜躁国产| 国产精品都在这里| 国产日韩欧美电影在线观看| 国产精品v日韩精品| 欧美性色xo影院| 久久久久久久av| 国产福利成人在线| 亚洲第一网站男人都懂| 久久精品国产免费观看| 亚洲深夜福利视频| 国产精品高潮呻吟久久av野狼| 欧美另类暴力丝袜| 九九热这里只有精品免费看| 亚洲成人网在线| 日韩精品免费在线播放| 久久久精品久久久久| 午夜精品福利在线观看| 亚洲精品天天看| 久久久久久久久久久av| 91免费国产视频| 欧美激情亚洲精品| 亚洲少妇中文在线| 日韩中文字幕久久| 91久久在线视频| 亚洲欧美制服丝袜| 日韩精品亚洲视频| 国精产品一区一区三区有限在线| 午夜精品一区二区三区视频免费看| 日韩av手机在线| 国产主播欧美精品| 国产精品极品美女粉嫩高清在线| 日韩精品视频在线观看网址| 欧美日韩成人精品| 51精品国产黑色丝袜高跟鞋| 国模私拍视频一区| 91高清在线免费观看| 国产女同一区二区| 国产精品嫩草影院一区二区| 久久久久亚洲精品成人网小说| 中文字幕亚洲一区二区三区五十路| 国产在线观看精品一区二区三区| 日本韩国在线不卡| 亚洲精品之草原avav久久| 尤物九九久久国产精品的分类| 日韩av免费看| 日韩精品中文字幕有码专区| 日韩欧美国产高清91| 亚洲免费视频观看| 亚洲天天在线日亚洲洲精| 茄子视频成人在线| 日韩成人激情在线| 亚洲国产精品福利| 91精品国产综合久久香蕉的用户体验| 日韩一区视频在线| 久久精品成人动漫| 国产黑人绿帽在线第一区| 国产在线拍揄自揄视频不卡99| 久久精品91久久香蕉加勒比| 久久久亚洲精选| 欧美国产日韩视频| 欧美成人午夜剧场免费观看| 欧美激情影音先锋| 亚洲加勒比久久88色综合| 91精品91久久久久久| 久久久久久久久久久成人| 成人av番号网| 黑人与娇小精品av专区| 欧美成人手机在线| 夜夜嗨av色综合久久久综合网| 久久久久久久亚洲精品| 国产精品久久av| 97超碰蝌蚪网人人做人人爽| 欧美一级淫片丝袜脚交| 欧美老少做受xxxx高潮| 欧美极品美女电影一区| 久久久久久久久综合|