【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; //視頻事件

 

3.視頻顯示

private void _VideoCapture_ImageGrabbed(object sender, EventArgs e)
{
_VideoCapture.Retrieve(_Frame, 1);
this.pictureBox1.Image = _Frame.Bitmap; //很神奇的pictureBox,居然不在UI線程也能顯示
}

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:admin@192.168.0.159/stream1",格式是rstp://用戶名:密碼@ip地址/...


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM