【C#】人臉識別 視頻數據轉圖片數據

因爲不會用C#直接打開攝像頭,就只能用第三方dll。一開始用Aforge,後來發現有個問題,關閉攝像頭老是陷入等待,所以拋棄了。前一陣子開始用封裝了OpenCV的Emgu,一路走來也是N聲嘆息。

一、安裝Emgu的嘆息
一開始自己下載並安裝了Emgu,然後各種測試,發現還需要這個那個的,最後發現直接安裝一個EMGU.CV的NuGet包就OK了。
當然要打開視頻,還需要引用System.ServiceModel。

二、獲取視頻用Emgu獲取視頻真心十分方便
1.定義兩個變量

VideoCapture _VideoCapture;
Mat _Frame = new Mat();

2.初始化視頻

_VideoCapture = new VideoCapture();
//_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, 1024); //設置寬度
//_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, 768);//設置高度
_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps, 10);//設置每秒鐘的幀數
_VideoCapture.Start();
_VideoCapture.ImageGrabbed += _VideoCapture_ImageGrabbed; //視頻事件
_VideoCapture = new VideoCapture();
//_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, 1024); //設置寬度
//_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, 768);//設置高度
_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps, 10);//設置每秒鐘的幀數
_VideoCapture.Start();
_VideoCapture.ImageGrabbed += _VideoCapture_ImageGrabbed; //視頻事件

4.獲取當前幀
用於人臉比對,一般在另外一個線程

Mat curFrame=_VideoCapture.QueryFrame();

一切十分完美,就是_Frame.Bitmap似乎沒有Dispose(後來發現Mat的地址是不變,不會發生內存泄漏),但運行起來也沒問題。

三、人臉識別的嘆息
看了一下,人臉識別需要Bitmap,方便

Mat curFrame=_VideoCapture.QueryFrame();
Bitmap bitmap=curFrame.Bitmap;

var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
int width = (bitmap.Width + 3) / 4 * 4;
var bytesCount = bmpData.Height * width * 3;
IntPtr pImageData = Marshal.AllocCoTaskMem(bytesCount);
if (width == bitmap.Width)
      CopyMemory(pImageData, bmpData.Scan0, bytesCount);
else
      for (int i = 0; i < bitmap.Width; i++)
            CopyMemory(IntPtr.Add(pImageData, i * width * 3), IntPtr.Add(bmpData.Scan0, i * bmpData.Stride), bmpData.Stride);
bitmap.UnlockBits(bmpData);

得到了ArcFace所需的圖片數據pImageData,測試一下挺好,能運行。時間一長,報錯了:“試讀取或寫入受保護的內存。這通常指示其他內存已損壞。”
估計人臉識別的線程和顯示視頻的線程衝突了,查看了Emgu的源代碼,發現QueryFrame就是封裝了Retrieve。
好吧,克隆一下,Bitmap bitmap=(Bitmap)curFrame.Bitmap.Clone();問題依舊!查看地址發現Clone沒卵用!

四、最終解決的辦法
研究了Mat這個東東,發現GetData()就能返回圖片數據,而且不會衝突,最後寫成:
1.定義

IntPtr _PImageData;
int  _ImageWidth,_ImageHeight,_ImageSize;

2.初始化

_ImageWidth=_VideoCapture.Width;
_ImageHeight=_VideoCapture.Height;
_ImageSize = _VideoCapture.Width  * _VideoCapture.Height * 3;
_PImageData = Marshal.AllocCoTaskMem(_ImageSize);

3.轉換

Marshal.Copy(_Frame.GetData(), 0, _PImageData, _ImageSize);

ASFDetectFaces(pEngine,_ImageWidth, _ImageHeight,513,_PImageData, out var faceInfo);

一切變得如此簡單,長嘆一聲!

五、其他
1.視頻圖片的寬度一般是4的倍數,所以上述方式肯定沒問題。
2.經常有人問如何獲取網絡攝像頭、ip攝像頭的圖像,其實就是

videoCapture = new VideoCapture("string filename");

如某tplink的IP攝像頭的filename是這樣的"rtsp://admin:[email protected]/stream1",格式是rstp://用戶名:密碼@ip地址/...

各位C#的親,你們怎麼轉換的?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章